mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: fix the test view mobile layout (#23061)
Fixes https://github.com/microsoft/playwright/issues/23036
This commit is contained in:
parent
62146b946c
commit
37b2853b7b
@ -21,7 +21,6 @@
|
|||||||
background-color: var(--color-canvas-subtle);
|
background-color: var(--color-canvas-subtle);
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
margin-top: 24px;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 38px;
|
line-height: 38px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@ -50,7 +50,6 @@ test('should toggle filters', async ({ page, mount }) => {
|
|||||||
}}
|
}}
|
||||||
filterText=''
|
filterText=''
|
||||||
setFilterText={(filterText: string) => filters.push(filterText)}
|
setFilterText={(filterText: string) => filters.push(filterText)}
|
||||||
projectNames={[]}
|
|
||||||
>
|
>
|
||||||
</HeaderView>);
|
</HeaderView>);
|
||||||
await component.locator('a', { hasText: 'All' }).click();
|
await component.locator('a', { hasText: 'All' }).click();
|
||||||
@ -64,52 +63,3 @@ test('should toggle filters', async ({ page, mount }) => {
|
|||||||
await expect(page).toHaveURL(/#\?q=s:skipped/);
|
await expect(page).toHaveURL(/#\?q=s:skipped/);
|
||||||
expect(filters).toEqual(['', 's:passed', 's:failed', 's:flaky', 's:skipped']);
|
expect(filters).toEqual(['', 's:passed', 's:failed', 's:flaky', 's:skipped']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should show the project names', async ({ mount }) => {
|
|
||||||
const stats = {
|
|
||||||
total: 100,
|
|
||||||
expected: 42,
|
|
||||||
unexpected: 31,
|
|
||||||
flaky: 17,
|
|
||||||
skipped: 10,
|
|
||||||
ok: false,
|
|
||||||
duration: 100000
|
|
||||||
};
|
|
||||||
await test.step('with 1 project', async () => {
|
|
||||||
const component = await mount(<HeaderView
|
|
||||||
stats={stats}
|
|
||||||
filterText=''
|
|
||||||
setFilterText={() => {}}
|
|
||||||
projectNames={['my-project']}
|
|
||||||
>
|
|
||||||
</HeaderView>);
|
|
||||||
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(<HeaderView
|
|
||||||
stats={stats}
|
|
||||||
filterText=''
|
|
||||||
setFilterText={() => {}}
|
|
||||||
projectNames={['']}
|
|
||||||
>
|
|
||||||
</HeaderView>);
|
|
||||||
await expect(component.getByText('Project:')).toBeHidden();
|
|
||||||
|
|
||||||
await component.unmount();
|
|
||||||
});
|
|
||||||
await test.step('with more than 1 project', async () => {
|
|
||||||
const component = await mount(<HeaderView
|
|
||||||
stats={stats}
|
|
||||||
filterText=''
|
|
||||||
setFilterText={() => {}}
|
|
||||||
projectNames={['great-project', 'my-project']}
|
|
||||||
>
|
|
||||||
</HeaderView>);
|
|
||||||
await expect(component.getByText('my-project')).toBeHidden();
|
|
||||||
await expect(component.getByText('great-project')).toBeHidden();
|
|
||||||
|
|
||||||
await component.unmount();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@ -22,14 +22,12 @@ import './headerView.css';
|
|||||||
import * as icons from './icons';
|
import * as icons from './icons';
|
||||||
import { Link, navigate } from './links';
|
import { Link, navigate } from './links';
|
||||||
import { statusIcon } from './statusIcon';
|
import { statusIcon } from './statusIcon';
|
||||||
import { msToString } from './uiUtils';
|
|
||||||
|
|
||||||
export const HeaderView: React.FC<React.PropsWithChildren<{
|
export const HeaderView: React.FC<React.PropsWithChildren<{
|
||||||
stats: Stats,
|
stats: Stats,
|
||||||
filterText: string,
|
filterText: string,
|
||||||
setFilterText: (filterText: string) => void,
|
setFilterText: (filterText: string) => void,
|
||||||
projectNames: string[],
|
}>> = ({ stats, filterText, setFilterText }) => {
|
||||||
}>> = ({ stats, filterText, setFilterText, projectNames }) => {
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const popstateFn = () => {
|
const popstateFn = () => {
|
||||||
const params = new URLSearchParams(window.location.hash.slice(1));
|
const params = new URLSearchParams(window.location.hash.slice(1));
|
||||||
@ -60,10 +58,6 @@ export const HeaderView: React.FC<React.PropsWithChildren<{
|
|||||||
}}></input>
|
}}></input>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div className='pt-2'>
|
|
||||||
{projectNames.length === 1 && !!projectNames[0] && <span data-testid="project-name" style={{ color: 'var(--color-fg-subtle)', float: 'left' }}>Project: {projectNames[0]}</span>}
|
|
||||||
<span data-testid="overall-duration" style={{ color: 'var(--color-fg-subtle)', paddingRight: '10px', float: 'right' }}>Total time: {msToString(stats.duration)}</span>
|
|
||||||
</div>
|
|
||||||
</>);
|
</>);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -55,7 +55,7 @@ test('should switch to actual', async ({ mount }) => {
|
|||||||
for (let i = 0; i < imageCount; ++i) {
|
for (let i = 0; i < imageCount; ++i) {
|
||||||
const image = images.nth(i);
|
const image = images.nth(i);
|
||||||
const box = await image.boundingBox();
|
const box = await image.boundingBox();
|
||||||
expect(box).toEqual({ x: 400, y: 108, width: 200, height: 200 });
|
expect(box).toEqual({ x: 400, y: 124, width: 200, height: 200 });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ test('should switch to expected', async ({ mount }) => {
|
|||||||
for (let i = 0; i < imageCount; ++i) {
|
for (let i = 0; i < imageCount; ++i) {
|
||||||
const image = images.nth(i);
|
const image = images.nth(i);
|
||||||
const box = await image.boundingBox();
|
const box = await image.boundingBox();
|
||||||
expect(box).toEqual({ x: 400, y: 108, width: 200, height: 200 });
|
expect(box).toEqual({ x: 400, y: 124, width: 200, height: 200 });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -79,5 +79,5 @@ test('should show diff by default', async ({ mount }) => {
|
|||||||
|
|
||||||
const image = component.locator('img');
|
const image = component.locator('img');
|
||||||
const box = await image.boundingBox();
|
const box = await image.boundingBox();
|
||||||
expect(box).toEqual({ x: 400, y: 108, width: 200, height: 200 });
|
expect(box).toEqual({ x: 400, y: 124, width: 200, height: 200 });
|
||||||
});
|
});
|
||||||
|
|||||||
@ -50,10 +50,17 @@ export const ReportView: React.FC<{
|
|||||||
|
|
||||||
return <div className='htmlreport vbox px-4 pb-4'>
|
return <div className='htmlreport vbox px-4 pb-4'>
|
||||||
<main>
|
<main>
|
||||||
{report?.json() && <HeaderView stats={report.json().stats} filterText={filterText} setFilterText={setFilterText} projectNames={report.json().projectNames}></HeaderView>}
|
{report?.json() && <HeaderView stats={report.json().stats} filterText={filterText} setFilterText={setFilterText}></HeaderView>}
|
||||||
{report?.json().metadata && <MetadataView {...report?.json().metadata as Metainfo} />}
|
{report?.json().metadata && <MetadataView {...report?.json().metadata as Metainfo} />}
|
||||||
<Route predicate={testFilesRoutePredicate}>
|
<Route predicate={testFilesRoutePredicate}>
|
||||||
<TestFilesView report={report?.json()} filter={filter} expandedFiles={expandedFiles} setExpandedFiles={setExpandedFiles}></TestFilesView>
|
<TestFilesView
|
||||||
|
report={report?.json()}
|
||||||
|
filter={filter}
|
||||||
|
expandedFiles={expandedFiles}
|
||||||
|
setExpandedFiles={setExpandedFiles}
|
||||||
|
projectNames={report?.json().projectNames || []}
|
||||||
|
stats={report?.json().stats || { duration: 0 }}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route predicate={testCaseRoutePredicate}>
|
<Route predicate={testCaseRoutePredicate}>
|
||||||
{!!report && <TestCaseViewLoader report={report}></TestCaseViewLoader>}
|
{!!report && <TestCaseViewLoader report={report}></TestCaseViewLoader>}
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
height: 48px;
|
height: 48px;
|
||||||
min-width: 70px;
|
min-width: 70px;
|
||||||
box-shadow: inset 0 -1px 0 var(--color-border-muted) !important;
|
box-shadow: inset 0 -1px 0 var(--color-border-muted) !important;
|
||||||
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbed-pane-tab-strip:focus {
|
.tabbed-pane-tab-strip:focus {
|
||||||
|
|||||||
@ -42,7 +42,8 @@
|
|||||||
line-height: 1.25 !important;
|
line-height: 1.25 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.test-case-location {
|
.test-case-location,
|
||||||
|
.test-case-duration {
|
||||||
flex: none;
|
flex: none;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 8px 24px;
|
padding: 0 8px 24px;
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import { statusIcon } from './statusIcon';
|
|||||||
import './testCaseView.css';
|
import './testCaseView.css';
|
||||||
import { TestResultView } from './testResultView';
|
import { TestResultView } from './testResultView';
|
||||||
import { hashStringToInt, matchTags } from './labelUtils';
|
import { hashStringToInt, matchTags } from './labelUtils';
|
||||||
|
import { msToString } from './uiUtils';
|
||||||
|
|
||||||
export const TestCaseView: React.FC<{
|
export const TestCaseView: React.FC<{
|
||||||
projectNames: string[],
|
projectNames: string[],
|
||||||
@ -42,7 +43,11 @@ export const TestCaseView: React.FC<{
|
|||||||
return <div className='test-case-column vbox'>
|
return <div className='test-case-column vbox'>
|
||||||
{test && <div className='test-case-path'>{test.path.join(' › ')}</div>}
|
{test && <div className='test-case-path'>{test.path.join(' › ')}</div>}
|
||||||
{test && <div className='test-case-title'>{test?.title}</div>}
|
{test && <div className='test-case-title'>{test?.title}</div>}
|
||||||
{test && <div className='test-case-location'>{test.location.file}:{test.location.line}</div>}
|
{test && <div className='hbox'>
|
||||||
|
<div className='test-case-location'>{test.location.file}:{test.location.line}</div>
|
||||||
|
<div style={{ flex: 'auto' }}></div>
|
||||||
|
<div className='test-case-duration'>{msToString(test.duration)}</div>
|
||||||
|
</div>}
|
||||||
{test && (!!test.projectName || labels) && <div className='test-case-project-labels-row'>
|
{test && (!!test.projectName || labels) && <div className='test-case-project-labels-row'>
|
||||||
{test && !!test.projectName && <ProjectLink projectNames={projectNames} projectName={test.projectName}></ProjectLink>}
|
{test && !!test.projectName && <ProjectLink projectNames={projectNames} projectName={test.projectName}></ProjectLink>}
|
||||||
{labels && <LabelsLinkView labels={labels} />}
|
{labels && <LabelsLinkView labels={labels} />}
|
||||||
|
|||||||
@ -19,13 +19,16 @@ import * as React from 'react';
|
|||||||
import type { Filter } from './filter';
|
import type { Filter } from './filter';
|
||||||
import { TestFileView } from './testFileView';
|
import { TestFileView } from './testFileView';
|
||||||
import './testFileView.css';
|
import './testFileView.css';
|
||||||
|
import { msToString } from './uiUtils';
|
||||||
|
|
||||||
export const TestFilesView: React.FC<{
|
export const TestFilesView: React.FC<{
|
||||||
report?: HTMLReport,
|
report?: HTMLReport,
|
||||||
expandedFiles: Map<string, boolean>,
|
expandedFiles: Map<string, boolean>,
|
||||||
setExpandedFiles: (value: Map<string, boolean>) => void,
|
setExpandedFiles: (value: Map<string, boolean>) => void,
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
}> = ({ report, filter, expandedFiles, setExpandedFiles }) => {
|
stats: { duration: number },
|
||||||
|
projectNames: string[],
|
||||||
|
}> = ({ report, filter, expandedFiles, setExpandedFiles, projectNames, stats }) => {
|
||||||
const filteredFiles = React.useMemo(() => {
|
const filteredFiles = React.useMemo(() => {
|
||||||
const result: { file: TestFileSummary, defaultExpanded: boolean }[] = [];
|
const result: { file: TestFileSummary, defaultExpanded: boolean }[] = [];
|
||||||
let visibleTests = 0;
|
let visibleTests = 0;
|
||||||
@ -38,6 +41,11 @@ export const TestFilesView: React.FC<{
|
|||||||
return result;
|
return result;
|
||||||
}, [report, filter]);
|
}, [report, filter]);
|
||||||
return <>
|
return <>
|
||||||
|
<div className='p-2' style={{ display: 'flex' }}>
|
||||||
|
{projectNames.length === 1 && !!projectNames[0] && <div data-testid="project-name" style={{ color: 'var(--color-fg-subtle)' }}>Project: {projectNames[0]}</div>}
|
||||||
|
<div style={{ flex: 'auto' }}></div>
|
||||||
|
<div data-testid="overall-duration" style={{ color: 'var(--color-fg-subtle)' }}>Total time: {msToString(stats.duration)}</div>
|
||||||
|
</div>
|
||||||
{report && filteredFiles.map(({ file, defaultExpanded }) => {
|
{report && filteredFiles.map(({ file, defaultExpanded }) => {
|
||||||
return <TestFileView
|
return <TestFileView
|
||||||
key={`file-${file.fileId}`}
|
key={`file-${file.fileId}`}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user