@web/test-runner-chrome
Version:
Chrome browser launcher for Web Test Runner
106 lines (90 loc) • 3.36 kB
text/typescript
import { Page, JSCoverageEntry } from 'puppeteer-core';
import { TestRunnerCoreConfig } from '@web/test-runner-core';
import { v8ToIstanbul } from '@web/test-runner-coverage-v8';
import { SessionResult } from '@web/test-runner-core';
declare global {
interface Window {
__bringTabToFront: (id: string) => void;
__releaseLock: (id: string) => void;
}
}
export class ChromeLauncherPage {
private config: TestRunnerCoreConfig;
private testFiles: string[];
private browser: string;
public puppeteerPage: Page;
private nativeInstrumentationEnabledOnPage = false;
private patchAdded = false;
private resolvers: Record<string, () => void> = {};
constructor(
config: TestRunnerCoreConfig,
testFiles: string[],
product: string,
puppeteerPage: Page,
) {
this.config = config;
this.testFiles = testFiles;
this.browser = product;
this.puppeteerPage = puppeteerPage;
}
async runSession(url: string, coverage: boolean) {
if (
coverage &&
this.config.coverageConfig?.nativeInstrumentation !== false &&
this.browser === 'chromium'
) {
if (this.nativeInstrumentationEnabledOnPage) {
await this.puppeteerPage.coverage.stopJSCoverage();
}
this.nativeInstrumentationEnabledOnPage = true;
await this.puppeteerPage.coverage.startJSCoverage({
includeRawScriptCoverage: true,
});
}
await this.puppeteerPage.setViewport({ height: 600, width: 800 });
await this.puppeteerPage.goto(url);
}
async stopSession(): Promise<SessionResult> {
const testCoverage = await this.collectTestCoverage(this.config, this.testFiles);
// navigate to an empty page to kill any running code on the page, stopping timers and
// breaking a potential endless reload loop
await this.puppeteerPage.goto('about:blank');
return { testCoverage };
}
private async collectTestCoverage(config: TestRunnerCoreConfig, testFiles: string[]) {
const userAgentPromise = this.puppeteerPage
.browser()
.userAgent()
.catch(() => undefined);
try {
const coverageFromBrowser = await this.puppeteerPage.evaluate(
() => (window as any).__coverage__,
);
if (coverageFromBrowser) {
// coverage was generated by JS, return that
return coverageFromBrowser;
}
} catch (error) {
// evaluate throws when the test navigates in the browser
}
if (config.coverageConfig?.nativeInstrumentation === false) {
throw new Error(
'Coverage is enabled with nativeInstrumentation disabled. ' +
'Expected coverage provided in the browser as a global __coverage__ variable.' +
'Use a plugin like babel-plugin-istanbul to generate the coverage, or enable native instrumentation.',
);
}
if (!this.nativeInstrumentationEnabledOnPage) {
return undefined;
}
const [userAgent, coverageResult] = await Promise.all([
userAgentPromise,
this.puppeteerPage.coverage?.stopJSCoverage(),
]);
const v8Coverage = coverageResult
?.map(entry => entry.rawScriptCoverage)
.filter((cov): cov is Required<JSCoverageEntry>['rawScriptCoverage'] => cov !== undefined);
this.nativeInstrumentationEnabledOnPage = false;
return v8ToIstanbul(config, testFiles, v8Coverage, userAgent);
}
}