ScuttleBot

scuttlebot / tests / e2e / node_modules / playwright / lib / plugins / webServerPlugin.js
Source Blame History 237 lines
f7eb47b… lmata 1 "use strict";
f7eb47b… lmata 2 var __create = Object.create;
f7eb47b… lmata 3 var __defProp = Object.defineProperty;
f7eb47b… lmata 4 var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
f7eb47b… lmata 5 var __getOwnPropNames = Object.getOwnPropertyNames;
f7eb47b… lmata 6 var __getProtoOf = Object.getPrototypeOf;
f7eb47b… lmata 7 var __hasOwnProp = Object.prototype.hasOwnProperty;
f7eb47b… lmata 8 var __export = (target, all) => {
f7eb47b… lmata 9 for (var name in all)
f7eb47b… lmata 10 __defProp(target, name, { get: all[name], enumerable: true });
f7eb47b… lmata 11 };
f7eb47b… lmata 12 var __copyProps = (to, from, except, desc) => {
f7eb47b… lmata 13 if (from && typeof from === "object" || typeof from === "function") {
f7eb47b… lmata 14 for (let key of __getOwnPropNames(from))
f7eb47b… lmata 15 if (!__hasOwnProp.call(to, key) && key !== except)
f7eb47b… lmata 16 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
f7eb47b… lmata 17 }
f7eb47b… lmata 18 return to;
f7eb47b… lmata 19 };
f7eb47b… lmata 20 var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
f7eb47b… lmata 21 // If the importer is in node compatibility mode or this is not an ESM
f7eb47b… lmata 22 // file that has been converted to a CommonJS file using a Babel-
f7eb47b… lmata 23 // compatible transform (i.e. "__esModule" has not been set), then set
f7eb47b… lmata 24 // "default" to the CommonJS "module.exports" for node compatibility.
f7eb47b… lmata 25 isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
f7eb47b… lmata 26 mod
f7eb47b… lmata 27 ));
f7eb47b… lmata 28 var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
f7eb47b… lmata 29 var webServerPlugin_exports = {};
f7eb47b… lmata 30 __export(webServerPlugin_exports, {
f7eb47b… lmata 31 WebServerPlugin: () => WebServerPlugin,
f7eb47b… lmata 32 webServer: () => webServer,
f7eb47b… lmata 33 webServerPluginsForConfig: () => webServerPluginsForConfig
f7eb47b… lmata 34 });
f7eb47b… lmata 35 module.exports = __toCommonJS(webServerPlugin_exports);
f7eb47b… lmata 36 var import_net = __toESM(require("net"));
f7eb47b… lmata 37 var import_path = __toESM(require("path"));
f7eb47b… lmata 38 var import_utils = require("playwright-core/lib/utils");
f7eb47b… lmata 39 var import_utils2 = require("playwright-core/lib/utils");
f7eb47b… lmata 40 var import_utilsBundle = require("playwright-core/lib/utilsBundle");
f7eb47b… lmata 41 const DEFAULT_ENVIRONMENT_VARIABLES = {
f7eb47b… lmata 42 "BROWSER": "none",
f7eb47b… lmata 43 // Disable that create-react-app will open the page in the browser
f7eb47b… lmata 44 "FORCE_COLOR": "1",
f7eb47b… lmata 45 "DEBUG_COLORS": "1"
f7eb47b… lmata 46 };
f7eb47b… lmata 47 const debugWebServer = (0, import_utilsBundle.debug)("pw:webserver");
f7eb47b… lmata 48 class WebServerPlugin {
f7eb47b… lmata 49 constructor(options, checkPortOnly) {
f7eb47b… lmata 50 this.name = "playwright:webserver";
f7eb47b… lmata 51 this._options = options;
f7eb47b… lmata 52 this._checkPortOnly = checkPortOnly;
f7eb47b… lmata 53 }
f7eb47b… lmata 54 async setup(config, configDir, reporter) {
f7eb47b… lmata 55 this._reporter = reporter;
f7eb47b… lmata 56 this._isAvailableCallback = this._options.url ? getIsAvailableFunction(this._options.url, this._checkPortOnly, !!this._options.ignoreHTTPSErrors, this._reporter.onStdErr?.bind(this._reporter)) : void 0;
f7eb47b… lmata 57 this._options.cwd = this._options.cwd ? import_path.default.resolve(configDir, this._options.cwd) : configDir;
f7eb47b… lmata 58 try {
f7eb47b… lmata 59 await this._startProcess();
f7eb47b… lmata 60 await this._waitForProcess();
f7eb47b… lmata 61 } catch (error) {
f7eb47b… lmata 62 await this.teardown();
f7eb47b… lmata 63 throw error;
f7eb47b… lmata 64 }
f7eb47b… lmata 65 }
f7eb47b… lmata 66 async teardown() {
f7eb47b… lmata 67 debugWebServer(`Terminating the WebServer`);
f7eb47b… lmata 68 await this._killProcess?.();
f7eb47b… lmata 69 debugWebServer(`Terminated the WebServer`);
f7eb47b… lmata 70 }
f7eb47b… lmata 71 async _startProcess() {
f7eb47b… lmata 72 let processExitedReject = (error) => {
f7eb47b… lmata 73 };
f7eb47b… lmata 74 this._processExitedPromise = new Promise((_, reject) => processExitedReject = reject);
f7eb47b… lmata 75 const isAlreadyAvailable = await this._isAvailableCallback?.();
f7eb47b… lmata 76 if (isAlreadyAvailable) {
f7eb47b… lmata 77 debugWebServer(`WebServer is already available`);
f7eb47b… lmata 78 if (this._options.reuseExistingServer)
f7eb47b… lmata 79 return;
f7eb47b… lmata 80 const port = new URL(this._options.url).port;
f7eb47b… lmata 81 throw new Error(`${this._options.url ?? `http://localhost${port ? ":" + port : ""}`} is already used, make sure that nothing is running on the port/url or set reuseExistingServer:true in config.webServer.`);
f7eb47b… lmata 82 }
f7eb47b… lmata 83 if (!this._options.command)
f7eb47b… lmata 84 throw new Error("config.webServer.command cannot be empty");
f7eb47b… lmata 85 debugWebServer(`Starting WebServer process ${this._options.command}...`);
f7eb47b… lmata 86 const { launchedProcess, gracefullyClose } = await (0, import_utils.launchProcess)({
f7eb47b… lmata 87 command: this._options.command,
f7eb47b… lmata 88 env: {
f7eb47b… lmata 89 ...DEFAULT_ENVIRONMENT_VARIABLES,
f7eb47b… lmata 90 ...process.env,
f7eb47b… lmata 91 ...this._options.env
f7eb47b… lmata 92 },
f7eb47b… lmata 93 cwd: this._options.cwd,
f7eb47b… lmata 94 stdio: "stdin",
f7eb47b… lmata 95 shell: true,
f7eb47b… lmata 96 attemptToGracefullyClose: async () => {
f7eb47b… lmata 97 if (process.platform === "win32")
f7eb47b… lmata 98 throw new Error("Graceful shutdown is not supported on Windows");
f7eb47b… lmata 99 if (!this._options.gracefulShutdown)
f7eb47b… lmata 100 throw new Error("skip graceful shutdown");
f7eb47b… lmata 101 const { signal, timeout = 0 } = this._options.gracefulShutdown;
f7eb47b… lmata 102 process.kill(-launchedProcess.pid, signal);
f7eb47b… lmata 103 return new Promise((resolve, reject) => {
f7eb47b… lmata 104 const timer = timeout !== 0 ? setTimeout(() => reject(new Error(`process didn't close gracefully within timeout`)), timeout) : void 0;
f7eb47b… lmata 105 launchedProcess.once("close", (...args) => {
f7eb47b… lmata 106 clearTimeout(timer);
f7eb47b… lmata 107 resolve();
f7eb47b… lmata 108 });
f7eb47b… lmata 109 });
f7eb47b… lmata 110 },
f7eb47b… lmata 111 log: () => {
f7eb47b… lmata 112 },
f7eb47b… lmata 113 onExit: (code) => processExitedReject(new Error(code ? `Process from config.webServer was not able to start. Exit code: ${code}` : "Process from config.webServer exited early.")),
f7eb47b… lmata 114 tempDirectories: []
f7eb47b… lmata 115 });
f7eb47b… lmata 116 this._killProcess = gracefullyClose;
f7eb47b… lmata 117 debugWebServer(`Process started`);
f7eb47b… lmata 118 if (this._options.wait?.stdout || this._options.wait?.stderr)
f7eb47b… lmata 119 this._waitForStdioPromise = new import_utils.ManualPromise();
f7eb47b… lmata 120 const stdioWaitCollectors = {
f7eb47b… lmata 121 stdout: this._options.wait?.stdout ? "" : void 0,
f7eb47b… lmata 122 stderr: this._options.wait?.stderr ? "" : void 0
f7eb47b… lmata 123 };
f7eb47b… lmata 124 launchedProcess.stdout.on("data", (data) => {
f7eb47b… lmata 125 if (debugWebServer.enabled || this._options.stdout === "pipe")
f7eb47b… lmata 126 this._reporter.onStdOut?.(prefixOutputLines(data.toString(), this._options.name));
f7eb47b… lmata 127 });
f7eb47b… lmata 128 launchedProcess.stderr.on("data", (data) => {
f7eb47b… lmata 129 if (debugWebServer.enabled || (this._options.stderr === "pipe" || !this._options.stderr))
f7eb47b… lmata 130 this._reporter.onStdErr?.(prefixOutputLines(data.toString(), this._options.name));
f7eb47b… lmata 131 });
f7eb47b… lmata 132 const resolveStdioPromise = () => {
f7eb47b… lmata 133 stdioWaitCollectors.stdout = void 0;
f7eb47b… lmata 134 stdioWaitCollectors.stderr = void 0;
f7eb47b… lmata 135 this._waitForStdioPromise?.resolve();
f7eb47b… lmata 136 };
f7eb47b… lmata 137 for (const stdio of ["stdout", "stderr"]) {
f7eb47b… lmata 138 launchedProcess[stdio].on("data", (data) => {
f7eb47b… lmata 139 if (!this._options.wait?.[stdio] || stdioWaitCollectors[stdio] === void 0)
f7eb47b… lmata 140 return;
f7eb47b… lmata 141 stdioWaitCollectors[stdio] += data.toString();
f7eb47b… lmata 142 this._options.wait[stdio].lastIndex = 0;
f7eb47b… lmata 143 const result = this._options.wait[stdio].exec(stdioWaitCollectors[stdio]);
f7eb47b… lmata 144 if (result) {
f7eb47b… lmata 145 for (const [key, value] of Object.entries(result.groups || {}))
f7eb47b… lmata 146 process.env[key.toUpperCase()] = value;
f7eb47b… lmata 147 resolveStdioPromise();
f7eb47b… lmata 148 }
f7eb47b… lmata 149 });
f7eb47b… lmata 150 }
f7eb47b… lmata 151 }
f7eb47b… lmata 152 async _waitForProcess() {
f7eb47b… lmata 153 if (!this._isAvailableCallback && !this._waitForStdioPromise) {
f7eb47b… lmata 154 this._processExitedPromise.catch(() => {
f7eb47b… lmata 155 });
f7eb47b… lmata 156 return;
f7eb47b… lmata 157 }
f7eb47b… lmata 158 debugWebServer(`Waiting for availability...`);
f7eb47b… lmata 159 const launchTimeout = this._options.timeout || 60 * 1e3;
f7eb47b… lmata 160 const cancellationToken = { canceled: false };
f7eb47b… lmata 161 const deadline = (0, import_utils.monotonicTime)() + launchTimeout;
f7eb47b… lmata 162 const racingPromises = [this._processExitedPromise];
f7eb47b… lmata 163 if (this._isAvailableCallback)
f7eb47b… lmata 164 racingPromises.push((0, import_utils.raceAgainstDeadline)(() => waitFor(this._isAvailableCallback, cancellationToken), deadline));
f7eb47b… lmata 165 if (this._waitForStdioPromise)
f7eb47b… lmata 166 racingPromises.push((0, import_utils.raceAgainstDeadline)(() => this._waitForStdioPromise, deadline));
f7eb47b… lmata 167 const { timedOut } = await Promise.race(racingPromises);
f7eb47b… lmata 168 cancellationToken.canceled = true;
f7eb47b… lmata 169 if (timedOut)
f7eb47b… lmata 170 throw new Error(`Timed out waiting ${launchTimeout}ms from config.webServer.`);
f7eb47b… lmata 171 debugWebServer(`WebServer available`);
f7eb47b… lmata 172 }
f7eb47b… lmata 173 }
f7eb47b… lmata 174 async function isPortUsed(port) {
f7eb47b… lmata 175 const innerIsPortUsed = (host) => new Promise((resolve) => {
f7eb47b… lmata 176 const conn = import_net.default.connect(port, host).on("error", () => {
f7eb47b… lmata 177 resolve(false);
f7eb47b… lmata 178 }).on("connect", () => {
f7eb47b… lmata 179 conn.end();
f7eb47b… lmata 180 resolve(true);
f7eb47b… lmata 181 });
f7eb47b… lmata 182 });
f7eb47b… lmata 183 return await innerIsPortUsed("127.0.0.1") || await innerIsPortUsed("::1");
f7eb47b… lmata 184 }
f7eb47b… lmata 185 async function waitFor(waitFn, cancellationToken) {
f7eb47b… lmata 186 const logScale = [100, 250, 500];
f7eb47b… lmata 187 while (!cancellationToken.canceled) {
f7eb47b… lmata 188 const connected = await waitFn();
f7eb47b… lmata 189 if (connected)
f7eb47b… lmata 190 return;
f7eb47b… lmata 191 const delay = logScale.shift() || 1e3;
f7eb47b… lmata 192 debugWebServer(`Waiting ${delay}ms`);
f7eb47b… lmata 193 await new Promise((x) => setTimeout(x, delay));
f7eb47b… lmata 194 }
f7eb47b… lmata 195 }
f7eb47b… lmata 196 function getIsAvailableFunction(url, checkPortOnly, ignoreHTTPSErrors, onStdErr) {
f7eb47b… lmata 197 const urlObject = new URL(url);
f7eb47b… lmata 198 if (!checkPortOnly)
f7eb47b… lmata 199 return () => (0, import_utils.isURLAvailable)(urlObject, ignoreHTTPSErrors, debugWebServer, onStdErr);
f7eb47b… lmata 200 const port = urlObject.port;
f7eb47b… lmata 201 return () => isPortUsed(+port);
f7eb47b… lmata 202 }
f7eb47b… lmata 203 const webServer = (options) => {
f7eb47b… lmata 204 return new WebServerPlugin(options, false);
f7eb47b… lmata 205 };
f7eb47b… lmata 206 const webServerPluginsForConfig = (config) => {
f7eb47b… lmata 207 const shouldSetBaseUrl = !!config.config.webServer;
f7eb47b… lmata 208 const webServerPlugins = [];
f7eb47b… lmata 209 for (const webServerConfig of config.webServers) {
f7eb47b… lmata 210 if (webServerConfig.port && webServerConfig.url)
f7eb47b… lmata 211 throw new Error(`Either 'port' or 'url' should be specified in config.webServer.`);
f7eb47b… lmata 212 let url;
f7eb47b… lmata 213 if (webServerConfig.port || webServerConfig.url) {
f7eb47b… lmata 214 url = webServerConfig.url || `http://localhost:${webServerConfig.port}`;
f7eb47b… lmata 215 if (shouldSetBaseUrl && !webServerConfig.url)
f7eb47b… lmata 216 process.env.PLAYWRIGHT_TEST_BASE_URL = url;
f7eb47b… lmata 217 }
f7eb47b… lmata 218 webServerPlugins.push(new WebServerPlugin({ ...webServerConfig, url }, webServerConfig.port !== void 0));
f7eb47b… lmata 219 }
f7eb47b… lmata 220 return webServerPlugins;
f7eb47b… lmata 221 };
f7eb47b… lmata 222 function prefixOutputLines(output, prefixName = "WebServer") {
f7eb47b… lmata 223 const lastIsNewLine = output[output.length - 1] === "\n";
f7eb47b… lmata 224 let lines = output.split("\n");
f7eb47b… lmata 225 if (lastIsNewLine)
f7eb47b… lmata 226 lines.pop();
f7eb47b… lmata 227 lines = lines.map((line) => import_utils2.colors.dim(`[${prefixName}] `) + line);
f7eb47b… lmata 228 if (lastIsNewLine)
f7eb47b… lmata 229 lines.push("");
f7eb47b… lmata 230 return lines.join("\n");
f7eb47b… lmata 231 }
f7eb47b… lmata 232 // Annotate the CommonJS export names for ESM import in node:
f7eb47b… lmata 233 0 && (module.exports = {
f7eb47b… lmata 234 WebServerPlugin,
f7eb47b… lmata 235 webServer,
f7eb47b… lmata 236 webServerPluginsForConfig
f7eb47b… lmata 237 });

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button