diff --git a/packages/html-reporter/src/checkbox.css b/packages/html-reporter/src/checkbox.css new file mode 100644 index 0000000000000..2f935069a5733 --- /dev/null +++ b/packages/html-reporter/src/checkbox.css @@ -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. +*/ +.setting { + line-height: 38px; + padding-left: 9px; + } + + .default { + margin-right: 7px; + } + \ No newline at end of file diff --git a/packages/html-reporter/src/checkbox.tsx b/packages/html-reporter/src/checkbox.tsx new file mode 100644 index 0000000000000..432e8f09b4037 --- /dev/null +++ b/packages/html-reporter/src/checkbox.tsx @@ -0,0 +1,47 @@ +/* + 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 * as React from 'react'; +import './checkbox.css'; + +export type Check = { + value: T; + set: (value: T) => void; + name: string; + title?: string; +}; + +export const CheckBox: React.FunctionComponent<{ + checkBoxSettings: Check[]; + }> = ({ checkBoxSettings }) => { + return ( + <> + {checkBoxSettings.map(({ value, set, name, title }) => { + const labelId = `checkBoxSetting-${name}`; + + return ( +
+ set(!value)} + /> + +
+ ); + })} + + ); + }; diff --git a/packages/html-reporter/src/testResultView.tsx b/packages/html-reporter/src/testResultView.tsx index 060a1aaba4f01..10eabe3f5f7ff 100644 --- a/packages/html-reporter/src/testResultView.tsx +++ b/packages/html-reporter/src/testResultView.tsx @@ -27,6 +27,7 @@ import { ImageDiffView } from '@web/shared/imageDiffView'; import { CodeSnippet, TestErrorView, TestScreenshotErrorView } from './testErrorView'; import * as icons from './icons'; import './testResultView.css'; +import { CheckBox } from './checkbox'; interface ImageDiffWithAnchors extends ImageDiff { anchors: string[]; @@ -71,6 +72,7 @@ export const TestResultView: React.FC<{ test: TestCase, result: TestResult, }> = ({ test, result }) => { + const [showSnippets, setShowSnippets] = React.useState(true); const { screenshots, videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors } = React.useMemo(() => { const attachments = result.attachments.filter(a => !a.name.startsWith('_')); const screenshots = new Set(attachments.filter(a => a.contentType.startsWith('image/'))); @@ -94,7 +96,8 @@ export const TestResultView: React.FC<{ })} } {!!result.steps.length && - {result.steps.map((step, i) => )} + + {result.steps.map((step, i) => )} } {diffs.map((diff, index) => @@ -175,7 +178,8 @@ const StepTreeItem: React.FC<{ result: TestResult; step: TestStep; depth: number, -}> = ({ test, step, result, depth }) => { + showSnippets: boolean, +}> = ({ test, step, result, depth, showSnippets }) => { return {msToString(step.duration)} {step.attachments.length > 0 && { evt.stopPropagation(); }}>{icons.attachment()}} @@ -183,9 +187,9 @@ const StepTreeItem: React.FC<{ {step.title} {step.count > 1 && <> ✕ {step.count}} {step.location && — {step.location.file}:{step.location.line}} - } loadChildren={step.steps.length || step.snippet ? () => { - const snippet = step.snippet ? [] : []; - const steps = step.steps.map((s, i) => ); + } loadChildren={step.steps.length || (step.snippet && showSnippets) ? () => { + const snippet = (step.snippet && showSnippets) ? [] : []; + const steps = step.steps.map((s, i) => ); return snippet.concat(steps); } : undefined} depth={depth}/>; }; diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index 727cbe4077547..4791d40551066 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -2790,6 +2790,69 @@ for (const useIntermediateMergeReport of [true, false] as const) { await page.getByRole('link', { name: 'sample' }).click(); await expect(page.getByRole('button', { name: 'Copy prompt' })).toHaveCount(1); }); + + test('should toggle code snippet visibility', async ({ runInlineTest, showReport, page }) => { + await runInlineTest({ + 'a.test.js': ` + const { test, expect } = require('@playwright/test'); + + test('test with multiple steps and snippets', async ({ page }) => { + console.log('Test started'); // First snippet + + await test.step('Step 1: Navigate to example page', async () => { + await page.goto('https://example.com'); + console.log('Navigated to example.com'); // Second snippet + }); + + await test.step('Step 2: Check page title', async () => { + const title = await page.title(); + console.log('Page title is:', title); // Third snippet + expect(title).toBe('Example Domain'); // Fourth snippet + }); + + await test.step('Step 3: Find and click a link', async () => { + const link = page.locator('a'); + await expect(link).toBeVisible(); + console.log('Link is visible'); // Fifth snippet + await link.click(); + console.log('Clicked the link'); // Sixth snippet + }); + + console.log('Test finished'); // Seventh snippet + }); + `, + }, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' }); + + + await showReport(); + + const testLink = page.locator('a[title*="test with multiple steps and snippets"]').nth(0); + await expect(testLink).toBeVisible(); + await testLink.click(); + + await expect(page).toHaveURL(/#\?testId=/); + const toggleCheckbox = page.locator('#checkBoxSetting-Show\\ Snippets'); + await expect(toggleCheckbox).toBeVisible(); + + const secondTreeItem = page.locator('.tree-item-title').nth(1); + await expect(secondTreeItem).toBeVisible(); + await secondTreeItem.click(); + + const thirdTreeItem = page.locator('.tree-item-title').nth(2); + await expect(thirdTreeItem).toBeVisible(); + await thirdTreeItem.click(); + + const codeSnippets = page.locator('[data-testid="test-snippet"]'); + await expect(codeSnippets.first()).toBeVisible(); + + // click checkbox to hide snippets + await toggleCheckbox.click(); + await expect(codeSnippets.first()).not.toBeVisible(); + + // click checkbox again to show code snippets + await toggleCheckbox.click(); + await expect(codeSnippets.first()).toBeVisible(); + }); }); }