fix(ct-react): support shorthand fragment notation (#32900)

Closes https://github.com/microsoft/playwright/issues/32853

Vite turns the shorthand fragment notation `<></>` into `import {
Fragment } from "react"; <Fragment></Fragment>`. On the Node.js side of
things, this `react` import resolves to our mock version of React, which
currently mocks `Fragment` as `{}`. Currently, we pass that straight to
`React.createElement`, which throws an error.

The fix is to make our `Fragment` mock detectable with a tag, and when
we render it replace it with the real `__pwReact.Fragment`.
This commit is contained in:
Simon Knott 2024-10-02 11:19:09 +02:00 committed by GitHub
parent 0fd9452127
commit 208a54529d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 20 additions and 2 deletions

View File

@ -33,12 +33,24 @@ function isJsxComponent(component) {
}
/**
* @param {any} type
* @returns {boolean} type is Playwright's mock JSX.Fragment
*/
function isJsxFragment(type) {
return typeof type === 'object' && type?.__pw_jsx_fragment;
}
/**
* Turns the Playwright representation of JSX (see jsx-runtime.js) into React.createElement calls.
* @param {any} value
*/
function __pwRender(value) {
return window.__pwTransformObject(value, v => {
if (isJsxComponent(v)) {
const component = v;
let type = component.type;
if (isJsxFragment(type))
type = __pwReact.Fragment;
const props = component.props ? __pwRender(component.props) : {};
const key = component.key ? __pwRender(component.key) : undefined;
const { children, ...propsWithoutChildren } = props;
@ -47,7 +59,7 @@ function __pwRender(value) {
const createElementArguments = [propsWithoutChildren];
if (children)
createElementArguments.push(children);
return { result: __pwReact.createElement(component.type, ...createElementArguments) };
return { result: __pwReact.createElement(type, ...createElementArguments) };
}
});
}

View File

@ -32,7 +32,8 @@ function jsxs(type, props, key) {
};
}
const Fragment = {};
// this is used in <></> notation
const Fragment = { __pw_jsx_fragment: true };
module.exports = {
Fragment,

View File

@ -46,3 +46,8 @@ test('render inline component with an error if its nested', async ({ mount }) =>
<MyInlineComponent value="Max" />
</DefaultChildren>)).rejects.toThrow('Component "MyInlineComponent" cannot be mounted.');
});
test('render Fragment shorthand notation', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32853' } }, async ({ mount }) => {
const component = await mount(<>Learn React</>);
await expect(component).toContainText('Learn React');
});