diff --git a/packages/playwright-ct-react/index.d.ts b/packages/playwright-ct-react/index.d.ts index 17c946ac1b..4be9b96787 100644 --- a/packages/playwright-ct-react/index.d.ts +++ b/packages/playwright-ct-react/index.d.ts @@ -34,17 +34,12 @@ export type PlaywrightTestConfig = Omit & { } }; -export interface MountOptions { - hooksConfig?: any; -} - interface MountResult extends Locator { unmount(): Promise; - rerender(component: JSX.Element): Promise; } export interface ComponentFixtures { - mount(component: JSX.Element, options?: MountOptions): Promise; + mount(component: JSX.Element, options?: { hooksConfig?: any }): Promise; } export const test: TestType< diff --git a/packages/playwright-ct-react/registerSource.mjs b/packages/playwright-ct-react/registerSource.mjs index f1183f5392..a6948efe7d 100644 --- a/packages/playwright-ct-react/registerSource.mjs +++ b/packages/playwright-ct-react/registerSource.mjs @@ -82,7 +82,3 @@ window.playwrightUnmount = async rootElement => { if (!ReactDOM.unmountComponentAtNode(rootElement)) throw new Error('Component was not mounted'); }; - -window.playwrightRerender = async (rootElement, component) => { - ReactDOM.render(render(/** @type {Component} */(component)), rootElement); -}; diff --git a/packages/playwright-test/src/mount.ts b/packages/playwright-test/src/mount.ts index 17ab6c3cde..feeb6c9bf5 100644 --- a/packages/playwright-test/src/mount.ts +++ b/packages/playwright-test/src/mount.ts @@ -21,7 +21,7 @@ let boundCallbacksForMount: Function[] = []; interface MountResult extends Locator { unmount(locator: Locator): Promise; - rerender(options: Omit | string | JsxComponent): Promise; + rerender(options: Omit): Promise; } export const fixtures: Fixtures< @@ -60,8 +60,11 @@ export const fixtures: Fixtures< await window.playwrightUnmount(rootElement); }); }, - rerender: async (component: JsxComponent | string, options?: Omit) => { - await innerRerender(page, component, options); + rerender: async (options: Omit) => { + await locator.evaluate(async (element, options) => { + const rootElement = document.getElementById('root')!; + return await window.playwrightRerender(rootElement, options); + }, options); } }); }); @@ -69,32 +72,13 @@ export const fixtures: Fixtures< }, }; -async function innerRerender(page: Page, jsxOrType: JsxComponent | string, options: Omit = {}): Promise { - const component = createComponent(jsxOrType, options); - wrapFunctions(component, page, boundCallbacksForMount); - - await page.evaluate(async ({ component }) => { - const unwrapFunctions = (object: any) => { - for (const [key, value] of Object.entries(object)) { - if (typeof value === 'string' && (value as string).startsWith('__pw_func_')) { - const ordinal = +value.substring('__pw_func_'.length); - object[key] = (...args: any[]) => { - (window as any)['__ct_dispatch'](ordinal, args); - }; - } else if (typeof value === 'object' && value) { - unwrapFunctions(value); - } - } - }; - - unwrapFunctions(component); - const rootElement = document.getElementById('root')!; - return await window.playwrightRerender(rootElement, component); - }, { component }); -} - async function innerMount(page: Page, jsxOrType: JsxComponent | string, options: MountOptions = {}): Promise { - const component = createComponent(jsxOrType, options); + let component: Component; + if (typeof jsxOrType === 'string') + component = { kind: 'object', type: jsxOrType, options }; + else + component = jsxOrType; + wrapFunctions(component, page, boundCallbacksForMount); // WebKit does not wait for deferred scripts. @@ -130,11 +114,6 @@ async function innerMount(page: Page, jsxOrType: JsxComponent | string, options: return selector; } -function createComponent(jsxOrType: JsxComponent | string, options: Omit = {}): Component { - if (typeof jsxOrType !== 'string') return jsxOrType; - return { kind: 'object', type: jsxOrType, options }; -} - function wrapFunctions(object: any, page: Page, callbacks: Function[]) { for (const [key, value] of Object.entries(object)) { const type = typeof value; diff --git a/packages/playwright-test/types/component.d.ts b/packages/playwright-test/types/component.d.ts index 5fc210c814..561d400832 100644 --- a/packages/playwright-test/types/component.d.ts +++ b/packages/playwright-test/types/component.d.ts @@ -40,6 +40,6 @@ declare global { interface Window { playwrightMount(component: Component, rootElement: Element, hooksConfig: any): Promise; playwrightUnmount(rootElement: Element): Promise; - playwrightRerender(rootElement: Element, optionsOrComponent: Omit | Component): Promise; + playwrightRerender(rootElement: Element, options: Omit): Promise; } } diff --git a/tests/components/ct-react-vite/playwright/index.ts b/tests/components/ct-react-vite/playwright/index.ts index 267e6fd64f..3d3b46bdc0 100644 --- a/tests/components/ct-react-vite/playwright/index.ts +++ b/tests/components/ct-react-vite/playwright/index.ts @@ -1,5 +1,7 @@ //@ts-check -import '../src/assets/index.css'; + +import '../src/index.css'; + import { beforeMount, afterMount } from '@playwright/experimental-ct-react/hooks'; beforeMount(async ({ hooksConfig }) => { diff --git a/tests/components/ct-react-vite/src/App.spec.tsx b/tests/components/ct-react-vite/src/App.spec.tsx new file mode 100644 index 0000000000..4de184d7c0 --- /dev/null +++ b/tests/components/ct-react-vite/src/App.spec.tsx @@ -0,0 +1,27 @@ +import { test, expect } from '@playwright/experimental-ct-react'; +import App from './App'; + +test.use({ viewport: { width: 500, height: 500 } }); + +test('should work', async ({ mount }) => { + const component = await mount(); + await expect(component).toContainText('Hello Vite + React!'); +}); + +test('should configure app', async ({ page, mount }) => { + const messages: string[] = []; + page.on('console', m => messages.push(m.text())); + await mount(, { + hooksConfig: { + route: 'A' + } + }); + expect(messages).toEqual(['Before mount: {\"route\":\"A\"}', 'After mount']); +}); + +test('should unmount', async ({ page, mount }) => { + const component = await mount(); + await expect(page.locator('#root')).toContainText('Hello Vite + React!'); + await component.unmount(); + await expect(page.locator('#root')).not.toContainText('Hello Vite + React!'); +}); diff --git a/tests/components/ct-react-vite/src/App.tsx b/tests/components/ct-react-vite/src/App.tsx index 1a5c71390f..78b8518c54 100644 --- a/tests/components/ct-react-vite/src/App.tsx +++ b/tests/components/ct-react-vite/src/App.tsx @@ -1,5 +1,6 @@ +import React from 'react' import { useState } from 'react' -import logo from './components/logo.svg' +import logo from './logo.svg' import './App.css' function App() { diff --git a/tests/components/ct-react-vite/src/components/Button.tsx b/tests/components/ct-react-vite/src/components/Button.tsx deleted file mode 100644 index 78b0a7791f..0000000000 --- a/tests/components/ct-react-vite/src/components/Button.tsx +++ /dev/null @@ -1,7 +0,0 @@ -type ButtonProps = { - title: string; - onClick?(props: string): void; -} -export default function Button(props: ButtonProps) { - return -} diff --git a/tests/components/ct-react-vite/src/components/Counter.tsx b/tests/components/ct-react-vite/src/components/Counter.tsx deleted file mode 100644 index cfba1e3057..0000000000 --- a/tests/components/ct-react-vite/src/components/Counter.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useRef } from "react" - -type CounterProps = { - count?: number; - onClick?(props: string): void; - children?: any; -} - -let _remountCount = 1; - -export default function Counter(props: CounterProps) { - const remountCount = useRef(_remountCount++); - return
props.onClick?.('hello')}> -
{ props.count }
-
{ remountCount.current }
- { props.children } -
-} diff --git a/tests/components/ct-react-vite/src/components/DefaultChildren.tsx b/tests/components/ct-react-vite/src/components/DefaultChildren.tsx deleted file mode 100644 index 691b6a0806..0000000000 --- a/tests/components/ct-react-vite/src/components/DefaultChildren.tsx +++ /dev/null @@ -1,15 +0,0 @@ -type DefaultChildrenProps = { - children?: any; -} - -export default function DefaultChildren(props: DefaultChildrenProps) { - return
-

Welcome!

-
- {props.children} -
-
- Thanks for visiting. -
-
-} diff --git a/tests/components/ct-react-vite/src/components/MultiRoot.tsx b/tests/components/ct-react-vite/src/components/MultiRoot.tsx deleted file mode 100644 index f29e397c0f..0000000000 --- a/tests/components/ct-react-vite/src/components/MultiRoot.tsx +++ /dev/null @@ -1,6 +0,0 @@ -export default function MultiRoot() { - return <> -
root 1
-
root 2
- -} diff --git a/tests/components/ct-react-vite/src/components/MultipleChildren.tsx b/tests/components/ct-react-vite/src/components/MultipleChildren.tsx deleted file mode 100644 index 63bd0104c6..0000000000 --- a/tests/components/ct-react-vite/src/components/MultipleChildren.tsx +++ /dev/null @@ -1,18 +0,0 @@ - -type MultipleChildrenProps = { - children?: [any, any, any]; -} - -export default function MultipleChildren(props: MultipleChildrenProps) { - return
-
- {props.children?.at(0)} -
-
- {props.children?.at(1)} -
-
- {props.children?.at(2)} -
-
-} diff --git a/tests/components/ct-react-vite/src/assets/favicon.svg b/tests/components/ct-react-vite/src/favicon.svg similarity index 100% rename from tests/components/ct-react-vite/src/assets/favicon.svg rename to tests/components/ct-react-vite/src/favicon.svg diff --git a/tests/components/ct-react-vite/src/assets/index.css b/tests/components/ct-react-vite/src/index.css similarity index 100% rename from tests/components/ct-react-vite/src/assets/index.css rename to tests/components/ct-react-vite/src/index.css diff --git a/tests/components/ct-react-vite/src/assets/logo.svg b/tests/components/ct-react-vite/src/logo.svg similarity index 100% rename from tests/components/ct-react-vite/src/assets/logo.svg rename to tests/components/ct-react-vite/src/logo.svg diff --git a/tests/components/ct-react-vite/src/main.tsx b/tests/components/ct-react-vite/src/main.tsx index 919f50052b..606a3cf44e 100644 --- a/tests/components/ct-react-vite/src/main.tsx +++ b/tests/components/ct-react-vite/src/main.tsx @@ -1,6 +1,6 @@ import React from 'react' import ReactDOM from 'react-dom' -import './assets/index.css' +import './index.css' import App from './App' ReactDOM.render( diff --git a/tests/components/ct-react-vite/src/tests.spec.tsx b/tests/components/ct-react-vite/src/tests.spec.tsx deleted file mode 100644 index dd57589eaf..0000000000 --- a/tests/components/ct-react-vite/src/tests.spec.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { test, expect } from '@playwright/experimental-ct-react'; -import Button from './components/Button'; -import DefaultChildren from './components/DefaultChildren'; -import MultipleChildren from './components/MultipleChildren'; -import MultiRoot from './components/MultiRoot'; -import Counter from './components/Counter'; - -test.use({ viewport: { width: 500, height: 500 } }); - -test('props should work', async ({ mount }) => { - const component = await mount() - await component.click() - expect(messages).toEqual(['hello']) -}) - -test('default slot should work', async ({ mount }) => { - const component = await mount( - Main Content - ) - await expect(component).toContainText('Main Content') -}) - -test('multiple children should work', async ({ mount }) => { - const component = await mount( -
One
-
Two
-
) - await expect(component.locator('#one')).toContainText('One') - await expect(component.locator('#two')).toContainText('Two') -}) - -test('named children should work', async ({ mount }) => { - const component = await mount( -
Header
-
Main Content
-
Footer
-
); - await expect(component).toContainText('Header') - await expect(component).toContainText('Main Content') - await expect(component).toContainText('Footer') -}) - -test('children should callback', async ({ mount }) => { - let clickFired = false; - const component = await mount( - clickFired = true}>Main Content - ); - await component.locator('text=Main Content').click(); - expect(clickFired).toBeTruthy(); -}) - -test('should run hooks', async ({ page, mount }) => { - const messages: string[] = []; - page.on('console', m => messages.push(m.text())); - await mount(