microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
66.98k stars 3.67k forks source link

[Feature] Component testing and code extraction #26440

Closed sand4rt closed 10 months ago

sand4rt commented 1 year ago

Playwright component testing is great so far and i think it offers substantial advantages over other component testing solutions (especially the ones that are based on HappyDOM/JSDOM). However, there are quite a lot of outstanding issues where the root cause is the distinction between the code execution in the browser and that in the Node environment. I think that restricting the component props and the MountOptions to be JSON serializable is a major drawback.

I'm curious if you have considered extracting the component and MountOptions, along with their references, into separate files by traversing the AST before bundling/transpiling? This allows the component and MountOptions to run in the browser without serialization. Modern meta frameworks such as Qwik, Remix, Solid Start, and Next 13 are already doing something similar and i think more and more people are getting familiar with this model.

Resources:

Related:

cc @pavelfeldman

pavelfeldman commented 1 year ago

So I have a very simple test:

let enabled = false;
let submitCount = 0;

const b = await mount(<button isEnabled={() => enabled}} onSubmit={() => ++submitCount}>Submit</button>);

await b.click();
expect(submitCount).toBe(0);

enabled = true;
await b.click();
expect(submitCount).toBe(1);

How does the module splitting help here?

sand4rt commented 1 year ago

This case is currently not supported anyway if i'm not mistaken:

let enabled = false;

const b = await mount(<button isEnabled={() => enabled}>Submit</button>);

enabled = true;
await b.click();

I think update should be used for this:

const b = await mount(<button isEnabled={false}>Submit</button>);
await b.update(<button isEnabled>Submit</button>);
await b.click();

The callbacks/events still need to be sent back to the Node environment as JSON. Not sure if it helps much in this case:

let submitCount = 0;

const b = await mount(<button onSubmit={() => ++submitCount}>Submit</button>);

await b.click();
expect(submitCount).toBe(1);
pavelfeldman commented 1 year ago

My point is that code splitting is helpful when contexts don't need to interact (synchronously). But the whole limitation you rightfully point to is all about being able to exchange complex objects synchronously. So I find module splitting a complex solution that does not actually solve the problem. Not saying there is no problem, but this proposed solution does not handle even a simplest case that I brought up in the snippet.

The only solution I know for now is embedding a component in a TestStory that can interact with the component synchronously. Maybe we could start with documenting it better and providing more examples for it.

ak-beam commented 1 year ago

@pavelfeldman what is a TestStory? I don't see that in a code search of the repo or any other issues.

pavelfeldman commented 10 months ago

what is a TestStory? I don't see that in a code search of the repo or any other issues. >

https://playwright.dev/docs/test-components#test-stories

I'll close as won't fix as per https://github.com/microsoft/playwright/issues/26440#issuecomment-1677548752. We work with the data actively and we can't establish data synchronization using code extraction.