diff --git a/package-lock.json b/package-lock.json index cc8b491c4e..3b55e120bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1297,6 +1297,10 @@ "resolved": "packages/playwright-ct-react", "link": true }, + "node_modules/@playwright/experimental-ct-react17": { + "resolved": "packages/playwright-ct-react17", + "link": true + }, "node_modules/@playwright/experimental-ct-solid": { "resolved": "packages/playwright-ct-solid", "link": true @@ -5990,6 +5994,22 @@ "node": ">=14" } }, + "packages/playwright-ct-react17": { + "name": "@playwright/experimental-ct-react17", + "version": "1.32.0-next", + "license": "Apache-2.0", + "dependencies": { + "@playwright/test": "1.32.0-next", + "@vitejs/plugin-react": "^3.1.0", + "vite": "^4.1.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, "packages/playwright-ct-solid": { "name": "@playwright/experimental-ct-solid", "version": "1.32.0-next", @@ -6908,6 +6928,14 @@ "vite": "^4.1.1" } }, + "@playwright/experimental-ct-react17": { + "version": "file:packages/playwright-ct-react17", + "requires": { + "@playwright/test": "1.32.0-next", + "@vitejs/plugin-react": "^3.1.0", + "vite": "^4.1.1" + } + }, "@playwright/experimental-ct-solid": { "version": "file:packages/playwright-ct-solid", "requires": { diff --git a/packages/html-reporter/src/headerView.spec.tsx b/packages/html-reporter/src/headerView.spec.tsx index 32112305c5..733df21b6b 100644 --- a/packages/html-reporter/src/headerView.spec.tsx +++ b/packages/html-reporter/src/headerView.spec.tsx @@ -84,6 +84,8 @@ test('should show the project names', async ({ mount }) => { > ); await expect(component.getByText('Project: my-project')).toBeVisible(); + + await component.unmount(); }); await test.step('with 1 project and empty projectName', async () => { const component = await mount( { > ); await expect(component.getByText('Project:')).toBeHidden(); + + await component.unmount(); }); await test.step('with more than 1 project', async () => { const component = await mount( { ); await expect(component.getByText('my-project')).toBeHidden(); await expect(component.getByText('great-project')).toBeHidden(); + + await component.unmount(); }); }); diff --git a/packages/playwright-ct-react/registerSource.mjs b/packages/playwright-ct-react/registerSource.mjs index ff26e17c9c..71b6283c42 100644 --- a/packages/playwright-ct-react/registerSource.mjs +++ b/packages/playwright-ct-react/registerSource.mjs @@ -17,14 +17,16 @@ // @ts-check // This file is injected into the registry as text, no dependencies are allowed. -import React from 'react'; -import ReactDOM from 'react-dom'; +import * as React from 'react'; +import { createRoot } from 'react-dom/client'; /** @typedef {import('../playwright-test/types/component').Component} Component */ /** @typedef {import('react').FunctionComponent} FrameworkComponent */ /** @type {Map} */ const registry = new Map(); +/** @type {Map>} */ +const rootRegistry = new Map(); /** * @param {{[key: string]: FrameworkComponent}} components @@ -79,17 +81,33 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => { App = () => wrapper; } - ReactDOM.render(App(), rootElement); + if (rootRegistry.has(rootElement)) { + throw new Error( + 'Attempting to mount a component into an container that already has a React root' + ); + } + + const root = createRoot(rootElement); + rootRegistry.set(rootElement, root); + root.render(App()); for (const hook of window.__pw_hooks_after_mount || []) await hook({ hooksConfig }); }; window.playwrightUnmount = async rootElement => { - if (!ReactDOM.unmountComponentAtNode(rootElement)) + const root = rootRegistry.get(rootElement); + if (root === undefined) throw new Error('Component was not mounted'); + + root.unmount(); + rootRegistry.delete(rootElement); }; window.playwrightUpdate = async (rootElement, component) => { - ReactDOM.render(render(/** @type {Component} */(component)), rootElement); + const root = rootRegistry.get(rootElement); + if (root === undefined) + throw new Error('Component was not mounted'); + + root.render(render(/** @type {Component} */ (component))); }; diff --git a/packages/playwright-ct-react17/.npmignore b/packages/playwright-ct-react17/.npmignore new file mode 100644 index 0000000000..62701eb493 --- /dev/null +++ b/packages/playwright-ct-react17/.npmignore @@ -0,0 +1,12 @@ +**/* + +!README.md +!LICENSE +!cli.js +!register.d.ts +!register.mjs +!registerSource.mjs +!index.d.ts +!index.js +!hooks.d.ts +!hooks.mjs diff --git a/packages/playwright-ct-react17/README.md b/packages/playwright-ct-react17/README.md new file mode 100644 index 0000000000..23ae82312d --- /dev/null +++ b/packages/playwright-ct-react17/README.md @@ -0,0 +1,3 @@ +> **BEWARE** This package is EXPERIMENTAL and does not respect semver. + +Read more at https://playwright.dev/docs/test-components diff --git a/packages/playwright-ct-react17/cli.js b/packages/playwright-ct-react17/cli.js new file mode 100755 index 0000000000..17ed363fa2 --- /dev/null +++ b/packages/playwright-ct-react17/cli.js @@ -0,0 +1,17 @@ +#!/usr/bin/env node +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +module.exports = require('playwright-core/cli'); diff --git a/packages/playwright-ct-react17/hooks.d.ts b/packages/playwright-ct-react17/hooks.d.ts new file mode 100644 index 0000000000..bd84d3bcfe --- /dev/null +++ b/packages/playwright-ct-react17/hooks.d.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +type JsonPrimitive = string | number | boolean | null; +type JsonValue = JsonPrimitive | JsonObject | JsonArray; +type JsonArray = JsonValue[]; +type JsonObject = { [Key in string]?: JsonValue }; +export declare function beforeMount( + callback: (params: { hooksConfig: HooksConfig; App: () => JSX.Element }) => Promise +): void; +export declare function afterMount( + callback: (params: { hooksConfig: HooksConfig }) => Promise +): void; diff --git a/packages/playwright-ct-react17/hooks.mjs b/packages/playwright-ct-react17/hooks.mjs new file mode 100644 index 0000000000..b7cea242c4 --- /dev/null +++ b/packages/playwright-ct-react17/hooks.mjs @@ -0,0 +1,29 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const __pw_hooks_before_mount = []; +const __pw_hooks_after_mount = []; + +window.__pw_hooks_before_mount = __pw_hooks_before_mount; +window.__pw_hooks_after_mount = __pw_hooks_after_mount; + +export const beforeMount = callback => { + __pw_hooks_before_mount.push(callback); +}; + +export const afterMount = callback => { + __pw_hooks_after_mount.push(callback); +}; diff --git a/packages/playwright-ct-react17/index.d.ts b/packages/playwright-ct-react17/index.d.ts new file mode 100644 index 0000000000..63327ee138 --- /dev/null +++ b/packages/playwright-ct-react17/index.d.ts @@ -0,0 +1,70 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { + TestType, + PlaywrightTestArgs, + PlaywrightTestConfig as BasePlaywrightTestConfig, + PlaywrightTestOptions, + PlaywrightWorkerArgs, + PlaywrightWorkerOptions, + Locator, +} from '@playwright/test'; +import type { InlineConfig } from 'vite'; + +export type PlaywrightTestConfig = Omit, 'use'> & { + use?: BasePlaywrightTestConfig['use'] & { + ctPort?: number; + ctTemplateDir?: string; + ctCacheDir?: string; + ctViteConfig?: InlineConfig | (() => Promise); + }; +}; + +type JsonPrimitive = string | number | boolean | null; +type JsonValue = JsonPrimitive | JsonObject | JsonArray; +type JsonArray = JsonValue[]; +type JsonObject = { [Key in string]?: JsonValue }; + +export interface MountOptions { + hooksConfig?: HooksConfig; +} + +interface MountResult extends Locator { + unmount(): Promise; + update(component: JSX.Element): Promise; +} + +export interface ComponentFixtures { + mount( + component: JSX.Element, + options?: MountOptions + ): Promise; +} + +export const test: TestType< + PlaywrightTestArgs & PlaywrightTestOptions & ComponentFixtures, + PlaywrightWorkerArgs & PlaywrightWorkerOptions +>; + +/** + * Defines Playwright config + */ +export function defineConfig(config: PlaywrightTestConfig): PlaywrightTestConfig; +export function defineConfig(config: PlaywrightTestConfig): PlaywrightTestConfig; +export function defineConfig(config: PlaywrightTestConfig): PlaywrightTestConfig; + +export { expect, devices } from '@playwright/test'; diff --git a/packages/playwright-ct-react17/index.js b/packages/playwright-ct-react17/index.js new file mode 100644 index 0000000000..b40cd0a184 --- /dev/null +++ b/packages/playwright-ct-react17/index.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/test'); +const { fixtures } = require('@playwright/test/lib/mount'); +const path = require('path'); + +const plugin = () => { + // Only fetch upon request to avoid resolution in workers. + const { createPlugin } = require('@playwright/test/lib/plugins/vitePlugin'); + return createPlugin( + path.join(__dirname, 'registerSource.mjs'), + () => import('@vitejs/plugin-react').then(plugin => plugin.default())); +}; +const defineConfig = config => originalDefineConfig({ ...config, _plugins: [plugin] }); +const test = baseTest.extend(fixtures); + +module.exports = { test, expect, devices, defineConfig }; diff --git a/packages/playwright-ct-react17/package.json b/packages/playwright-ct-react17/package.json new file mode 100644 index 0000000000..4a561c060a --- /dev/null +++ b/packages/playwright-ct-react17/package.json @@ -0,0 +1,36 @@ +{ + "name": "@playwright/experimental-ct-react17", + "version": "1.32.0-next", + "description": "Playwright Component Testing for React", + "repository": "github:Microsoft/playwright", + "homepage": "https://playwright.dev", + "engines": { + "node": ">=14" + }, + "author": { + "name": "Microsoft Corporation" + }, + "license": "Apache-2.0", + "exports": { + ".": { + "types": "./index.d.ts", + "default": "./index.js" + }, + "./register": { + "types": "./register.d.ts", + "default": "./register.mjs" + }, + "./hooks": { + "types": "./hooks.d.ts", + "default": "./hooks.mjs" + } + }, + "dependencies": { + "@vitejs/plugin-react": "^3.1.0", + "@playwright/test": "1.32.0-next", + "vite": "^4.1.1" + }, + "bin": { + "playwright": "./cli.js" + } +} diff --git a/packages/playwright-ct-react17/register.d.ts b/packages/playwright-ct-react17/register.d.ts new file mode 100644 index 0000000000..0925c1f690 --- /dev/null +++ b/packages/playwright-ct-react17/register.d.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default function register( + components: { [key: string]: any }, +): void diff --git a/packages/playwright-ct-react17/register.mjs b/packages/playwright-ct-react17/register.mjs new file mode 100644 index 0000000000..47f32ee602 --- /dev/null +++ b/packages/playwright-ct-react17/register.mjs @@ -0,0 +1,21 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { register } from './registerSource.mjs'; + +export default components => { + register(components); +}; diff --git a/packages/playwright-ct-react17/registerSource.mjs b/packages/playwright-ct-react17/registerSource.mjs new file mode 100644 index 0000000000..5c8c97f4f7 --- /dev/null +++ b/packages/playwright-ct-react17/registerSource.mjs @@ -0,0 +1,93 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @ts-check +// This file is injected into the registry as text, no dependencies are allowed. + +import React from 'react'; +import ReactDOM from 'react-dom'; + +/** @typedef {import('../playwright-test/types/component').Component} Component */ +/** @typedef {import('react').FunctionComponent} FrameworkComponent */ + +/** @type {Map} */ +const registry = new Map(); + +/** + * @param {{[key: string]: FrameworkComponent}} components + */ +export function register(components) { + for (const [name, value] of Object.entries(components)) + registry.set(name, value); +} + +/** + * @param {Component} component + * @returns {JSX.Element} + */ +function render(component) { + let componentFunc = registry.get(component.type); + if (!componentFunc) { + // Lookup by shorthand. + for (const [name, value] of registry) { + if (component.type.endsWith(`_${name}`)) { + componentFunc = value; + break; + } + } + } + + if (!componentFunc && component.type[0].toUpperCase() === component.type[0]) + throw new Error(`Unregistered component: ${component.type}. Following components are registered: ${[...registry.keys()]}`); + + const componentFuncOrString = componentFunc || component.type; + + if (component.kind !== 'jsx') + throw new Error('Object mount notation is not supported'); + + return React.createElement(componentFuncOrString, component.props, ...component.children.map(child => { + if (typeof child === 'string') + return child; + return render(child); + }).filter(child => { + if (typeof child === 'string') + return !!child.trim(); + return true; + })); +} + +window.playwrightMount = async (component, rootElement, hooksConfig) => { + let App = () => render(component); + for (const hook of window.__pw_hooks_before_mount || []) { + const wrapper = await hook({ App, hooksConfig }); + if (wrapper) + App = () => wrapper; + } + + ReactDOM.render(App(), rootElement); + + for (const hook of window.__pw_hooks_after_mount || []) + await hook({ hooksConfig }); +}; + +window.playwrightUnmount = async rootElement => { + if (!ReactDOM.unmountComponentAtNode(rootElement)) + throw new Error('Component was not mounted'); +}; + +window.playwrightUpdate = async (rootElement, component) => { + ReactDOM.render(render(/** @type {Component} */(component)), rootElement); +}; diff --git a/tests/components/ct-react-vite/package.json b/tests/components/ct-react-vite/package.json index 75b569e951..56c9325ed7 100644 --- a/tests/components/ct-react-vite/package.json +++ b/tests/components/ct-react-vite/package.json @@ -9,19 +9,19 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-router-dom": "^6.4.2" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.6.1" }, "devDependencies": { - "@types/react": "^17.0.33", - "@types/react-dom": "^17.0.10", + "@types/react": "^18.0.26", + "@types/react-dom": "^18.0.10", "@vitejs/plugin-react": "^3.0.0", "typescript": "^4.5.4", "vite": "^4.1.1" }, "@standaloneDevDependencies": { - "@playwright/experimental-ct-react": "^1.22.2", + "@playwright/experimental-ct-react": "^1.22.0", "@playwright/test": "^1.22.2" } } diff --git a/tests/components/ct-react-vite/src/components/Counter.tsx b/tests/components/ct-react-vite/src/components/Counter.tsx index 2548202aec..acb2cc5f1a 100644 --- a/tests/components/ct-react-vite/src/components/Counter.tsx +++ b/tests/components/ct-react-vite/src/components/Counter.tsx @@ -1,4 +1,4 @@ -import { useRef } from "react" +import { useLayoutEffect, useRef, useState } from "react" type CounterProps = { count?: number; @@ -9,11 +9,17 @@ import { useRef } from "react" let _remountCount = 1; export default function Counter(props: CounterProps) { - const remountCount = useRef(_remountCount++); + const [remountCount] = useState(_remountCount); + const didMountRef = useRef(false) + useLayoutEffect(() => { + if (!didMountRef.current) { + didMountRef.current = true; + _remountCount++; + } + }, []) return
props.onClick?.('hello')}>
{ props.count }
-
{ remountCount.current }
+
{ remountCount }
{ props.children }
} - \ No newline at end of file diff --git a/tests/components/ct-react-vite/src/main.tsx b/tests/components/ct-react-vite/src/main.tsx index e02d5e7d44..f3fe4a896d 100644 --- a/tests/components/ct-react-vite/src/main.tsx +++ b/tests/components/ct-react-vite/src/main.tsx @@ -1,12 +1,13 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; +import * as React from 'react'; +import { createRoot } from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; import App from './App'; import './assets/index.css'; -ReactDOM.render( +createRoot(document.getElementById("root")!).render( - - , - document.getElementById('root') -) + + + + +); diff --git a/tests/components/ct-react/package.json b/tests/components/ct-react/package.json index abaa43fbd8..6d4e31fa46 100644 --- a/tests/components/ct-react/package.json +++ b/tests/components/ct-react/package.json @@ -3,19 +3,19 @@ "version": "0.1.0", "private": true, "dependencies": { - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-router-dom": "^6.4.2" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.6.1" }, "devDependencies": { "@types/node": "^16.11.26", - "@types/react": "^17.0.39", - "@types/react-dom": "^17.0.13", + "@types/react": "^18.0.26", + "@types/react-dom": "^18.0.10", "react-scripts": "5.0.0", "typescript": "^4.6.2" }, "@standaloneDevDependencies": { - "@playwright/experimental-ct-react": "^1.22.2", + "@playwright/experimental-ct-react": "^1.2.2", "@playwright/test": "^1.22.2" }, "scripts": { diff --git a/tests/components/ct-react/src/components/Counter.tsx b/tests/components/ct-react/src/components/Counter.tsx index 2548202aec..acb2cc5f1a 100644 --- a/tests/components/ct-react/src/components/Counter.tsx +++ b/tests/components/ct-react/src/components/Counter.tsx @@ -1,4 +1,4 @@ -import { useRef } from "react" +import { useLayoutEffect, useRef, useState } from "react" type CounterProps = { count?: number; @@ -9,11 +9,17 @@ import { useRef } from "react" let _remountCount = 1; export default function Counter(props: CounterProps) { - const remountCount = useRef(_remountCount++); + const [remountCount] = useState(_remountCount); + const didMountRef = useRef(false) + useLayoutEffect(() => { + if (!didMountRef.current) { + didMountRef.current = true; + _remountCount++; + } + }, []) return
props.onClick?.('hello')}>
{ props.count }
-
{ remountCount.current }
+
{ remountCount }
{ props.children }
} - \ No newline at end of file diff --git a/tests/components/ct-react/src/index.js b/tests/components/ct-react/src/index.js index 34f6873778..2ed26f3b4b 100644 --- a/tests/components/ct-react/src/index.js +++ b/tests/components/ct-react/src/index.js @@ -1,12 +1,13 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; +import * as React from 'react'; +import { createRoot } from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; import App from './App'; import './assets/index.css'; -ReactDOM.render( +createRoot(document.getElementById("root")).render( - - , - document.getElementById('root') + + + + ); diff --git a/tests/playwright-test/playwright.ct-build.spec.ts b/tests/playwright-test/playwright.ct-build.spec.ts index a9a2f1a11c..eee94f366b 100644 --- a/tests/playwright-test/playwright.ct-build.spec.ts +++ b/tests/playwright-test/playwright.ct-build.spec.ts @@ -45,7 +45,7 @@ test('should work with the empty component list', async ({ runInlineTest }, test expect(metainfo.version).toEqual(require('playwright-core/package.json').version); expect(metainfo.viteVersion).toEqual(require('vite/package.json').version); expect(Object.entries(metainfo.tests)).toHaveLength(1); - expect(Object.entries(metainfo.sources)).toHaveLength(8); + expect(Object.entries(metainfo.sources)).toHaveLength(9); }); test('should extract component list', async ({ runInlineTest }, testInfo) => { diff --git a/tsconfig.json b/tsconfig.json index d2d4e9e26b..c81ac3ab54 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -34,6 +34,7 @@ "packages/*/lib", "packages/html-reporter", "packages/playwright-ct-react", + "packages/playwright-ct-react17", "packages/playwright-ct-solid", "packages/playwright-ct-svelte", "packages/playwright-ct-vue", diff --git a/utils/workspace.js b/utils/workspace.js index b9a57b7627..fdce5ac467 100755 --- a/utils/workspace.js +++ b/utils/workspace.js @@ -178,6 +178,11 @@ const workspace = new Workspace(ROOT_PATH, [ path: path.join(ROOT_PATH, 'packages', 'playwright-ct-react'), files: ['LICENSE'], }), + new PWPackage({ + name: '@playwright/experimental-ct-react17', + path: path.join(ROOT_PATH, 'packages', 'playwright-ct-react17'), + files: ['LICENSE'], + }), new PWPackage({ name: '@playwright/experimental-ct-solid', path: path.join(ROOT_PATH, 'packages', 'playwright-ct-solid'),