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:
parent
0fd9452127
commit
208a54529d
|
|
@ -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
|
* @param {any} value
|
||||||
*/
|
*/
|
||||||
function __pwRender(value) {
|
function __pwRender(value) {
|
||||||
return window.__pwTransformObject(value, v => {
|
return window.__pwTransformObject(value, v => {
|
||||||
if (isJsxComponent(v)) {
|
if (isJsxComponent(v)) {
|
||||||
const component = v;
|
const component = v;
|
||||||
|
let type = component.type;
|
||||||
|
if (isJsxFragment(type))
|
||||||
|
type = __pwReact.Fragment;
|
||||||
const props = component.props ? __pwRender(component.props) : {};
|
const props = component.props ? __pwRender(component.props) : {};
|
||||||
const key = component.key ? __pwRender(component.key) : undefined;
|
const key = component.key ? __pwRender(component.key) : undefined;
|
||||||
const { children, ...propsWithoutChildren } = props;
|
const { children, ...propsWithoutChildren } = props;
|
||||||
|
|
@ -47,7 +59,7 @@ function __pwRender(value) {
|
||||||
const createElementArguments = [propsWithoutChildren];
|
const createElementArguments = [propsWithoutChildren];
|
||||||
if (children)
|
if (children)
|
||||||
createElementArguments.push(children);
|
createElementArguments.push(children);
|
||||||
return { result: __pwReact.createElement(component.type, ...createElementArguments) };
|
return { result: __pwReact.createElement(type, ...createElementArguments) };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ function jsxs(type, props, key) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Fragment = {};
|
// this is used in <></> notation
|
||||||
|
const Fragment = { __pw_jsx_fragment: true };
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Fragment,
|
Fragment,
|
||||||
|
|
|
||||||
|
|
@ -46,3 +46,8 @@ test('render inline component with an error if its nested', async ({ mount }) =>
|
||||||
<MyInlineComponent value="Max" />
|
<MyInlineComponent value="Max" />
|
||||||
</DefaultChildren>)).rejects.toThrow('Component "MyInlineComponent" cannot be mounted.');
|
</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');
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue