-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: add proxyHijackESM for better spyOn in browser #4701
Conversation
✅ Deploy Preview for fastidious-cascaron-4ded94 ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
FYI I simplified the approach a bit, using the natural comparison of imported modules to match their 'proxies'. This makes the |
@sheremet-va Is this approach interesting to the team? Should I bother rebasing it? I think being able to |
I am not sure yet how this works from your PR. Can you explain by providing code examples? How does the transformed file look in the end? For now the priority is to finish #5036 |
There's actually very little transform happening. Think of it this way:
Here's what the final transformed source might look like: // src.ts: the source of foo function
export function foo() { return 1; }
// consumer.ts: someone random that uses foo
const { foo } = await __vitest_mocker__.wrap(import('./src.ts'));
// outputs 1
console.debug(foo());
const oldFoo = foo;
const wholeModule = await __vitest_mocker__.wrap(import('./src.ts'));
// this is what spyOn does internally
Object.defineProperty(wholeModule, 'foo', { get() { return () => 2; } });
// outputs 2
console.debug(foo());
const newFoo = foo;
// true, because 'foo' just dereferences the current value when called
console.info(newFoo === oldFoo); The key is that the consumer of This whole PR basically does the same as |
This would break live bindings though. This is why the previous approach changes the imports and all references of the module use the new export. This also allows the browser to fetch static imports in a more performant way. |
Good point. It's already a compromise made for mocking whole modules in vitest, though, and it's not called out.
I appreciate this isn't a perfect solution, but it does help people test inside a browser. If this approach isn't good for you (even behind a flag, where we can iterate on it) I can go away and think about a better solution that still fixes #4264. I suspect it might be a combination of this and |
It was a bug and it has been fixed for some time now. The bug was introduced when we migrated to more browser-friendly solution, but now it uses a code transform similar to Vite SSR.
I believe we can provide a solution that works well out of the box, I don't feel like having it now would greatly benefit us to be honest. I think we should build on the current solution with ESM hijacking and the new mocker transform. |
Fair enough, I misunderstood the project's goals re: live binding. |
Description
Changes the way
vi.spyOn
works in the browser via a new experimental flagproxyHijackESM
that's intended to replaceslowHijackESM
.(I'm not making promises about the speed of this approach, just that it works via a
Proxy
)Refixes #4264 (which wasn't "fixed", it was just disabled) - importing
React
now works, the React sample included now uses this 👍The approach is vaguely:
HoistMocks
)__vitest_mocker__.wrap
, so matching imports can be modified in a central locationslowHijackESM
), wraps these imports in aProxy
, which letstinyspy
work on itProxy
, so code likeimport { callable } from 'foo'
continues to work —callable
is itself now a "bound" callableProxy
, even though it looks like an orphan varFor more context, this is the start of an approach to #3046. This makes every module's import be wrapped so it can be piggybacked by
vi.mock
in a follow-up PR. (A complete version is here but it's not good enough for review)Alternatives: Using iframes-per-test might make some things simpler, but I think this PR still helps — because
spyOn
is something we allow on any module - theHoistMocks
/Proxy
combination needs to run on anything a user might spy.Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
pnpm-lock.yaml
unless you introduce a new test example.Tests
pnpm test:ci
.Documentation
pnpm run docs
command.Changesets
feat:
,fix:
,perf:
,docs:
, orchore:
.