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);
 | 
			
		||||
  padding: 0 8px;
 | 
			
		||||
  border-bottom: none;
 | 
			
		||||
  margin-top: 24px;
 | 
			
		||||
  font-weight: 600;
 | 
			
		||||
  line-height: 38px;
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,6 @@ test('should toggle filters', async ({ page, mount }) => {
 | 
			
		||||
    }}
 | 
			
		||||
    filterText=''
 | 
			
		||||
    setFilterText={(filterText: string) => filters.push(filterText)}
 | 
			
		||||
    projectNames={[]}
 | 
			
		||||
  >
 | 
			
		||||
  </HeaderView>);
 | 
			
		||||
  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/);
 | 
			
		||||
  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 { Link, navigate } from './links';
 | 
			
		||||
import { statusIcon } from './statusIcon';
 | 
			
		||||
import { msToString } from './uiUtils';
 | 
			
		||||
 | 
			
		||||
export const HeaderView: React.FC<React.PropsWithChildren<{
 | 
			
		||||
  stats: Stats,
 | 
			
		||||
  filterText: string,
 | 
			
		||||
  setFilterText: (filterText: string) => void,
 | 
			
		||||
  projectNames: string[],
 | 
			
		||||
}>> = ({ stats, filterText, setFilterText, projectNames }) => {
 | 
			
		||||
}>> = ({ stats, filterText, setFilterText }) => {
 | 
			
		||||
  React.useEffect(() => {
 | 
			
		||||
    const popstateFn = () => {
 | 
			
		||||
      const params = new URLSearchParams(window.location.hash.slice(1));
 | 
			
		||||
@ -60,10 +58,6 @@ export const HeaderView: React.FC<React.PropsWithChildren<{
 | 
			
		||||
        }}></input>
 | 
			
		||||
      </form>
 | 
			
		||||
    </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) {
 | 
			
		||||
    const image = images.nth(i);
 | 
			
		||||
    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) {
 | 
			
		||||
    const image = images.nth(i);
 | 
			
		||||
    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 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'>
 | 
			
		||||
    <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} />}
 | 
			
		||||
      <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 predicate={testCaseRoutePredicate}>
 | 
			
		||||
        {!!report && <TestCaseViewLoader report={report}></TestCaseViewLoader>}
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,7 @@
 | 
			
		||||
  height: 48px;
 | 
			
		||||
  min-width: 70px;
 | 
			
		||||
  box-shadow: inset 0 -1px 0 var(--color-border-muted) !important;
 | 
			
		||||
  margin-bottom: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tabbed-pane-tab-strip:focus {
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,8 @@
 | 
			
		||||
  line-height: 1.25 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.test-case-location {
 | 
			
		||||
.test-case-location,
 | 
			
		||||
.test-case-duration {
 | 
			
		||||
  flex: none;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  padding: 0 8px 24px;
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ import { statusIcon } from './statusIcon';
 | 
			
		||||
import './testCaseView.css';
 | 
			
		||||
import { TestResultView } from './testResultView';
 | 
			
		||||
import { hashStringToInt, matchTags } from './labelUtils';
 | 
			
		||||
import { msToString } from './uiUtils';
 | 
			
		||||
 | 
			
		||||
export const TestCaseView: React.FC<{
 | 
			
		||||
  projectNames: string[],
 | 
			
		||||
@ -42,7 +43,11 @@ export const TestCaseView: React.FC<{
 | 
			
		||||
  return <div className='test-case-column vbox'>
 | 
			
		||||
    {test && <div className='test-case-path'>{test.path.join(' › ')}</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 && <ProjectLink projectNames={projectNames} projectName={test.projectName}></ProjectLink>}
 | 
			
		||||
      {labels && <LabelsLinkView labels={labels} />}
 | 
			
		||||
 | 
			
		||||
@ -19,13 +19,16 @@ import * as React from 'react';
 | 
			
		||||
import type { Filter } from './filter';
 | 
			
		||||
import { TestFileView } from './testFileView';
 | 
			
		||||
import './testFileView.css';
 | 
			
		||||
import { msToString } from './uiUtils';
 | 
			
		||||
 | 
			
		||||
export const TestFilesView: React.FC<{
 | 
			
		||||
  report?: HTMLReport,
 | 
			
		||||
  expandedFiles: Map<string, boolean>,
 | 
			
		||||
  setExpandedFiles: (value: Map<string, boolean>) => void,
 | 
			
		||||
  filter: Filter,
 | 
			
		||||
}> = ({ report, filter, expandedFiles, setExpandedFiles }) => {
 | 
			
		||||
  stats: { duration: number },
 | 
			
		||||
  projectNames: string[],
 | 
			
		||||
}> = ({ report, filter, expandedFiles, setExpandedFiles, projectNames, stats }) => {
 | 
			
		||||
  const filteredFiles = React.useMemo(() => {
 | 
			
		||||
    const result: { file: TestFileSummary, defaultExpanded: boolean }[] = [];
 | 
			
		||||
    let visibleTests = 0;
 | 
			
		||||
@ -38,6 +41,11 @@ export const TestFilesView: React.FC<{
 | 
			
		||||
    return result;
 | 
			
		||||
  }, [report, filter]);
 | 
			
		||||
  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 }) => {
 | 
			
		||||
      return <TestFileView
 | 
			
		||||
        key={`file-${file.fileId}`}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user