What was happening?
- When we use CT, we go over the test files, look at the imports using
`tsxTransform.ts` and store them inside a map, these we feed into the
import registry which we build using Vite and have access inside the
browser
- In case of an inline component in the same file as where the test file
is, this is not happening.
- jsx-runtime via babel kicks in, transforms every JSX component in
something like that:
```
{
__pw_type: 'jsx',
type: [Function: MyInlineComponent],
props: { value: 'Max' },
key: undefined
}
```
this then gets passed into `wrapObject` which maps any function from the
Node.js side into expose function calls so they work inside the browser.
The assumption for `wrapObject` was to do it mostly for callbacks. So it
does for `type` - which is actually our component. We then pass this to
the React render function, which calls back the exposed function but we
never return anything, so it mounts `undefined`.
---
While there have been experiments from certain vendors to get the
'client only' code inside a server side file, we should throw for now to
not confuse users. We might revisit this in the future since Babel / TSX
doesn't support it outside of the box.
Fixes https://github.com/microsoft/playwright/issues/32167
49 lines
2 KiB
TypeScript
49 lines
2 KiB
TypeScript
import { test, expect } from '@playwright/experimental-ct-react';
|
|
import Button from '@/components/Button';
|
|
import EmptyFragment from '@/components/EmptyFragment';
|
|
import { ComponentAsProp } from '@/components/ComponentAsProp';
|
|
import DefaultChildren from '@/components/DefaultChildren';
|
|
|
|
test('render props', async ({ mount }) => {
|
|
const component = await mount(<Button title="Submit" />);
|
|
await expect(component).toContainText('Submit');
|
|
});
|
|
|
|
test('render component as props', async ({ mount }) => {
|
|
const component = await mount(<ComponentAsProp component={<Button title="Submit" />} />);
|
|
await expect(component.getByRole('button', { name: 'submit' })).toBeVisible();
|
|
});
|
|
|
|
test('render jsx array as props', async ({ mount }) => {
|
|
const component = await mount(<ComponentAsProp component={[<h4>{[4]}</h4>,[[<p>[2,3]</p>]]]} />);
|
|
await expect(component.getByRole('heading', { level: 4 })).toHaveText('4');
|
|
await expect(component.getByRole('paragraph')).toHaveText('[2,3]');
|
|
});
|
|
|
|
test('render attributes', async ({ mount }) => {
|
|
const component = await mount(<Button className="primary" title="Submit" />);
|
|
await expect(component).toHaveClass('primary');
|
|
});
|
|
|
|
test('render an empty component', async ({ mount, page }) => {
|
|
const component = await mount(<EmptyFragment />);
|
|
expect(await page.evaluate(() => 'props' in window && window.props)).toEqual({});
|
|
expect(await component.allTextContents()).toEqual(['']);
|
|
expect(await component.textContent()).toBe('');
|
|
await expect(component).toHaveText('');
|
|
});
|
|
|
|
function MyInlineComponent({ value }: { value: string }) {
|
|
return <>Hello {value}</>;
|
|
}
|
|
|
|
test('render inline component with an error', async ({ mount }) => {
|
|
await expect(mount(<MyInlineComponent value="Max" />)).rejects.toThrow('Component "MyInlineComponent" cannot be mounted.');
|
|
});
|
|
|
|
test('render inline component with an error if its nested', async ({ mount }) => {
|
|
await expect(mount(<DefaultChildren>
|
|
<MyInlineComponent value="Max" />
|
|
</DefaultChildren>)).rejects.toThrow('Component "MyInlineComponent" cannot be mounted.');
|
|
});
|