withastro / astro

The web framework for content-driven websites. ⭐️ Star to support our work!
https://astro.build
Other
45.55k stars 2.39k forks source link

Vitest not able to render Astro components with React/Preact children. #11882

Open dvelasquez opened 2 weeks ago

dvelasquez commented 2 weeks ago

Astro Info

Astro                    v4.15.1
Node                     v20.15.1
System                   macOS (arm64)
Package Manager          npm
Output                   static
Adapter                  none
Integrations             @astrojs/preact
                         @astrojs/react

If this issue only occurs in one browser, which browser is a problem?

No response

Describe the Bug

Vitest container api don't work with astro components that have the directive client:only="preact" or client:load and load a react/preact children.

Here is the component test:

import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import { getContainerRenderer } from '@astrojs/preact';
import { loadRenderers } from 'astro:container';
import { expect, test } from 'vitest';
import Index from './index.astro';

test('Header tests', async () => {
  const renderers = await loadRenderers([getContainerRenderer()]);
  const container = await AstroContainer.create({ renderers });
  const result = await container.renderToString(Index);

  expect(result).toContain(/This is a preact island/);
});

This throws the following error: NoClientEntrypoint: `Island` component has a `client:only` directive, but no client entrypoint was provided by `@astrojs/preact`.

When adding the client entrypoint with container.addClientRenderer the code looks like this:

test('Header tests', async () => {
  const renderers = await loadRenderers([getContainerRenderer()]);
  const container = await AstroContainer.create({ renderers });

  container.addClientRenderer({
    name: '@astrojs/preact',
    entrypoint: '@astrojs/preact/client.js',
  });

  const result = await container.renderToString(Index);

  expect(result).toContain(/This is a preact island/);
});

And throws the following error

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

Vitest caught 1 unhandled error during the test run.
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.

What's the expected result?

Container API is able to render the Astro component and it framework children components.

Link to Minimal Reproducible Example

https://stackblitz.com/edit/withastro-astro-utorbc?file=src%2Fpages%2Findex.test.ts

Participation

ematipico commented 2 weeks ago

Please add the server renderer manually: https://docs.astro.build/en/reference/container-reference/#adding-a-renderer-manually

Let me know if that fixes the issue

dvelasquez commented 2 weeks ago

I changed the code to this:

import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import preactRenderer from '@astrojs/preact/server.js';
import { expect, test } from 'vitest';
import Index from './index.astro';

test('Header tests', async () => {
  const container = await AstroContainer.create();

  container.addServerRenderer({
    name: '@astro/preact',
    renderer: preactRenderer,
  });

  container.addClientRenderer({
    name: '@astrojs/preact',
    entrypoint: '@astrojs/preact/client.js',
  });

  const result = await container.renderToString(Index);

  expect(result).toContain(/This is a preact island/);
});

But vitest is still throwing an unhandled error

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

Vitest caught 1 unhandled error during the test run.
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.
ematipico commented 2 weeks ago

Thank you

ematipico commented 2 weeks ago

Actually the code for adding the server renderer is incorrect, check the docs please, and report back

dvelasquez commented 2 weeks ago

The documentation says the server renderer don't have a name, but typescript complains if a name is not given. Anyway, the same unhandled error persist when updating the code to how is in the documentation.

import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import preactRenderer from '@astrojs/preact/server.js';
import { expect, test } from 'vitest';
import Index from './index.astro';

test('Header tests', async () => {
  const container = await AstroContainer.create();

  container.addServerRenderer({ renderer: preactRenderer });
  container.addClientRenderer({
    name: '@astrojs/preact',
    entrypoint: '@astrojs/preact/client.js',
  });

  const result = await container.renderToString(Index);

  expect(result).toContain(/This is a preact island/);
});
Screenshot 2024-08-30 at 11 01 19
joshualouisjenson commented 7 hours ago

I am getting similar issues when trying to load a Vue component and using the client:load directive. SSR and bundling appears to work fine (I can see the component CSS and JS loaded into the browser and the component appears on the page with styling) but the hydration step fails with the following errors in the console and the component has no interactivity: image

From my unexperienced view, it seems like the resolve function for the Container pipeline is not properly mapping the "component-url", and "before-hydration-url" attributes to the correct locations during generateHydrateScript in hydration.js