Skip to content

testing: Make expect compare values as Jest does - #711

Merged
sunli829 merged 4 commits into
mainfrom
expect-semantics
Oct 29, 2026
Merged

sunli829 merged 4 commits into
mainfrom
expect-semantics

Conversation

@sunli829

Copy link
Copy Markdown
Collaborator

Summary

nsh/test's matchers compared values through JSON: undefined equalled null, dates, maps, sets and typed arrays lost their contents, toBe was structural and a RegExp ran in Rust's syntax. This makes them Jest's (decided in the API review before the first recorded release):

  • toBe is Object.is.
  • toEqual is Jest's recursive equality: classes, undefined properties as missing ones, dates by time, regular expressions by source and flags, maps and sets by entries, buffers and typed arrays by bytes, cycles.
  • toMatchObject uses it for its leaves; toContain is ===, toContainEqual structural.
  • A RegExp given to toMatch / toThrow runs on the JS engine.
  • Values are kept as JS made them and compared in Rust over the VM (nsh-testing values.rs), never through JSON.
  • Selector regexes stay matched in Rust (off the JS thread, or on a real host's agent); their docs now say Rust's regex syntax.

Public API

  • nsh_core::element::JsClass — #[non_exhaustive] enum { Array, Function, Date, RegExp, Map, Set, Error, ArrayBuffer, View(&'static str), Object, … }: the class of a JS object, what toEqual compares by.
  • JsIn::class(&self) -> Option<JsClass> — a value's class.
  • JsIn::same_value(&self, other: JsIn<'_>) -> bool — Object.is.
  • JsIn::as_buffer(&self) -> Option<Vec<u8>> — an ArrayBuffer's or a view's bytes.
  • JsIn::iterate(&self) -> Result<Option<Vec<JsOwned<'a>>>, PropError> — the items of an iterable.
  • JsIn::invoke(&self, name: &str, args: &[JsIn<'_>]) -> Result<JsOwned<'a>, PropError> — call a method.
  • JsHost::{class, same_value, buffer, iterate, invoke} — the host side of the above, with defaults for hosts that have none.
  • nsh/test: expect's matchers keep their names and signatures; their meaning is Jest's.

Test

  • cargo test -p nsh-testing --features js,bundle,agent (new tests/testing/expect/Expect.test.ts), -p nsh-js, -p nsh-core
  • nsh test of gallery (223), gallery-rs (74), counter, embedded, host-echo, rust-exports
  • cargo clippy --workspace --all-targets, web clippy

🤖 Generated with Claude Code

nsh/test's matchers compared values through JSON, so `undefined` and
`null` were equal, dates, maps, sets and typed arrays lost their
contents, `toBe` was structural and a RegExp ran in Rust's syntax.
Tests written against Jest's meaning passed or failed for the wrong
reasons, and the shape freezes with the first recorded release.

expect now keeps the values JS made and compares them in Rust over the
VM: `toBe` is `Object.is`, `toEqual` is Jest's recursive equality
(classes, undefined properties as missing, dates, regular expressions,
maps, sets, buffers and typed arrays, cycles), `toMatchObject` uses it
for its leaves, `toContain` is `===`, and a RegExp given to `toMatch`
or `toThrow` runs on the JS engine. Selectors stay matched in Rust,
off the JS thread, and their docs now say so.
@sunli829
sunli829 merged commit 8d79044 into main Oct 29, 2026
11 checks passed
@sunli829
sunli829 deleted the expect-semantics branch October 29, 2026 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant