From 18e7ca5421c319da6bf3da82f157cbfc569575e9 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Wed, 22 Jul 2026 14:03:36 +0200 Subject: [PATCH 1/4] Revert "fix(cli): don't crash the response when navigating closes the last tab" (#41931) --- packages/playwright-core/src/tools/backend/response.ts | 3 +-- tests/mcp/cli-navigation.spec.ts | 7 ------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/playwright-core/src/tools/backend/response.ts b/packages/playwright-core/src/tools/backend/response.ts index d28de344964bf..5a35236996240 100644 --- a/packages/playwright-core/src/tools/backend/response.ts +++ b/packages/playwright-core/src/tools/backend/response.ts @@ -275,8 +275,7 @@ export class Response { if (this._includeSnapshot !== 'none' || tabHeaders.some(header => header.changed)) { if (tabHeaders.length !== 1) addSection('Open tabs', renderTabsMarkdown(tabHeaders)); - if (tabHeaders.length) - addSection('Page', renderTabMarkdown(tabHeaders.find(h => h.current) ?? tabHeaders[0])); + addSection('Page', renderTabMarkdown(tabHeaders.find(h => h.current) ?? tabHeaders[0])); } // Handle modal states. diff --git a/tests/mcp/cli-navigation.spec.ts b/tests/mcp/cli-navigation.spec.ts index 3d07790288a86..74f606fadf051 100644 --- a/tests/mcp/cli-navigation.spec.ts +++ b/tests/mcp/cli-navigation.spec.ts @@ -52,10 +52,3 @@ test('run-code', async ({ cli, server }) => { const { output } = await cli('run-code', '() => page.title()'); expect(output).toContain('"Title"'); }); - -test('goto chrome:// page that closes the tab does not crash the response', async ({ cli, server, mcpBrowser }) => { - test.skip(mcpBrowser !== 'chromium' && mcpBrowser !== 'chrome', 'chrome:// pages are chromium-specific'); - await cli('open', server.HELLO_WORLD); - const { output } = await cli('goto', 'chrome://extensions/'); - expect(output).toContain('No open tabs. Navigate to a URL to create one.'); -}); From 3e95c6c8fcc86ec46ed0fd1eb1cdf35572d04871 Mon Sep 17 00:00:00 2001 From: Devin Rousso Date: Wed, 22 Jul 2026 08:58:35 -0600 Subject: [PATCH 2/4] fix(role): name `` and `` from an associated label (#41911) `` and `` are labelable, but the html-aam per-element name step only ran the associated-labels branch for `TEXTAREA`/`SELECT`/`INPUT` (plus `BUTTON`/`OUTPUT`) fixes --- packages/injected/src/roleUtils.ts | 4 ++-- tests/library/role-utils.spec.ts | 15 +++++++++++++++ tests/library/selector-generator.spec.ts | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/injected/src/roleUtils.ts b/packages/injected/src/roleUtils.ts index 6e9f8daa24929..a0e7f43339a2b 100644 --- a/packages/injected/src/roleUtils.ts +++ b/packages/injected/src/roleUtils.ts @@ -848,9 +848,9 @@ function getTextAlternativeInternal(element: Element, options: AccessibleNameOpt // For "other form elements", we count select and any other input. // // Note: WebKit does not follow the spec and uses placeholder when aria-labelledby is present. - if (!labelledBy && (tagName === 'TEXTAREA' || tagName === 'SELECT' || tagName === 'INPUT')) { + if (!labelledBy && (tagName === 'TEXTAREA' || tagName === 'SELECT' || tagName === 'INPUT' || tagName === 'METER' || tagName === 'PROGRESS')) { options.visitedElements.add(element); - const labels = (element as (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)).labels || []; + const labels = (element as (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLMeterElement | HTMLProgressElement)).labels || []; if (labels.length) return getAccessibleNameFromAssociatedLabels(labels, options); diff --git a/tests/library/role-utils.spec.ts b/tests/library/role-utils.spec.ts index 21594e7c06425..81be2a9f163c7 100644 --- a/tests/library/role-utils.spec.ts +++ b/tests/library/role-utils.spec.ts @@ -344,6 +344,21 @@ test('input type=search maps to searchbox unless list points at a datalist', { expect.soft(await getNameAndRole(page, '#search4')).toEqual({ role: 'combobox', name: '' }); }); +test('meter and progress get their name from an associated label', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/41891' }, +}, async ({ page }) => { + await page.setContent(` + + + + + `); + expect.soft(await getNameAndRole(page, '#meter1')).toEqual({ role: 'meter', name: 'Battery' }); + expect.soft(await getNameAndRole(page, '#progress1')).toEqual({ role: 'progressbar', name: 'Loading' }); + expect.soft(await getNameAndRole(page, '#meter2')).toEqual({ role: 'meter', name: 'Charge' }); + expect.soft(await getNameAndRole(page, '#meter3')).toEqual({ role: 'meter', name: 'Overridden' }); +}); + test('native controls labelled-by', async ({ page }) => { await page.setContent(` diff --git a/tests/library/selector-generator.spec.ts b/tests/library/selector-generator.spec.ts index 6d050cacf64bd..a8a233940c9a8 100644 --- a/tests/library/selector-generator.spec.ts +++ b/tests/library/selector-generator.spec.ts @@ -593,7 +593,7 @@ it.describe('selector generator', () => { expect.soft(await generate(page, '#target1')).toBe('internal:role=textbox[name=\"Target1\"i]'); expect.soft(await generate(page, '#target2')).toBe('internal:role=button[name=\"Target2\"i]'); expect.soft(await generate(page, '#target3')).toBe('internal:label=\"Target3\"i'); - expect.soft(await generate(page, '#target4')).toBe('internal:label=\"Target4\"i'); + expect.soft(await generate(page, '#target4')).toBe('internal:role=progressbar[name=\"Target4\"i]'); expect.soft(await generate(page, '#target5')).toBe('#target5'); expect.soft(await generate(page, '#target6')).toBe('internal:text="text"i'); From 56e1b06825775be599b406575f3aba91acb7ab0b Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 22 Jul 2026 16:20:35 +0100 Subject: [PATCH 3/4] test(html-reporter): add story picker to the component gallery (#41936) --- docs/src/test-components-js.md | 4 +- packages/html-reporter/playwright.config.ts | 2 + .../playwright/gallery/index.html | 10 +- .../html-reporter/playwright/gallery/main.tsx | 39 +++- packages/html-reporter/src/DEPS.list | 3 + .../html-reporter/src/headerView.story.tsx | 5 +- packages/html-reporter/src/sampleReport.ts | 170 ++++++++++++++++++ .../html-reporter/src/testCaseView.spec.ts | 8 +- .../html-reporter/src/testCaseView.story.tsx | 131 +------------- .../html-reporter/src/testFileView.spec.ts | 32 ++++ .../html-reporter/src/testFileView.story.tsx | 21 +++ packages/web/playwright.config.ts | 2 + 12 files changed, 288 insertions(+), 139 deletions(-) create mode 100644 packages/html-reporter/src/sampleReport.ts create mode 100644 packages/html-reporter/src/testFileView.spec.ts create mode 100644 packages/html-reporter/src/testFileView.story.tsx diff --git a/docs/src/test-components-js.md b/docs/src/test-components-js.md index 8d411a66ffa84..d95e8ad577808 100644 --- a/docs/src/test-components-js.md +++ b/docs/src/test-components-js.md @@ -219,7 +219,7 @@ This pattern is the heart of the methodology: ### Per-test props -When a scenario is genuinely parametric — a boundary-value sweep, a text matrix — pass plain serializable props as the second argument to `mount`. The gallery hands them to the story as its props: +When a scenario benefits from parameterizing, pass plain serializable props as the second argument to `mount`. The gallery hands them to the story as its props: ```js title="src/components/Button.story.tsx" import { Button } from './Button'; @@ -256,7 +256,7 @@ await expect(await mount('Button/Primary')).toHaveScreenshot('primary.png'); await expect(await mount('Button/Disabled')).toHaveScreenshot('disabled.png'); ``` -Screenshot the returned root locator, not the page, to avoid asserting on browser chrome. +Screenshot the returned root locator, not the page, to avoid asserting on anything extra you might put in the gallery. ### Handling network requests diff --git a/packages/html-reporter/playwright.config.ts b/packages/html-reporter/playwright.config.ts index d15ded9a00656..22b4ac5f4c73d 100644 --- a/packages/html-reporter/playwright.config.ts +++ b/packages/html-reporter/playwright.config.ts @@ -18,6 +18,8 @@ import path from 'path'; import url from 'url'; import { devices, defineConfig } from '@playwright/test'; +process.env.PWTEST_UNDER_TEST = '1'; + const dirname = path.dirname(url.fileURLToPath(import.meta.url)); const outputDir = path.join(dirname, '..', '..', 'test-results'); diff --git a/packages/html-reporter/playwright/gallery/index.html b/packages/html-reporter/playwright/gallery/index.html index 0842528ca8989..0279894a6dfd7 100644 --- a/packages/html-reporter/playwright/gallery/index.html +++ b/packages/html-reporter/playwright/gallery/index.html @@ -22,11 +22,17 @@ Component Gallery -
+ +
diff --git a/packages/html-reporter/playwright/gallery/main.tsx b/packages/html-reporter/playwright/gallery/main.tsx index 38120efe0cc83..467a9ecb2e3e4 100644 --- a/packages/html-reporter/playwright/gallery/main.tsx +++ b/packages/html-reporter/playwright/gallery/main.tsx @@ -17,6 +17,7 @@ import { flushSync } from 'react-dom'; import { createRoot, type Root } from 'react-dom/client'; import '../../src/theme.css'; +import { SearchParamsProvider } from '../../src/links'; const stories = import.meta.glob('../../src/**/*.story.{tsx,jsx}'); const storyId = (file: string) => file.replace(/^(\.\.\/)+src\//, '').replace(/\.story\.\w+$/, ''); @@ -29,7 +30,7 @@ async function resolveStory(id: string): Promise | unde return mod?.[name] ?? mod?.default; } -const rootElement = document.getElementById('root')!; +const wrapperElement = document.getElementById('wrapper')!; let root: Root | undefined; (window as any).mount = async ({ story, props }: { story: string, props?: Record }) => { @@ -37,12 +38,44 @@ let root: Root | undefined; if (!Story) throw new Error(`Unknown story: ${story}`); // Reuse the root so that update() reconciles and preserves state. - root ??= createRoot(rootElement); + root ??= createRoot(wrapperElement); // flushSync so that a render error rejects the promise instead of being swallowed. - flushSync(() => root!.render()); + flushSync(() => root!.render( + +
+ +
+
+ )); }; (window as any).unmount = async () => { root?.unmount(); root = undefined; }; + +async function listStories(): Promise { + const lists = await Promise.all(Object.entries(stories).map(async ([file, loadModule]) => { + const mod = await loadModule() as Record; + return Object.keys(mod).filter(name => typeof mod[name] === 'function').map(name => `${storyId(file)}/${name}`); + })); + return lists.flat().sort(); +} + +const pickerElement = document.getElementById('picker') as HTMLSelectElement; +let pickerPopulated = false; +async function populatePicker() { + if (pickerPopulated) + return; + pickerPopulated = true; + for (const id of await listStories()) + pickerElement.add(new Option(id, id)); +} +// Populate on mouseenter/focus rather than on click, because an already-open +// select popup does not refresh when options are added. +pickerElement.addEventListener('mouseenter', () => void populatePicker()); +pickerElement.addEventListener('focus', () => void populatePicker()); +pickerElement.addEventListener('change', () => { + if (pickerElement.value) + void (window as any).mount({ story: pickerElement.value }); +}); diff --git a/packages/html-reporter/src/DEPS.list b/packages/html-reporter/src/DEPS.list index 87831ba08b5a2..00c60454474a8 100644 --- a/packages/html-reporter/src/DEPS.list +++ b/packages/html-reporter/src/DEPS.list @@ -10,3 +10,6 @@ [testCaseView.spec.ts] *** + +[testFileView.spec.ts] +*** diff --git a/packages/html-reporter/src/headerView.story.tsx b/packages/html-reporter/src/headerView.story.tsx index d4c61c327b7a4..27f58574841f1 100644 --- a/packages/html-reporter/src/headerView.story.tsx +++ b/packages/html-reporter/src/headerView.story.tsx @@ -16,7 +16,6 @@ import * as React from 'react'; import { GlobalFilterView } from './headerView'; -import { SearchParamsProvider } from './links'; const stats = { total: 100, @@ -29,8 +28,8 @@ const stats = { export const Default = () => { const [filterText, setFilterText] = React.useState(''); - return + return <> - ; + ; }; diff --git a/packages/html-reporter/src/sampleReport.ts b/packages/html-reporter/src/sampleReport.ts new file mode 100644 index 0000000000000..322d8168c72b5 --- /dev/null +++ b/packages/html-reporter/src/sampleReport.ts @@ -0,0 +1,170 @@ +/** + * 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 { LoadedReport } from './loadedReport'; +import type { HTMLReport, TestCase, TestCaseSummary, TestFileSummary, TestResult } from './types'; + +const passedResult: TestResult = { + retry: 0, + workerIndex: 0, + startTime: new Date(0).toUTCString(), + duration: 100, + errors: [], + steps: [{ + title: 'Outer step', + startTime: new Date(100).toUTCString(), + duration: 10, + location: { file: 'test.spec.ts', line: 62, column: 0 }, + count: 1, + steps: [{ + title: 'Inner step', + startTime: new Date(200).toUTCString(), + duration: 10, + location: { file: 'test.spec.ts', line: 82, column: 0 }, + steps: [], + attachments: [], + count: 1, + }], + attachments: [], + }], + annotations: [ + { type: 'annotation', description: 'Annotation text' }, + { type: 'annotation', description: 'Another annotation text' }, + { type: '_annotation', description: 'Hidden annotation' }, + ], + attachments: [], + status: 'passed', +}; + +const failedResult: TestResult = { + ...passedResult, + errors: [{ message: 'Error message' }], + status: 'failed', +}; + +export const basicTest: TestCase = { + testId: 'basic-test', + title: 'My test', + path: [], + projectName: 'chromium', + location: { file: 'test.spec.ts', line: 42, column: 0 }, + annotations: passedResult.annotations, + tags: [], + outcome: 'expected', + duration: 200, + ok: true, + results: [passedResult], +}; + +export const annotationLinksTest: TestCase = { + ...basicTest, + testId: 'annotation-links-test', + title: 'Test with annotation links', + duration: 10, + annotations: [], + results: [{ + ...passedResult, + annotations: [ + { type: 'more info', description: 'read https://playwright.dev/docs/intro and https://playwright.dev/docs/api/class-playwright' }, + { type: 'related issues', description: 'https://github.com/microsoft/playwright/issues/23180, https://github.com/microsoft/playwright/issues/23181' }, + ] + }] +}; + +const resultWithAttachments: TestResult = { + ...passedResult, + steps: [{ + title: 'Outer step', + startTime: new Date(100).toUTCString(), + duration: 10, + location: { file: 'test.spec.ts', line: 62, column: 0 }, + count: 1, + steps: [], + attachments: [1], + }], + attachments: [{ + name: 'first attachment', + body: 'The body with https://playwright.dev/docs/intro link and https://github.com/microsoft/playwright/issues/31284.', + contentType: 'text/plain' + }, { + name: 'attachment with inline link https://github.com/microsoft/playwright/issues/31284', + contentType: 'text/plain' + }], + annotations: [], +}; + +export const attachmentLinksTest: TestCase = { + ...basicTest, + testId: 'attachment-links-test', + title: 'Test with attachment links', + path: ['group'], + duration: 10, + annotations: [], + results: [resultWithAttachments] +}; + +export const nextTest: TestCaseSummary = { + ...attachmentLinksTest, + testId: 'next-test', + title: 'next test', + path: [], +}; + +export const twoAttemptsTest: TestCase = { + ...basicTest, + testId: 'two-attempts-test', + title: 'Test with two attempts', + outcome: 'flaky', + results: [ + { ...failedResult, duration: 50 }, + { ...passedResult, duration: 150 }, + ], +}; + +export const webkitTest: TestCase = { + ...basicTest, + testId: 'webkit-test', + title: 'Failing webkit test', + projectName: 'webkit', + outcome: 'unexpected', + ok: false, + annotations: [], + results: [failedResult], +}; + +export const testFile: TestFileSummary = { + fileId: 'file-id', + fileName: 'test.spec.ts', + tests: [basicTest, annotationLinksTest, attachmentLinksTest, nextTest, twoAttemptsTest, webkitTest], + stats: { total: 6, expected: 4, unexpected: 1, flaky: 1, skipped: 0, ok: false }, +}; + +export const report: HTMLReport = { + metadata: {}, + files: [testFile], + stats: testFile.stats, + projectNames: ['chromium', 'webkit'], + startTime: 0, + duration: 200, + machines: [], + errors: [], + options: {}, +}; + +export const loadedReport: LoadedReport = { + json: () => report, + entry: async () => undefined, +}; diff --git a/packages/html-reporter/src/testCaseView.spec.ts b/packages/html-reporter/src/testCaseView.spec.ts index 511c207b2be10..e4f44e7ac4864 100644 --- a/packages/html-reporter/src/testCaseView.spec.ts +++ b/packages/html-reporter/src/testCaseView.spec.ts @@ -96,24 +96,24 @@ test('should correctly render prev and next', async ({ mount }) => { - text: group - link "« previous" - link "next »" - - text: "My test test.spec.ts:42 10ms chromium" + - text: "Test with attachment links test.spec.ts:42 10ms chromium" `); }); test('total duration is selected run duration', async ({ mount, page }) => { const component = await mount('testCaseView/TwoAttempts'); await expect(component).toMatchAriaSnapshot(` - - text: "My test test.spec.ts:42 200ms chromium" + - text: "Test with two attempts test.spec.ts:42 200ms chromium" - tablist: - tab "Run 50ms" - 'tab "Retry #1 150ms"' `); await page.getByRole('tab', { name: 'Run' }).click(); await expect(component).toMatchAriaSnapshot(` - - text: "My test test.spec.ts:42 200ms chromium" + - text: "Test with two attempts test.spec.ts:42 200ms chromium" `); await page.getByRole('tab', { name: 'Retry' }).click(); await expect(component).toMatchAriaSnapshot(` - - text: "My test test.spec.ts:42 200ms chromium" + - text: "Test with two attempts test.spec.ts:42 200ms chromium" `); }); diff --git a/packages/html-reporter/src/testCaseView.story.tsx b/packages/html-reporter/src/testCaseView.story.tsx index ba2f9066c5d9a..cda50d19cd735 100644 --- a/packages/html-reporter/src/testCaseView.story.tsx +++ b/packages/html-reporter/src/testCaseView.story.tsx @@ -14,139 +14,20 @@ * limitations under the License. */ -import type { LoadedReport } from './loadedReport'; +import { annotationLinksTest, attachmentLinksTest, basicTest, loadedReport, nextTest, twoAttemptsTest } from './sampleReport'; import { TestCaseView } from './testCaseView'; -import type { HTMLReport, TestCase, TestCaseSummary, TestResult } from './types'; - -const report: LoadedReport = { - json: () => ({ projectNames: ['chromium', 'webkit'] } as HTMLReport), - entry: async () => undefined, -}; - -const result: TestResult = { - retry: 0, - workerIndex: 0, - startTime: new Date(0).toUTCString(), - duration: 100, - errors: [], - steps: [{ - title: 'Outer step', - startTime: new Date(100).toUTCString(), - duration: 10, - location: { file: 'test.spec.ts', line: 62, column: 0 }, - count: 1, - steps: [{ - title: 'Inner step', - startTime: new Date(200).toUTCString(), - duration: 10, - location: { file: 'test.spec.ts', line: 82, column: 0 }, - steps: [], - attachments: [], - count: 1, - }], - attachments: [], - }], - annotations: [ - { type: 'annotation', description: 'Annotation text' }, - { type: 'annotation', description: 'Another annotation text' }, - { type: '_annotation', description: 'Hidden annotation' }, - ], - attachments: [], - status: 'passed', -}; - -const testCase: TestCase = { - testId: 'testid', - title: 'My test', - path: [], - projectName: 'chromium', - location: { file: 'test.spec.ts', line: 42, column: 0 }, - annotations: result.annotations, - tags: [], - outcome: 'expected', - duration: 200, - ok: true, - results: [result] -}; - -const annotationLinkRenderingTestCase: TestCase = { - ...testCase, - duration: 10, - annotations: [], - results: [{ - ...result, - annotations: [ - { type: 'more info', description: 'read https://playwright.dev/docs/intro and https://playwright.dev/docs/api/class-playwright' }, - { type: 'related issues', description: 'https://github.com/microsoft/playwright/issues/23180, https://github.com/microsoft/playwright/issues/23181' }, - ] - }] -}; - -const resultWithAttachment: TestResult = { - ...result, - steps: [{ - title: 'Outer step', - startTime: new Date(100).toUTCString(), - duration: 10, - location: { file: 'test.spec.ts', line: 62, column: 0 }, - count: 1, - steps: [], - attachments: [1], - }], - attachments: [{ - name: 'first attachment', - body: 'The body with https://playwright.dev/docs/intro link and https://github.com/microsoft/playwright/issues/31284.', - contentType: 'text/plain' - }, { - name: 'attachment with inline link https://github.com/microsoft/playwright/issues/31284', - contentType: 'text/plain' - }], - annotations: [], -}; - -const attachmentLinkRenderingTestCase: TestCase = { - ...testCase, - path: ['group'], - duration: 10, - annotations: [], - results: [resultWithAttachment] -}; - -const testCaseSummary: TestCaseSummary = { - ...attachmentLinkRenderingTestCase, - testId: 'nextTestId', - title: 'next test', - path: [], -}; - -const testCaseWithTwoAttempts: TestCase = { - ...testCase, - results: [ - { - ...result, - errors: [{ message: 'Error message' }], - status: 'failed', - duration: 50, - }, - { - ...result, - duration: 150, - status: 'passed', - }, - ], -}; export const Default = () => - ; + ; export const AnnotationLinks = () => - ; + ; export const AttachmentLinks = () => - ; + ; export const PrevNext = () => - ; + ; export const TwoAttempts = () => - ; + ; diff --git a/packages/html-reporter/src/testFileView.spec.ts b/packages/html-reporter/src/testFileView.spec.ts new file mode 100644 index 0000000000000..55e9d88439107 --- /dev/null +++ b/packages/html-reporter/src/testFileView.spec.ts @@ -0,0 +1,32 @@ +/** + * 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 { expect, test } from '@playwright/test'; + +import type { Default } from './testFileView.story'; + +test.use({ viewport: { width: 800, height: 600 } }); + +test('should render project links', async ({ mount, page }) => { + const component = await mount('testFileView/Default'); + await expect(component.locator('.label', { hasText: 'chromium' })).toHaveCount(5); + await expect(component.locator('.label', { hasText: 'webkit' })).toHaveCount(1); + const webkitLabel = component.locator('.label', { hasText: 'webkit' }); + await webkitLabel.click(); + await expect(page).toHaveURL(/p(:|%3A)webkit/); + await webkitLabel.click({ modifiers: ['ControlOrMeta'] }); + await expect(page).not.toHaveURL(/webkit/); +}); diff --git a/packages/html-reporter/src/testFileView.story.tsx b/packages/html-reporter/src/testFileView.story.tsx new file mode 100644 index 0000000000000..4c973c0ac902c --- /dev/null +++ b/packages/html-reporter/src/testFileView.story.tsx @@ -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 { report, testFile } from './sampleReport'; +import { TestFileView } from './testFileView'; + +export const Default = () => + ; diff --git a/packages/web/playwright.config.ts b/packages/web/playwright.config.ts index b8eb76a2f868a..85dd14a184715 100644 --- a/packages/web/playwright.config.ts +++ b/packages/web/playwright.config.ts @@ -17,6 +17,8 @@ import path from 'path'; import { devices, defineConfig } from '@playwright/test'; +process.env.PWTEST_UNDER_TEST = '1'; + const outputDir = path.join(__dirname, '..', '..', 'test-results'); export default defineConfig({ From ac877bc190ec28604e7bd9cbccaf8044a1f0c39f Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Wed, 22 Jul 2026 19:03:45 +0200 Subject: [PATCH 4/4] test(evaluate): skip using declaration on WebKit (#41929) --- tests/page/page-evaluate.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/page/page-evaluate.spec.ts b/tests/page/page-evaluate.spec.ts index 5b85b8dd665a5..fa45692fc6138 100644 --- a/tests/page/page-evaluate.spec.ts +++ b/tests/page/page-evaluate.spec.ts @@ -867,9 +867,10 @@ it('should work with Array.from/map', async ({ page }) => { })).toBe('([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})'); }); -it('should work with a using declaration', async ({ page, nodeVersion }) => { +it('should work with a using declaration', async ({ page, nodeVersion, browserName }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/41511' }); it.skip(nodeVersion.major < 24, 'using is lowered to a module-scope helper that does not survive evaluate serialization on Node < 24'); + it.skip(browserName === 'webkit', 'WebKit does not support using declarations'); const disposed = await page.evaluate(() => { let disposed = false; {