Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@web/test-runner-chrome

Package Overview
Dependencies
Maintainers
7
Versions
93
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@web/test-runner-chrome - npm Package Compare versions

Comparing version 0.0.0-canary-20230420104136 to 0.0.0-canary-20231122093600

6

dist/ChromeLauncher.d.ts
import * as puppeteerCore from 'puppeteer-core';
import { Browser, Page, PuppeteerNodeLaunchOptions, BrowserContext } from 'puppeteer-core';
import { BrowserLauncher, TestRunnerCoreConfig } from '@web/test-runner-core';
import { ChromeLauncherPage } from './ChromeLauncherPage';
import { ChromeLauncherPage } from './ChromeLauncherPage.js';
interface CreateArgs {

@@ -9,4 +9,4 @@ browser: Browser;

}
export declare type CreateBrowserContextFn = (args: CreateArgs) => BrowserContext | Promise<BrowserContext>;
export declare type CreatePageFn = (args: CreateArgs & {
export type CreateBrowserContextFn = (args: CreateArgs) => BrowserContext | Promise<BrowserContext>;
export type CreatePageFn = (args: CreateArgs & {
context: BrowserContext;

@@ -13,0 +13,0 @@ }) => Promise<Page>;

@@ -1,7 +0,4 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChromeLauncher = void 0;
const puppeteer_core_1 = require("puppeteer-core");
const findExecutablePath_1 = require("./findExecutablePath");
const ChromeLauncherPage_1 = require("./ChromeLauncherPage");
import { launch as puppeteerCoreLaunch, } from 'puppeteer-core';
import { findExecutablePath } from './findExecutablePath.js';
import { ChromeLauncherPage } from './ChromeLauncherPage.js';
function capitalize(str) {

@@ -13,9 +10,22 @@ return `${str[0].toUpperCase()}${str.substring(1)}`;

'Use the --puppeteer flag to run tests with bundled compatible version of Chromium.';
class ChromeLauncher {
export class ChromeLauncher {
name;
type = 'puppeteer';
concurrency;
launchOptions;
customPuppeteer;
createBrowserContextFn;
createPageFn;
config;
testFiles;
browser;
browserContext;
debugBrowser;
debugBrowserContext;
cachedExecutablePath;
activePages = new Map();
activeDebugPages = new Map();
inactivePages = [];
__startBrowserPromise;
constructor(launchOptions, createBrowserContextFn, createPageFn, customPuppeteer, concurrency) {
var _a;
this.type = 'puppeteer';
this.activePages = new Map();
this.activeDebugPages = new Map();
this.inactivePages = [];
this.launchOptions = launchOptions;

@@ -30,3 +40,3 @@ this.customPuppeteer = customPuppeteer;

}
else if (!((_a = this.launchOptions) === null || _a === void 0 ? void 0 : _a.product) || this.launchOptions.product === 'chrome') {
else if (!this.launchOptions?.product || this.launchOptions.product === 'chrome') {
// with puppeteer we use the a packaged chromium, puppeteer calls it chrome but we

@@ -46,4 +56,8 @@ // should call it chromium to avoid confusion

launchBrowser(options = {}) {
const mergedOptions = {
headless: 'new',
...this.launchOptions,
...options,
};
if (this.customPuppeteer) {
const mergedOptions = Object.assign(Object.assign({}, this.launchOptions), options);
// launch using a custom puppeteer instance

@@ -60,11 +74,10 @@ return this.customPuppeteer.launch(mergedOptions).catch(error => {

// launch using puppeteer-core, connecting to an installed browser
const mergedOptions = Object.assign(Object.assign({}, this.launchOptions), options);
// add a default executable path if the user did not provide any
if (!mergedOptions.executablePath) {
if (!this.cachedExecutablePath) {
this.cachedExecutablePath = findExecutablePath_1.findExecutablePath();
this.cachedExecutablePath = findExecutablePath();
}
mergedOptions.executablePath = this.cachedExecutablePath;
}
return puppeteer_core_1.launch(mergedOptions).catch(error => {
return puppeteerCoreLaunch(mergedOptions).catch(error => {
console.error('');

@@ -82,7 +95,6 @@ console.error(`Failed to launch local browser installed at ${mergedOptions.executablePath}. ${errorHelp}`);

async stop() {
var _a, _b;
if ((_a = this.browser) === null || _a === void 0 ? void 0 : _a.isConnected()) {
if (this.browser?.isConnected()) {
await this.browser.close();
}
if ((_b = this.debugBrowser) === null || _b === void 0 ? void 0 : _b.isConnected()) {
if (this.debugBrowser?.isConnected()) {
await this.debugBrowser.close();

@@ -92,3 +104,2 @@ }

async startSession(sessionId, url) {
var _a;
const { browser, context } = await this.getOrStartBrowser();

@@ -103,3 +114,3 @@ let page;

this.activePages.set(sessionId, page);
await page.runSession(url, !!((_a = this.config) === null || _a === void 0 ? void 0 : _a.coverage));
await page.runSession(url, !!this.config?.coverage);
}

@@ -114,3 +125,3 @@ isActive(sessionId) {

if (!this.debugBrowser || !this.debugBrowserContext) {
this.debugBrowser = await this.launchBrowser({ devtools: true });
this.debugBrowser = await this.launchBrowser({ devtools: true, headless: false });
this.debugBrowserContext = await this.createBrowserContextFn({

@@ -129,3 +140,2 @@ config: this.config,

async createNewPage(browser, context) {
var _a, _b;
const puppeteerPagePromise = this.createPageFn({

@@ -141,3 +151,3 @@ config: this.config,

});
return new ChromeLauncherPage_1.ChromeLauncherPage(this.config, this.testFiles, (_b = (_a = this.launchOptions) === null || _a === void 0 ? void 0 : _a.product) !== null && _b !== void 0 ? _b : 'chromium', await puppeteerPagePromise);
return new ChromeLauncherPage(this.config, this.testFiles, this.launchOptions?.product ?? 'chromium', await puppeteerPagePromise);
}

@@ -158,7 +168,6 @@ async stopSession(sessionId) {

async getOrStartBrowser() {
var _a;
if (this.__startBrowserPromise) {
return this.__startBrowserPromise;
}
if (!this.browser || !((_a = this.browser) === null || _a === void 0 ? void 0 : _a.isConnected()) || !this.browserContext) {
if (!this.browser || !this.browser?.isConnected() || !this.browserContext) {
this.__startBrowserPromise = this.startBrowser();

@@ -173,4 +182,4 @@ const { browser, context } = await this.__startBrowserPromise;

getPage(sessionId) {
var _a, _b, _c;
const page = (_b = (_a = this.activePages.get(sessionId)) === null || _a === void 0 ? void 0 : _a.puppeteerPage) !== null && _b !== void 0 ? _b : (_c = this.activeDebugPages.get(sessionId)) === null || _c === void 0 ? void 0 : _c.puppeteerPage;
const page = this.activePages.get(sessionId)?.puppeteerPage ??
this.activeDebugPages.get(sessionId)?.puppeteerPage;
if (!page) {

@@ -182,3 +191,2 @@ throw new Error(`Could not find a page for session ${sessionId}`);

}
exports.ChromeLauncher = ChromeLauncher;
//# sourceMappingURL=ChromeLauncher.js.map
import { Page } from 'puppeteer-core';
import { TestRunnerCoreConfig } from '@web/test-runner-core';
import { SessionResult } from '@web/test-runner-core';
declare global {
interface Window {
__bringTabToFront: (id: string) => void;
__releaseLock: (id: string) => void;
}
}
export declare class ChromeLauncherPage {

@@ -10,2 +16,4 @@ private config;

private nativeInstrumentationEnabledOnPage;
private patchAdded;
private resolvers;
constructor(config: TestRunnerCoreConfig, testFiles: string[], product: string, puppeteerPage: Page);

@@ -12,0 +20,0 @@ runSession(url: string, coverage: boolean): Promise<void>;

@@ -1,8 +0,13 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChromeLauncherPage = void 0;
const test_runner_coverage_v8_1 = require("@web/test-runner-coverage-v8");
class ChromeLauncherPage {
import { v8ToIstanbul } from '@web/test-runner-coverage-v8';
import { Mutex } from 'async-mutex';
const mutex = new Mutex();
export class ChromeLauncherPage {
config;
testFiles;
product;
puppeteerPage;
nativeInstrumentationEnabledOnPage = false;
patchAdded = false;
resolvers = {};
constructor(config, testFiles, product, puppeteerPage) {
this.nativeInstrumentationEnabledOnPage = false;
this.config = config;

@@ -14,5 +19,4 @@ this.testFiles = testFiles;

async runSession(url, coverage) {
var _a;
if (coverage &&
((_a = this.config.coverageConfig) === null || _a === void 0 ? void 0 : _a.nativeInstrumentation) !== false &&
this.config.coverageConfig?.nativeInstrumentation !== false &&
this.product === 'chromium') {

@@ -23,4 +27,41 @@ if (this.nativeInstrumentationEnabledOnPage) {

this.nativeInstrumentationEnabledOnPage = true;
await this.puppeteerPage.coverage.startJSCoverage();
await this.puppeteerPage.coverage.startJSCoverage({
includeRawScriptCoverage: true,
});
}
// Patching the browser page to workaround an issue in the new headless mode of Chrome where some functions
// with callbacks (requestAnimationFrame and requestIdleCallback) are not executing their callbacks.
// https://github.com/puppeteer/puppeteer/issues/10350
if (!this.patchAdded) {
await this.puppeteerPage.exposeFunction('__bringTabToFront', (id) => {
const promise = new Promise(resolve => {
this.resolvers[id] = resolve;
});
return mutex.runExclusive(async () => {
await this.puppeteerPage.bringToFront();
await promise;
});
});
await this.puppeteerPage.exposeFunction('__releaseLock', (id) => {
this.resolvers[id]?.();
});
await this.puppeteerPage.evaluateOnNewDocument(() => {
// eslint-disable-next-line @typescript-eslint/ban-types
function patchFunction(name, fn) {
window[name] = (...args) => {
const result = fn.call(window, ...args);
const id = Math.random().toString().substring(2);
// Make sure that the tab running the test code is brought back to the front.
window.__bringTabToFront(id);
fn.call(window, () => {
window.__releaseLock(id);
});
return result;
};
}
patchFunction('requestAnimationFrame', window.requestAnimationFrame);
patchFunction('requestIdleCallback', window.requestIdleCallback);
});
this.patchAdded = true;
}
await this.puppeteerPage.setViewport({ height: 600, width: 800 });

@@ -37,3 +78,2 @@ await this.puppeteerPage.goto(url);

async collectTestCoverage(config, testFiles) {
var _a, _b;
const userAgentPromise = this.puppeteerPage

@@ -53,3 +93,3 @@ .browser()

}
if (((_a = config.coverageConfig) === null || _a === void 0 ? void 0 : _a.nativeInstrumentation) === false) {
if (config.coverageConfig?.nativeInstrumentation === false) {
throw new Error('Coverage is enabled with nativeInstrumentation disabled. ' +

@@ -62,18 +102,13 @@ 'Expected coverage provided in the browser as a global __coverage__ variable.' +

}
// get native coverage from puppeteer
// TODO: this is using a private puppeteer API to grab v8 code coverage, this can be removed
// when https://github.com/puppeteer/puppeteer/issues/2136 is resolved
const response = (await this.puppeteerPage
._client()
.send('Profiler.takePreciseCoverage'));
const v8Coverage = response.result
// remove puppeteer specific scripts
.filter(r => r.url && r.url !== '__puppeteer_evaluation_script__');
const userAgent = await userAgentPromise;
await ((_b = this.puppeteerPage.coverage) === null || _b === void 0 ? void 0 : _b.stopJSCoverage());
const [userAgent, coverageResult] = await Promise.all([
userAgentPromise,
this.puppeteerPage.coverage?.stopJSCoverage(),
]);
const v8Coverage = coverageResult
?.map(entry => entry.rawScriptCoverage)
.filter((cov) => cov !== undefined);
this.nativeInstrumentationEnabledOnPage = false;
return test_runner_coverage_v8_1.v8ToIstanbul(config, testFiles, v8Coverage, userAgent);
return v8ToIstanbul(config, testFiles, v8Coverage, userAgent);
}
}
exports.ChromeLauncherPage = ChromeLauncherPage;
//# sourceMappingURL=ChromeLauncherPage.js.map

@@ -1,7 +0,4 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findExecutablePath = void 0;
const chrome_launcher_1 = require("chrome-launcher");
function findExecutablePath() {
const path = chrome_launcher_1.Launcher.getFirstInstallation();
import { Launcher as ChromeLauncher } from 'chrome-launcher';
export function findExecutablePath() {
const path = ChromeLauncher.getFirstInstallation();
if (!path) {

@@ -14,3 +11,2 @@ throw new Error('Could not automatically find any installation of Chrome using chrome-launcher. ' +

}
exports.findExecutablePath = findExecutablePath;
//# sourceMappingURL=findExecutablePath.js.map
import * as puppeteerCore from 'puppeteer-core';
import { ChromeLauncher, CreateBrowserContextFn, CreatePageFn } from './ChromeLauncher';
import { ChromeLauncher, CreateBrowserContextFn, CreatePageFn } from './ChromeLauncher.js';
import { PuppeteerNodeLaunchOptions, devices } from 'puppeteer-core';

@@ -4,0 +4,0 @@ export interface ChromeLauncherArgs {

@@ -1,34 +0,9 @@

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.chromeLauncher = exports.puppeteerCore = exports.devices = exports.ChromeLauncher = void 0;
const puppeteerCore = __importStar(require("puppeteer-core"));
exports.puppeteerCore = puppeteerCore;
const ChromeLauncher_1 = require("./ChromeLauncher");
Object.defineProperty(exports, "ChromeLauncher", { enumerable: true, get: function () { return ChromeLauncher_1.ChromeLauncher; } });
const puppeteer_core_1 = require("puppeteer-core");
Object.defineProperty(exports, "devices", { enumerable: true, get: function () { return puppeteer_core_1.devices; } });
function chromeLauncher(args = {}) {
import * as puppeteerCore from 'puppeteer-core';
import { ChromeLauncher } from './ChromeLauncher.js';
import { devices } from 'puppeteer-core';
export { ChromeLauncher, devices, puppeteerCore };
export function chromeLauncher(args = {}) {
const { launchOptions = {}, createBrowserContext = ({ browser }) => browser.defaultBrowserContext(), createPage = ({ context }) => context.newPage(), puppeteer, concurrency, } = args;
return new ChromeLauncher_1.ChromeLauncher(launchOptions, createBrowserContext, createPage, puppeteer, concurrency);
return new ChromeLauncher(launchOptions, createBrowserContext, createPage, puppeteer, concurrency);
}
exports.chromeLauncher = chromeLauncher;
//# sourceMappingURL=index.js.map
// this file is autogenerated with the generate-mjs-dts-entrypoints script
export * from './dist/index';
export * from './dist/index.js';
{
"name": "@web/test-runner-chrome",
"version": "0.0.0-canary-20230420104136",
"version": "0.0.0-canary-20231122093600",
"publishConfig": {

@@ -17,4 +17,6 @@ "access": "public"

"main": "dist/index.js",
"type": "module",
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.mjs",

@@ -25,3 +27,3 @@ "require": "./dist/index.js"

"engines": {
"node": ">=16.0.0"
"node": ">=18.0.0"
},

@@ -50,11 +52,12 @@ "scripts": {

"dependencies": {
"@web/test-runner-core": "0.0.0-canary-20230420104136",
"@web/test-runner-coverage-v8": "0.0.0-canary-20230420104136",
"@web/test-runner-core": "0.0.0-canary-20231122093600",
"@web/test-runner-coverage-v8": "0.0.0-canary-20231122093600",
"async-mutex": "0.4.0",
"chrome-launcher": "^0.15.0",
"puppeteer-core": "^19.8.1"
"puppeteer-core": "^20.0.0"
},
"devDependencies": {
"@types/istanbul-reports": "^3.0.0",
"@web/test-runner-mocha": "0.0.0-canary-20230420104136"
"@web/test-runner-mocha": "0.0.0-canary-20231122093600"
}
}

@@ -10,4 +10,4 @@ import * as puppeteerCore from 'puppeteer-core';

import { BrowserLauncher, TestRunnerCoreConfig } from '@web/test-runner-core';
import { findExecutablePath } from './findExecutablePath';
import { ChromeLauncherPage } from './ChromeLauncherPage';
import { findExecutablePath } from './findExecutablePath.js';
import { ChromeLauncherPage } from './ChromeLauncherPage.js';

@@ -83,4 +83,8 @@ function capitalize(str: string) {

launchBrowser(options: PuppeteerNodeLaunchOptions = {}) {
const mergedOptions: PuppeteerNodeLaunchOptions = {
headless: 'new',
...this.launchOptions,
...options,
};
if (this.customPuppeteer) {
const mergedOptions = { ...this.launchOptions, ...options };
// launch using a custom puppeteer instance

@@ -100,4 +104,2 @@ return this.customPuppeteer.launch(mergedOptions).catch(error => {

// launch using puppeteer-core, connecting to an installed browser
const mergedOptions = { ...this.launchOptions, ...options };
// add a default executable path if the user did not provide any

@@ -159,3 +161,3 @@ if (!mergedOptions.executablePath) {

if (!this.debugBrowser || !this.debugBrowserContext) {
this.debugBrowser = await this.launchBrowser({ devtools: true });
this.debugBrowser = await this.launchBrowser({ devtools: true, headless: false });
this.debugBrowserContext = await this.createBrowserContextFn({

@@ -162,0 +164,0 @@ config: this.config!,

@@ -1,6 +0,16 @@

import { Page } from 'puppeteer-core';
import { Page, JSCoverageEntry } from 'puppeteer-core';
import { TestRunnerCoreConfig } from '@web/test-runner-core';
import { V8Coverage, v8ToIstanbul } from '@web/test-runner-coverage-v8';
import { v8ToIstanbul } from '@web/test-runner-coverage-v8';
import { SessionResult } from '@web/test-runner-core';
import { Mutex } from 'async-mutex';
const mutex = new Mutex();
declare global {
interface Window {
__bringTabToFront: (id: string) => void;
__releaseLock: (id: string) => void;
}
}
export class ChromeLauncherPage {

@@ -12,2 +22,4 @@ private config: TestRunnerCoreConfig;

private nativeInstrumentationEnabledOnPage = false;
private patchAdded = false;
private resolvers: Record<string, () => void> = {};

@@ -36,5 +48,44 @@ constructor(

this.nativeInstrumentationEnabledOnPage = true;
await this.puppeteerPage.coverage.startJSCoverage();
await this.puppeteerPage.coverage.startJSCoverage({
includeRawScriptCoverage: true,
});
}
// Patching the browser page to workaround an issue in the new headless mode of Chrome where some functions
// with callbacks (requestAnimationFrame and requestIdleCallback) are not executing their callbacks.
// https://github.com/puppeteer/puppeteer/issues/10350
if (!this.patchAdded) {
await this.puppeteerPage.exposeFunction('__bringTabToFront', (id: string) => {
const promise = new Promise(resolve => {
this.resolvers[id] = resolve as () => void;
});
return mutex.runExclusive(async () => {
await this.puppeteerPage.bringToFront();
await promise;
});
});
await this.puppeteerPage.exposeFunction('__releaseLock', (id: string) => {
this.resolvers[id]?.();
});
await this.puppeteerPage.evaluateOnNewDocument(() => {
// eslint-disable-next-line @typescript-eslint/ban-types
function patchFunction(name: string, fn: Function) {
(window as any)[name] = (...args: unknown[]) => {
const result = fn.call(window, ...args);
const id = Math.random().toString().substring(2);
// Make sure that the tab running the test code is brought back to the front.
window.__bringTabToFront(id);
fn.call(window, () => {
window.__releaseLock(id);
});
return result;
};
}
patchFunction('requestAnimationFrame', window.requestAnimationFrame);
patchFunction('requestIdleCallback', window.requestIdleCallback);
});
this.patchAdded = true;
}
await this.puppeteerPage.setViewport({ height: 600, width: 800 });

@@ -85,16 +136,9 @@ await this.puppeteerPage.goto(url);

// get native coverage from puppeteer
// TODO: this is using a private puppeteer API to grab v8 code coverage, this can be removed
// when https://github.com/puppeteer/puppeteer/issues/2136 is resolved
const response = (await (this.puppeteerPage as any)
._client()
.send('Profiler.takePreciseCoverage')) as {
result: V8Coverage[];
};
const v8Coverage = response.result
// remove puppeteer specific scripts
.filter(r => r.url && r.url !== '__puppeteer_evaluation_script__');
const userAgent = await userAgentPromise;
await this.puppeteerPage.coverage?.stopJSCoverage();
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;

@@ -101,0 +145,0 @@

import * as puppeteerCore from 'puppeteer-core';
import { ChromeLauncher, CreateBrowserContextFn, CreatePageFn } from './ChromeLauncher';
import { ChromeLauncher, CreateBrowserContextFn, CreatePageFn } from './ChromeLauncher.js';
import { PuppeteerNodeLaunchOptions, devices } from 'puppeteer-core';

@@ -4,0 +4,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc