chore(html-reporter) rework setting favicon and page title
This commit is contained in:
parent
863ae531fd
commit
fefea60857
|
|
@ -21,7 +21,6 @@
|
||||||
<meta name='color-scheme' content='dark light'>
|
<meta name='color-scheme' content='dark light'>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
||||||
<title>Playwright Test Report | Summary</title>
|
<title>Playwright Test Report | Summary</title>
|
||||||
<link rel="icon" href="/logo_default.svg">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id='root'></div>
|
<div id='root'></div>
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import * as icons from './icons';
|
||||||
import { Link, navigate } from './links';
|
import { Link, navigate } from './links';
|
||||||
import { statusIcon } from './statusIcon';
|
import { statusIcon } from './statusIcon';
|
||||||
import { filterWithToken } from './filter';
|
import { filterWithToken } from './filter';
|
||||||
|
import { setDefaultFavIconAndTitle } from './uiUtils';
|
||||||
|
|
||||||
export const HeaderView: React.FC<React.PropsWithChildren<{
|
export const HeaderView: React.FC<React.PropsWithChildren<{
|
||||||
stats: Stats,
|
stats: Stats,
|
||||||
|
|
@ -33,6 +34,7 @@ export const HeaderView: React.FC<React.PropsWithChildren<{
|
||||||
const popstateFn = () => {
|
const popstateFn = () => {
|
||||||
const params = new URLSearchParams(window.location.hash.slice(1));
|
const params = new URLSearchParams(window.location.hash.slice(1));
|
||||||
setFilterText(params.get('q') || '');
|
setFilterText(params.get('q') || '');
|
||||||
|
setDefaultFavIconAndTitle();
|
||||||
};
|
};
|
||||||
window.addEventListener('popstate', popstateFn);
|
window.addEventListener('popstate', popstateFn);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import * as ReactDOM from 'react-dom';
|
||||||
import './colors.css';
|
import './colors.css';
|
||||||
import type { LoadedReport } from './loadedReport';
|
import type { LoadedReport } from './loadedReport';
|
||||||
import { ReportView } from './reportView';
|
import { ReportView } from './reportView';
|
||||||
|
import { setDefaultFavIconAndTitle } from './uiUtils';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const zipjs = zipImport as typeof zip;
|
const zipjs = zipImport as typeof zip;
|
||||||
|
|
||||||
|
|
@ -34,6 +35,10 @@ const ReportLoader: React.FC = () => {
|
||||||
const zipReport = new ZipReport();
|
const zipReport = new ZipReport();
|
||||||
zipReport.load().then(() => setReport(zipReport));
|
zipReport.load().then(() => setReport(zipReport));
|
||||||
}, [report]);
|
}, [report]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setDefaultFavIconAndTitle();
|
||||||
|
}, []);
|
||||||
return <ReportView report={report}></ReportView>;
|
return <ReportView report={report}></ReportView>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,14 +79,14 @@ test('should render test case', async ({ mount }) => {
|
||||||
|
|
||||||
test('should correctly set the page title and favicon', async ({ mount, page }) => {
|
test('should correctly set the page title and favicon', async ({ mount, page }) => {
|
||||||
await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={testCase} run={0} anchor=''></TestCaseView>);
|
await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={testCase} run={0} anchor=''></TestCaseView>);
|
||||||
expect(await page.locator('link[rel="icon"]').getAttribute('href')).toEqual('../logo_expected.svg');
|
expect(await page.locator('link[rel="icon"]').getAttribute('href')).toMatch(/\/assets\/logo_expected-[A-Za-z0-9]+\.svg/);
|
||||||
expect(await page.title()).toEqual(`| ${testCase.title}`);
|
expect(await page.title()).toEqual(`| ${testCase.title}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should fallback to default title and favicon', async ({ mount, page }) => {
|
test('should fallback to default title and favicon', async ({ mount, page }) => {
|
||||||
await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={undefined} run={0} anchor=''></TestCaseView>);
|
await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={undefined} run={0} anchor=''></TestCaseView>);
|
||||||
expect(await page.locator('link[rel="icon"]').getAttribute('href')).toEqual('../logo_default.svg');
|
expect(await page.locator('link[rel="icon"]').getAttribute('href')).toMatch(/\/assets\/logo_default-[A-Za-z0-9]+\.svg/);
|
||||||
expect(await page.title()).toEqual('Playwright Test Report');
|
expect(await page.title()).toEqual('Playwright Test Report | Summary');
|
||||||
});
|
});
|
||||||
|
|
||||||
const linkRenderingTestCase: TestCase = {
|
const linkRenderingTestCase: TestCase = {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { TestCase, TestCaseAnnotation, TestCaseSummary } from './types';
|
import type { TestCase, TestCaseAnnotation } from './types';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { TabbedPane } from './tabbedPane';
|
import { TabbedPane } from './tabbedPane';
|
||||||
import { AutoChip } from './chip';
|
import { AutoChip } from './chip';
|
||||||
|
|
@ -24,7 +24,7 @@ import { statusIcon } from './statusIcon';
|
||||||
import './testCaseView.css';
|
import './testCaseView.css';
|
||||||
import { TestResultView } from './testResultView';
|
import { TestResultView } from './testResultView';
|
||||||
import { hashStringToInt } from './labelUtils';
|
import { hashStringToInt } from './labelUtils';
|
||||||
import { msToString } from './uiUtils';
|
import { msToString, setDefaultFavIconAndTitle, setFavIcon } from './uiUtils';
|
||||||
|
|
||||||
export const TestCaseView: React.FC<{
|
export const TestCaseView: React.FC<{
|
||||||
projectNames: string[],
|
projectNames: string[],
|
||||||
|
|
@ -46,9 +46,9 @@ export const TestCaseView: React.FC<{
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
document.title = test?.title ? `| ${test.title}` : 'Playwright Test Report';
|
document.title = test?.title ? `| ${test.title}` : 'Playwright Test Report';
|
||||||
test?.outcome ? setFavicon({ outcome: test.outcome }) : setFavicon('default');
|
test?.outcome ? setFavIcon({ outcome: test.outcome }) : setDefaultFavIconAndTitle();
|
||||||
const resetFaviconOnBack = () => {
|
const resetFaviconOnBack = () => {
|
||||||
setFavicon('default');
|
setDefaultFavIconAndTitle();
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('popstate', resetFaviconOnBack);
|
window.addEventListener('popstate', resetFaviconOnBack);
|
||||||
|
|
@ -144,13 +144,3 @@ const LabelsLinkView: React.FC<React.PropsWithChildren<{
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function setFavicon(outcome: Pick<TestCaseSummary, 'outcome'> | 'default') {
|
|
||||||
let link: HTMLLinkElement | null = document.querySelector("link[rel*='icon']");
|
|
||||||
if (!link) {
|
|
||||||
link = document.createElement('link');
|
|
||||||
link.rel = 'icon';
|
|
||||||
document.getElementsByTagName('head')[0].appendChild(link);
|
|
||||||
}
|
|
||||||
const outcomeValue = outcome instanceof Object ? outcome.outcome : outcome;
|
|
||||||
link.href = `../logo_${outcomeValue}.svg`;
|
|
||||||
}
|
|
||||||
|
|
@ -13,6 +13,12 @@
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import favIconExpected from '../logo_expected.svg';
|
||||||
|
import favIconDefault from '../logo_default.svg';
|
||||||
|
import favIconUnExpected from '../logo_unexpected.svg';
|
||||||
|
import favIconSkipped from '../logo_skipped.svg';
|
||||||
|
import favIconFlaky from '../logo_flaky.svg';
|
||||||
|
import type { TestCaseSummary } from './types';
|
||||||
|
|
||||||
export function msToString(ms: number): string {
|
export function msToString(ms: number): string {
|
||||||
if (!isFinite(ms))
|
if (!isFinite(ms))
|
||||||
|
|
@ -39,3 +45,37 @@ export function msToString(ms: number): string {
|
||||||
const days = hours / 24;
|
const days = hours / 24;
|
||||||
return days.toFixed(1) + 'd';
|
return days.toFixed(1) + 'd';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setFavIcon(outcome: Pick<TestCaseSummary, 'outcome'>) {
|
||||||
|
const link: HTMLLinkElement | null = getLinkIconElement();
|
||||||
|
const outcomeValue = outcome.outcome;
|
||||||
|
|
||||||
|
if (outcomeValue === 'expected') {
|
||||||
|
link.href = favIconExpected;
|
||||||
|
} else if (outcomeValue === 'unexpected') {
|
||||||
|
link.href = favIconUnExpected;
|
||||||
|
} else if (outcomeValue === 'skipped') {
|
||||||
|
link.href = favIconSkipped;
|
||||||
|
} else if (outcomeValue === 'flaky') {
|
||||||
|
link.href = favIconFlaky;
|
||||||
|
} else {
|
||||||
|
link.href = favIconDefault;
|
||||||
|
document.title = 'Playwright Test Report | Summary';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setDefaultFavIconAndTitle() {
|
||||||
|
const link: HTMLLinkElement | null = getLinkIconElement();
|
||||||
|
link.href = favIconDefault;
|
||||||
|
document.title = 'Playwright Test Report | Summary';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLinkIconElement() {
|
||||||
|
let link: HTMLLinkElement | null = document.querySelector("link[rel*='icon']");
|
||||||
|
if (!link) {
|
||||||
|
link = document.createElement('link');
|
||||||
|
link.rel = 'icon';
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(link);
|
||||||
|
}
|
||||||
|
return link;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue