From 047564485add5dbf236f1c6504b71358f889e5ad Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Mon, 23 Feb 2026 14:21:51 +0100 Subject: [PATCH 1/5] test(Playwright): enable Playwright component testing --- .gitignore | 7 + eslint.config.mjs | 2 +- package.json | 4 + playwright-ct.config.ts | 43 ++ playwright/index.html | 17 + playwright/index.tsx | 1 + playwright/test/UI5FixturesTestComponents.tsx | 251 ++++++++++ playwright/test/ui5-fixtures.spec.tsx | 258 ++++++++++ playwright/ui5-fixtures.ts | 220 +++++++++ tsconfig.json | 3 + tsconfig.playwright.json | 11 + yarn.lock | 443 +++++++++++++++++- 12 files changed, 1255 insertions(+), 5 deletions(-) create mode 100644 playwright-ct.config.ts create mode 100644 playwright/index.html create mode 100644 playwright/index.tsx create mode 100644 playwright/test/UI5FixturesTestComponents.tsx create mode 100644 playwright/test/ui5-fixtures.spec.tsx create mode 100644 playwright/ui5-fixtures.ts create mode 100644 tsconfig.playwright.json diff --git a/.gitignore b/.gitignore index c84316b4463..5875388016e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,10 @@ debug-storybook.log .vscode .cursor/rules/nx-rules.mdc .github/instructions/nx.instructions.md + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ diff --git a/eslint.config.mjs b/eslint.config.mjs index bdfff080cc6..cc3edac8738 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -206,7 +206,7 @@ const config = tseslint.config( }, }, { - files: ['**/*.cy.ts', '**/*.cy.tsx'], + files: ['**/*.cy.ts', '**/*.cy.tsx', '**/*.spec.ts', '**/*.spec.tsx', 'playwright/**/*'], plugins: { 'no-only-tests': noOnlyTests, diff --git a/package.json b/package.json index 893317e54b5..5a0f6802698 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ "test:prepare": "rimraf temp && lerna run build", "test:open": "CYPRESS_COVERAGE=false cypress open --component --browser chrome", "test": "yarn test:prepare && cypress run --component --browser chrome --spec packages", + "test:pw": "playwright test -c playwright-ct.config.ts", + "test:pw:open": "playwright test -c playwright-ct.config.ts --ui", "clean": "tsc --build --clean && tsc --build tsconfig.build.json --clean && rimraf temp .out && lerna run clean", "clean:remove-modules": "yarn clean && rimraf node_modules", "prettier:all": "prettier --write --config ./prettier.config.js \"**/*\"", @@ -60,6 +62,8 @@ "@cypress/code-coverage": "4.0.0", "@eslint/compat": "2.0.2", "@eslint/js": "9.39.2", + "@playwright/experimental-ct-react": "1.58.2", + "@playwright/test": "1.58.2", "@semantic-release/github": "12.0.6", "@testing-library/cypress": "10.1.0", "@types/jscodeshift": "17.3.0", diff --git a/playwright-ct.config.ts b/playwright-ct.config.ts new file mode 100644 index 00000000000..4ada17b13ac --- /dev/null +++ b/playwright-ct.config.ts @@ -0,0 +1,43 @@ +import { fileURLToPath } from 'node:url'; +import { defineConfig, devices } from '@playwright/experimental-ct-react'; +import react from '@vitejs/plugin-react'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + testDir: '.', + testMatch: ['**/packages/main/src/components/**/test/*.spec.tsx', '**/playwright/test/*.spec.tsx'], + testIgnore: ['**/*.cy.tsx', '**/*.cy.ts', '**/*.stories.tsx', '**/*.mdx'], + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 1 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + trace: 'on-first-retry', + ctViteConfig: { + plugins: [ + react(), + tsconfigPaths({ + projects: [fileURLToPath(new URL('tsconfig.base.json', import.meta.url))], + }), + ], + optimizeDeps: { + esbuildOptions: { + target: 'esnext', + }, + exclude: ['**/*.cy.tsx', '**/*.cy.ts', '**/*.stories.tsx'], + }, + build: { + target: 'esnext', + rollupOptions: { + external: [/\.cy\.tsx$/, /\.cy\.ts$/, /\.stories\.tsx$/], + }, + }, + }, + }, + projects: [ + { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, + { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, + { name: 'webkit', use: { ...devices['Desktop Safari'] } }, + ], +}); diff --git a/playwright/index.html b/playwright/index.html new file mode 100644 index 00000000000..d11d6018986 --- /dev/null +++ b/playwright/index.html @@ -0,0 +1,17 @@ + + + + + + Playwright Component Tests + + + + +
+ + diff --git a/playwright/index.tsx b/playwright/index.tsx new file mode 100644 index 00000000000..135f5a79c4f --- /dev/null +++ b/playwright/index.tsx @@ -0,0 +1 @@ +import '@ui5/webcomponents-react/dist/Assets.js'; diff --git a/playwright/test/UI5FixturesTestComponents.tsx b/playwright/test/UI5FixturesTestComponents.tsx new file mode 100644 index 00000000000..f477b09df8d --- /dev/null +++ b/playwright/test/UI5FixturesTestComponents.tsx @@ -0,0 +1,251 @@ +import { Button } from '@ui5/webcomponents-react/Button'; +import { CheckBox } from '@ui5/webcomponents-react/CheckBox'; +import { ComboBox } from '@ui5/webcomponents-react/ComboBox'; +import { ComboBoxItem } from '@ui5/webcomponents-react/ComboBoxItem'; +import { Dialog } from '@ui5/webcomponents-react/Dialog'; +import { Input } from '@ui5/webcomponents-react/Input'; +import { List } from '@ui5/webcomponents-react/List'; +import { ListItemStandard } from '@ui5/webcomponents-react/ListItemStandard'; +import { MultiComboBox } from '@ui5/webcomponents-react/MultiComboBox'; +import { MultiComboBoxItem } from '@ui5/webcomponents-react/MultiComboBoxItem'; +import { MultiInput } from '@ui5/webcomponents-react/MultiInput'; +import { Option } from '@ui5/webcomponents-react/Option'; +import { RadioButton } from '@ui5/webcomponents-react/RadioButton'; +import { Select } from '@ui5/webcomponents-react/Select'; +import { SuggestionItem } from '@ui5/webcomponents-react/SuggestionItem'; +import { Switch } from '@ui5/webcomponents-react/Switch'; +import { Tab } from '@ui5/webcomponents-react/Tab'; +import { TabContainer } from '@ui5/webcomponents-react/TabContainer'; +import { TextArea } from '@ui5/webcomponents-react/TextArea'; +import { Toolbar } from '@ui5/webcomponents-react/Toolbar'; +import { ToolbarButton } from '@ui5/webcomponents-react/ToolbarButton'; +import { useState } from 'react'; + +export const InputTestComp = () => { + const [value, setValue] = useState(''); + return ( + <> + setValue(e.target.value)} /> + {value} + + ); +}; + +export const ClearInputTestComp = () => { + const [value, setValue] = useState('initial value'); + return ( + <> + setValue(e.target.value)} /> + {value} + + ); +}; + +export const CheckboxTestComp = () => { + const [checked, setChecked] = useState(false); + return ( +
+ setChecked(e.target.checked)} /> + {checked ? 'checked' : 'unchecked'} +
+ ); +}; + +export const SwitchTestComp = () => { + const [checked, setChecked] = useState(false); + return ( +
+ setChecked(e.target.checked)} /> + {checked ? 'on' : 'off'} +
+ ); +}; + +export const RadioButtonTestComp = () => { + const [selected, setSelected] = useState(''); + return ( +
+ setSelected('option1')} /> + setSelected('option2')} /> + {selected} +
+ ); +}; + +export const TextAreaTestComp = () => { + const [value, setValue] = useState(''); + return ( +
+