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
65.62k stars 3.57k forks source link

[Feature] Component SSR testing #19399

Open Tal500 opened 1 year ago

Tal500 commented 1 year ago

Playwrite supports currently component testing for many UI frameworks (in experimental mode), but only by mounting the component, and with no SSR rendering testing support.

I would like to add to the component testing environment some form of SSR mode in testing. For many simple component it might not really be needed, but for more advanced UI handaling it will be useful to verify that both SSR and hydration work great.

A note for designers: Some UI framework simulate a full browser context on SSR(i.e. window and document objects are available to use on SSR mode, e.g. React as far as I know), while some others simulate no browser context on SSR(e.g. Svelte).

Specifically, I think this 3 situations are needed to be tested, and have some flag indicaring the user what is currently being tested:

  1. Mount only mode (i.e. what happens today)
  2. SSR only mode. This means the user couldn't expect a JS client response to his interactions, but that's part of the deal.
  3. SSR mode, and then hydration. So the user can check that the SSR was right, and then apply hydration and check that the component after hydration works as well.

Not sure how to fuse this ideas with the currently mount call, I'm open to suggestions.

sand4rt commented 1 year ago

Agree that this might be useful.

Maybe a mode flag on the mountOptions? When set to ssr the component will be rendered in node with: - require('solid-js/web').renderToString - require('react-dom/server').renderToString - require('@vue/server-renderer').renderToString - Svelte: ?

And could be hydrated with: - require('solid-js/web').hydrate - require('react-dom').hydrate - require('vue').hydrate - For Svelte there is a hydrate flag on the ComponentConstructorOptions

test('default', async ({ mount }) => {
  const component = await mount(Component);
  // hydrates automatically because mode is 'default' by default
});

test('ssr', async ({ mount }) => {
  const component = await mount(Component, { mode: 'ssr' }); // include mode: 'stream' ?
  // .. verify that SSR was right 
  await component.hydrate();
  // .. verify the component after hydration works
});

['ssr', 'default'].forEach(mode => {
  test(mode, async ({ mount }) => {
    const component = await mount(Component, { mode });
    // .. verify that SSR and default was right 
  });
});

Not sure how to keep it consistent with all frameworks yet and if it's even possible and desirable.

related to:

Tal500 commented 1 year ago
  • ComponentConstructorOptions

This is a great question - how much let the user the control of the manual hydration, versus the automatic process by Playwright?

I think as a user, I would like to have Playwright mounting and hydrating everything for me, and specify the test code I want to be run before hydration, and also the test code I want after hydration, the way you did it for Svelte(disclaimer: I know only Svelte).

However, I wish that there could be a way to "skip" SSR automatically, so the same code will test both cases. In Svelte. I think that the easiest code will be to have "dual rendering" (this is a generalization of your code):

dual_test('CSR&SSR', async ({ render }) => {
   const { mount, ssr } = await render(Component);
   if (ssr) {
       // .. verify that the SSR was right 
   }
   const component = await mount();
   // .. verify the component after hydration works
 });

This way, the user write the test code once, and Playwright (could) test the component twice:

  1. With ssr = undefined(client mounting only mode).
  2. With ssr = (component SSR HTML string)(in SSR mode).

In any case, mount will be an async function that do mounting/hydration(depending on the mode).

I believe a similar fashion can be done to other frameworks.

sand4rt commented 1 year ago

Would like to explore the ideas a bit further if you can share a simple repository with SSR use case(s) you want to test?

Tal500 commented 1 year ago

Would like to explore the ideas a bit further if you can share a simple repository with SSR use case(s) you want to test?

Let's split our discussion to "simple" and "advanced" components.

Simple Components

Simple components here are one that their initial display does not need browser JS API (it may need it for the next "frames"). You can think about a simple <span class="fancy">Bla Bla {varA}</span> type of components (or even with more content). This components should render the same both for SSR and on client, so it would be nice that Playwright CT will check that the initial rendering works also for SSR, preferably without test code duplication by the user.

Advanced Components

These are components that use the browser JS API for the setup phase, and do some HTML/CSS tricks to make the component looks great (or at least, reasonable) in the SSR rendering phase, before the browser JS API is available. Why it needs the browser API? For example, it wants to know the actual size its container in the client, to know how to display it correctly. A great advanced example is svelte-splitpanes (maybe too much of an example). In these type of components, you wish to check that the SSR looks great as well.

khryshyn commented 1 year ago

It would be nice not only for rendering components but for Api SSR mocking as well for Nuxt.js / Next.js / etc...

sand4rt commented 1 year ago

@khryshyn yeah agree. No one seems to have a working/released solution for mocking API calls in server components yet tho.

@kettanaito / MSW is working on something which looks really promising: https://www.youtube.com/watch?v=H4lGysz_dOQ

kettanaito commented 1 year ago

Yeah, once we release setupRemoteServer() as a part of MSW, controlling inter-process network behavior will become trivial. That will work in Playwright too!