@storybook/test-runner
Version:
Test runner for Storybook stories
414 lines (408 loc) • 16 kB
JavaScript
import ESM_COMPAT_Module from "node:module";
import { fileURLToPath as ESM_COMPAT_fileURLToPath } from 'node:url';
const __filename = ESM_COMPAT_fileURLToPath(import.meta.url);
const require = ESM_COMPAT_Module.createRequire(import.meta.url);
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
// src/jest-playwright-preset/constants.ts
var IMPORT_KIND_PLAYWRIGHT = "playwright";
var CONFIG_ENVIRONMENT_NAME = "jest-playwright";
var CHROMIUM = "chromium";
var FIREFOX = "firefox";
var WEBKIT = "webkit";
var LAUNCH = "LAUNCH";
var PERSISTENT = "PERSISTENT";
var SERVER = "SERVER";
var DEFAULT_CONFIG = {
launchType: SERVER,
launchOptions: {},
connectOptions: {},
contextOptions: {},
browsers: [
CHROMIUM
],
exitOnPageError: true,
collectCoverage: false
};
var DEBUG_TIMEOUT = 4 * 24 * 60 * 60 * 1e3;
var PACKAGE_NAME = "@storybook/test-runner";
// src/jest-playwright-preset/utils.ts
import fs from "fs";
import path from "path";
var fsPromises = fs.promises;
var BROWSERS = [
CHROMIUM,
FIREFOX,
WEBKIT
];
var PlaywrightError = class PlaywrightError2 extends Error {
static {
__name(this, "PlaywrightError");
}
constructor(message) {
super(formatError(message));
this.name = "PlaywrightError";
}
};
var isObject = /* @__PURE__ */ __name((item) => {
return item && typeof item === "object" && !Array.isArray(item);
}, "isObject");
var deepMerge = /* @__PURE__ */ __name((target, source) => {
let output = {
...target
};
const keys = Object.keys(source);
if (isObject(target) && isObject(source)) {
keys.forEach((key) => {
if (Array.isArray(source[key]) && Array.isArray(target[key])) {
output = {
...output,
[key]: [
...source[key],
...target[key]
]
};
} else if (isObject(source[key])) {
if (!(key in target)) {
output = {
...output,
[key]: source[key]
};
} else {
output[key] = deepMerge(target[key], source[key]);
}
} else {
output = {
...output,
[key]: source[key]
};
}
});
}
return output;
}, "deepMerge");
var checkDeviceEnv = /* @__PURE__ */ __name((device, availableDevices) => {
if (!availableDevices.includes(device)) {
throw new PlaywrightError(`Wrong device. Should be one of [${availableDevices}], but got ${device}`);
}
}, "checkDeviceEnv");
var checkDevice = /* @__PURE__ */ __name((device, availableDevices) => {
if (typeof device === "string") {
const availableDeviceNames = Object.keys(availableDevices);
checkDeviceEnv(device, availableDeviceNames);
}
}, "checkDevice");
var getBrowserType = /* @__PURE__ */ __name((browser) => {
return browser || CHROMIUM;
}, "getBrowserType");
var getDeviceBrowserType = /* @__PURE__ */ __name((device, availableDevices) => {
if (typeof device === "string") {
return availableDevices[device].defaultBrowserType;
}
return device?.defaultBrowserType || null;
}, "getDeviceBrowserType");
var getPlaywrightInstance = /* @__PURE__ */ __name((browserName) => {
let pw;
let name;
if (!browserName) {
pw = __require(IMPORT_KIND_PLAYWRIGHT);
name = IMPORT_KIND_PLAYWRIGHT;
return {
name,
instance: pw,
devices: pw["devices"]
};
}
try {
pw = __require(`${IMPORT_KIND_PLAYWRIGHT}-${browserName}`);
name = browserName;
} catch (e) {
try {
pw = __require(IMPORT_KIND_PLAYWRIGHT);
name = IMPORT_KIND_PLAYWRIGHT;
} catch (e2) {
throw new PlaywrightError(`Cannot find playwright package to use ${browserName}`);
}
}
if (!pw[browserName]) {
throw new PlaywrightError(`Cannot find playwright package to use ${browserName}`);
}
return {
name,
instance: pw[browserName],
devices: pw["devices"]
};
}, "getPlaywrightInstance");
function getBrowserOptions(browserName, options) {
let result = options ? {
...options
} : {};
if (result[browserName]) {
result = deepMerge(result, result[browserName]);
}
BROWSERS.forEach((browser) => {
delete result[browser];
});
return result;
}
__name(getBrowserOptions, "getBrowserOptions");
var formatError = /* @__PURE__ */ __name((error) => `${PACKAGE_NAME}: ${error}`, "formatError");
// src/jest-playwright-preset/coverage.ts
import * as uuid from "uuid";
import path2 from "path";
import fs2 from "fs";
import { promisify } from "util";
import rimraf from "rimraf";
import NYC from "nyc";
var fsAsync = fs2.promises;
var NYC_DIR = ".nyc_output";
var COV_MERGE_DIR = path2.join(NYC_DIR, "merge");
var saveCoverageToFile = /* @__PURE__ */ __name(async (coverage) => {
await fsAsync.writeFile(path2.join(COV_MERGE_DIR, `${uuid.v4()}.json`), JSON.stringify(coverage));
}, "saveCoverageToFile");
var saveCoverageOnPage = /* @__PURE__ */ __name(async (page, collectCoverage = false) => {
if (!collectCoverage) {
console.warn(`${PACKAGE_NAME}: saveCoverage was called but collectCoverage is not true in config file`);
return;
}
const coverage = await page.evaluate(`window.__coverage__`);
if (coverage) {
await saveCoverageToFile(coverage);
}
}, "saveCoverageOnPage");
// src/jest-playwright-preset/PlaywrightEnvironment.ts
var handleError = /* @__PURE__ */ __name((error) => {
process.emit("uncaughtException", error);
}, "handleError");
var getBrowserPerProcess = /* @__PURE__ */ __name(async (playwrightInstance, browserType, config) => {
const { launchType, userDataDir, launchOptions, connectOptions } = config;
if (launchType === LAUNCH || launchType === PERSISTENT) {
if (browserType !== CHROMIUM && launchOptions?.args) {
launchOptions.args = launchOptions.args.filter((item) => item !== "--no-sandbox");
}
const options2 = getBrowserOptions(browserType, launchOptions);
if (launchType === LAUNCH) {
return playwrightInstance.launch(options2);
}
if (launchType === PERSISTENT) {
return playwrightInstance.launchPersistentContext(userDataDir, options2);
}
}
const options = getBrowserOptions(browserType, connectOptions);
return options && "endpointURL" in options ? playwrightInstance.connectOverCDP(options) : playwrightInstance.connect(options);
}, "getBrowserPerProcess");
var getDeviceConfig = /* @__PURE__ */ __name((device, availableDevices) => {
if (device) {
if (typeof device === "string") {
const { defaultBrowserType, ...deviceProps } = availableDevices[device];
return deviceProps;
} else {
const { name, defaultBrowserType, ...deviceProps } = device;
return deviceProps;
}
}
return {};
}, "getDeviceConfig");
var getDeviceName = /* @__PURE__ */ __name((device) => {
let deviceName = null;
if (device != null) {
if (typeof device === "string") {
deviceName = device;
} else {
deviceName = device.name;
}
}
return deviceName;
}, "getDeviceName");
var getPlaywrightEnv = /* @__PURE__ */ __name(() => {
const RootEnv = __require("jest-environment-node").default;
return class PlaywrightEnvironment extends RootEnv {
static {
__name(this, "PlaywrightEnvironment");
}
_config;
_jestPlaywrightConfig;
constructor(config) {
super(config);
this._config = config.projectConfig;
}
_getContextOptions(devices) {
const { browserName, device } = this._config;
const browserType = getBrowserType(browserName);
const { contextOptions } = this._jestPlaywrightConfig;
const deviceBrowserContextOptions = getDeviceConfig(device, devices);
const resultContextOptions = deepMerge(deviceBrowserContextOptions, getBrowserOptions(browserName, contextOptions));
if (browserType === FIREFOX && resultContextOptions.isMobile) {
console.warn(formatError(`isMobile is not supported in ${FIREFOX}.`));
delete resultContextOptions.isMobile;
}
return resultContextOptions;
}
_getSeparateEnvBrowserConfig(isDebug, config) {
const { debugOptions } = this._jestPlaywrightConfig;
const defaultBrowserConfig = {
...DEFAULT_CONFIG,
launchType: LAUNCH
};
let resultBrowserConfig = {
...defaultBrowserConfig,
...config
};
if (isDebug) {
if (debugOptions) {
resultBrowserConfig = deepMerge(resultBrowserConfig, debugOptions);
}
} else {
resultBrowserConfig = deepMerge(this._jestPlaywrightConfig, resultBrowserConfig);
}
return resultBrowserConfig;
}
_getSeparateEnvContextConfig(isDebug, config, browserName, devices) {
const { device, contextOptions } = config;
const { debugOptions } = this._jestPlaywrightConfig;
const deviceContextOptions = getDeviceConfig(device, devices);
let resultContextOptions = contextOptions || {};
if (isDebug) {
if (debugOptions?.contextOptions) {
resultContextOptions = deepMerge(resultContextOptions, debugOptions.contextOptions);
}
} else {
resultContextOptions = deepMerge(this._jestPlaywrightConfig.contextOptions, resultContextOptions);
}
resultContextOptions = deepMerge(deviceContextOptions, resultContextOptions);
return getBrowserOptions(browserName, resultContextOptions);
}
async _setNewPageInstance(context = this.global.context) {
const { exitOnPageError } = this._jestPlaywrightConfig;
const page = await context.newPage();
if (exitOnPageError) {
page.on("pageerror", handleError);
}
return page;
}
async _setCollectCoverage(context) {
await context.exposeFunction("reportCodeCoverage", (coverage) => {
if (coverage) saveCoverageToFile(coverage);
});
await context.addInitScript(() => window.addEventListener("beforeunload", () => {
reportCodeCoverage(window.__coverage__);
}));
}
async setup() {
const { wsEndpoint, browserName, testEnvironmentOptions } = this._config;
this._jestPlaywrightConfig = testEnvironmentOptions[CONFIG_ENVIRONMENT_NAME];
const { connectOptions, collectCoverage, selectors, launchType, skipInitialization } = this._jestPlaywrightConfig;
if (wsEndpoint) {
this._jestPlaywrightConfig.connectOptions = {
...connectOptions,
wsEndpoint
};
}
const browserType = getBrowserType(browserName);
const device = this._config.device;
const deviceName = getDeviceName(device);
const { name, instance: playwrightInstance, devices } = getPlaywrightInstance(browserType);
const contextOptions = this._getContextOptions(devices);
if (name === IMPORT_KIND_PLAYWRIGHT && selectors) {
const playwright = __require("playwright");
await Promise.all(selectors.map(({ name: name2, script }) => playwright.selectors.register(name2, script).catch((e) => {
if (!e.toString().includes("has been already")) {
throw e;
}
})));
}
this.global.browserName = browserType;
this.global.deviceName = deviceName;
if (!skipInitialization) {
const browserOrContext = await getBrowserPerProcess(playwrightInstance, browserType, this._jestPlaywrightConfig);
this.global.browser = launchType === PERSISTENT ? null : browserOrContext;
this.global.context = launchType === PERSISTENT ? browserOrContext : await this.global.browser.newContext(contextOptions);
if (collectCoverage) {
await this._setCollectCoverage(this.global.context);
}
this.global.page = await this._setNewPageInstance();
}
this.global.jestPlaywright = {
configSeparateEnv: /* @__PURE__ */ __name(async (config, isDebug = false) => {
const { device: device2 } = config;
const browserName2 = config.useDefaultBrowserType && device2 ? getDeviceBrowserType(device2, devices) || CHROMIUM : config.browser || browserType;
const deviceName2 = device2 ? getDeviceName(device2) : null;
checkDevice(deviceName2, devices);
const resultBrowserConfig = this._getSeparateEnvBrowserConfig(isDebug, config);
const resultContextOptions = this._getSeparateEnvContextConfig(isDebug, config, browserName2, devices);
const { instance } = getPlaywrightInstance(browserName2);
const browser = await getBrowserPerProcess(instance, browserName2, resultBrowserConfig);
const context = await browser.newContext(resultContextOptions);
const page = await context.newPage();
return {
browserName: browserName2,
deviceName: deviceName2,
browser,
context,
page
};
}, "configSeparateEnv"),
resetPage: /* @__PURE__ */ __name(async () => {
await this.global.page?.close();
this.global.page = await this._setNewPageInstance();
}, "resetPage"),
resetContext: /* @__PURE__ */ __name(async (newOptions) => {
const { browser, context } = this.global;
await context?.close();
const newContextOptions = newOptions ? deepMerge(contextOptions, newOptions) : contextOptions;
this.global.context = await browser.newContext(newContextOptions);
this.global.page = await this._setNewPageInstance();
}, "resetContext"),
resetBrowser: /* @__PURE__ */ __name(async (newOptions) => {
const { browser } = this.global;
await browser?.close();
this.global.browser = await getBrowserPerProcess(playwrightInstance, browserType, this._jestPlaywrightConfig);
const newContextOptions = newOptions ? deepMerge(contextOptions, newOptions) : contextOptions;
this.global.context = await this.global.browser.newContext(newContextOptions);
this.global.page = await this._setNewPageInstance();
}, "resetBrowser"),
saveCoverage: /* @__PURE__ */ __name(async (page) => saveCoverageOnPage(page, collectCoverage), "saveCoverage")
};
}
async handleTestEvent(event) {
const { browserName } = this._config;
const { collectCoverage, haveSkippedTests } = this._jestPlaywrightConfig;
const browserType = getBrowserType(browserName);
const { instance, devices } = getPlaywrightInstance(browserType);
const contextOptions = this._getContextOptions(devices);
if (haveSkippedTests && event.name === "run_start") {
this.global.browser = await getBrowserPerProcess(instance, browserType, this._jestPlaywrightConfig);
this.global.context = await this.global.browser.newContext(contextOptions);
if (collectCoverage) {
await this._setCollectCoverage(this.global.context);
}
this.global.page = await this._setNewPageInstance();
}
}
async teardown() {
const { browser, context, page } = this.global;
const { collectCoverage } = this._jestPlaywrightConfig;
page?.removeListener("pageerror", handleError);
if (collectCoverage) {
await Promise.all(context.pages().map((p) => p.close({
runBeforeUnload: true
})));
await new Promise((resolve) => setTimeout(resolve, 10));
}
await browser?.close();
await super.teardown();
}
};
}, "getPlaywrightEnv");
var PlaywrightEnvironment_default = getPlaywrightEnv();
// src/jest-playwright-entries/test-environment.ts
var test_environment_default = PlaywrightEnvironment_default;
export {
test_environment_default as default
};