UNPKG

@storybook/test-runner

Version:
414 lines (408 loc) • 16 kB
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 };