Skip to content

fix(core): actionable error for rs.spyOn on ESM namespace export#1498

Merged
fi3ework merged 2 commits into
mainfrom
worktree-radiant-mixing-bonbon
Jul 2, 2026
Merged

fix(core): actionable error for rs.spyOn on ESM namespace export#1498
fi3ework merged 2 commits into
mainfrom
worktree-radiant-mixing-bonbon

Conversation

@fi3ework

@fi3ework fi3ework commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

When a test spies an export of a native ES module namespace — e.g. an externalized node_modules dependency in the node test environment, or a re-exported binding — the namespace binding is non-configurable, so rs.spyOn(ns, 'method') failed with a bare TypeError: Cannot redefine property.

rs.spyOn now detects that exact shape (a module namespace whose target export is non-configurable, via the existing isModuleObject) and throws an actionable error pointing users at rs.mock('<module>', { spy: true }), which replaces the module's exports at build time and works regardless of re-export optimization.

The guard is deliberately narrow: bundled dependencies expose configurable getters (so it never fires for them), and ordinary non-configurable object properties are not module namespaces (so their original error is untouched). For a bundled re-export — where the spy silently installs but re-export inlining routes callers past it — the ineffectiveness cannot be detected at runtime, so it is documented as a limitation (EN/ZH) with rs.mock(..., { spy: true }) as the recommended alternative.

Related Links

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying rstest with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6676d3b
Status: ✅  Deploy successful!
Preview URL: https://d8f9947e.rstest.pages.dev
Branch Preview URL: https://worktree-radiant-mixing-bonb.rstest.pages.dev

View logs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02e1f3a34a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/core/src/runtime/api/spy.ts Outdated
When spying an export of a native ES module namespace (e.g. an externalized
node_modules dependency in the `node` test environment), the binding is
non-configurable, so `rs.spyOn` failed with a bare "Cannot redefine property".
It now throws an actionable error pointing at `rs.mock('<module>', { spy: true })`.

The guard is precise: it fires only for a module namespace (`isModuleObject`)
whose target export is non-configurable, so bundled deps (configurable getters)
and ordinary non-configurable object properties keep their existing behavior.

For a bundled re-export, where the spy silently installs but is bypassed by
re-export inlining, this cannot be detected at runtime — it is documented as a
limitation with `rs.mock(..., { spy: true })` as the recommended alternative.

Docs (en/zh) and regression tests included.

Refs #1492
@fi3ework fi3ework force-pushed the worktree-radiant-mixing-bonbon branch from 02e1f3a to 6676d3b Compare July 2, 2026 06:33
The guard reused isModuleObject, which also matches an __esModule interop
object from a transpiled CommonJS module. Those are ordinary objects whose
writable (even if non-configurable) exports tinyspy can still spy, so the
guard wrongly threw for them. Narrow it to a real [object Module] namespace
(Symbol.toStringTag === 'Module') so __esModule exports fall through and stay
spyable.
@fi3ework fi3ework enabled auto-merge (squash) July 2, 2026 06:48
@fi3ework fi3ework merged commit 82085ba into main Jul 2, 2026
12 checks passed
@fi3ework fi3ework deleted the worktree-radiant-mixing-bonbon branch July 2, 2026 06:54
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.

[Bug]: rs.spyOn on a re-exported namespace export silently no-ops (jest.spyOn intercepts the same code)

1 participant