ScuttleBot

scuttlebot / tests / e2e / node_modules / playwright / lib / runner / dispatcher.js
Source Blame History 530 lines
f7eb47b… lmata 1 "use strict";
f7eb47b… lmata 2 var __defProp = Object.defineProperty;
f7eb47b… lmata 3 var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
f7eb47b… lmata 4 var __getOwnPropNames = Object.getOwnPropertyNames;
f7eb47b… lmata 5 var __hasOwnProp = Object.prototype.hasOwnProperty;
f7eb47b… lmata 6 var __export = (target, all) => {
f7eb47b… lmata 7 for (var name in all)
f7eb47b… lmata 8 __defProp(target, name, { get: all[name], enumerable: true });
f7eb47b… lmata 9 };
f7eb47b… lmata 10 var __copyProps = (to, from, except, desc) => {
f7eb47b… lmata 11 if (from && typeof from === "object" || typeof from === "function") {
f7eb47b… lmata 12 for (let key of __getOwnPropNames(from))
f7eb47b… lmata 13 if (!__hasOwnProp.call(to, key) && key !== except)
f7eb47b… lmata 14 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
f7eb47b… lmata 15 }
f7eb47b… lmata 16 return to;
f7eb47b… lmata 17 };
f7eb47b… lmata 18 var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
f7eb47b… lmata 19 var dispatcher_exports = {};
f7eb47b… lmata 20 __export(dispatcher_exports, {
f7eb47b… lmata 21 Dispatcher: () => Dispatcher
f7eb47b… lmata 22 });
f7eb47b… lmata 23 module.exports = __toCommonJS(dispatcher_exports);
f7eb47b… lmata 24 var import_utils = require("playwright-core/lib/utils");
f7eb47b… lmata 25 var import_utils2 = require("playwright-core/lib/utils");
f7eb47b… lmata 26 var import_rebase = require("./rebase");
f7eb47b… lmata 27 var import_workerHost = require("./workerHost");
f7eb47b… lmata 28 var import_ipc = require("../common/ipc");
f7eb47b… lmata 29 var import_internalReporter = require("../reporters/internalReporter");
f7eb47b… lmata 30 var import_util = require("../util");
f7eb47b… lmata 31 var import_storage = require("./storage");
f7eb47b… lmata 32 class Dispatcher {
f7eb47b… lmata 33 constructor(config, reporter, failureTracker) {
f7eb47b… lmata 34 this._workerSlots = [];
f7eb47b… lmata 35 this._queue = [];
f7eb47b… lmata 36 this._workerLimitPerProjectId = /* @__PURE__ */ new Map();
f7eb47b… lmata 37 this._queuedOrRunningHashCount = /* @__PURE__ */ new Map();
f7eb47b… lmata 38 this._finished = new import_utils.ManualPromise();
f7eb47b… lmata 39 this._isStopped = true;
f7eb47b… lmata 40 this._extraEnvByProjectId = /* @__PURE__ */ new Map();
f7eb47b… lmata 41 this._producedEnvByProjectId = /* @__PURE__ */ new Map();
f7eb47b… lmata 42 this._config = config;
f7eb47b… lmata 43 this._reporter = reporter;
f7eb47b… lmata 44 this._failureTracker = failureTracker;
f7eb47b… lmata 45 for (const project of config.projects) {
f7eb47b… lmata 46 if (project.workers)
f7eb47b… lmata 47 this._workerLimitPerProjectId.set(project.id, project.workers);
f7eb47b… lmata 48 }
f7eb47b… lmata 49 }
f7eb47b… lmata 50 _findFirstJobToRun() {
f7eb47b… lmata 51 for (let index = 0; index < this._queue.length; index++) {
f7eb47b… lmata 52 const job = this._queue[index];
f7eb47b… lmata 53 const projectIdWorkerLimit = this._workerLimitPerProjectId.get(job.projectId);
f7eb47b… lmata 54 if (!projectIdWorkerLimit)
f7eb47b… lmata 55 return index;
f7eb47b… lmata 56 const runningWorkersWithSameProjectId = this._workerSlots.filter((w) => w.busy && w.worker && w.worker.projectId() === job.projectId).length;
f7eb47b… lmata 57 if (runningWorkersWithSameProjectId < projectIdWorkerLimit)
f7eb47b… lmata 58 return index;
f7eb47b… lmata 59 }
f7eb47b… lmata 60 return -1;
f7eb47b… lmata 61 }
f7eb47b… lmata 62 _scheduleJob() {
f7eb47b… lmata 63 if (this._isStopped)
f7eb47b… lmata 64 return;
f7eb47b… lmata 65 const jobIndex = this._findFirstJobToRun();
f7eb47b… lmata 66 if (jobIndex === -1)
f7eb47b… lmata 67 return;
f7eb47b… lmata 68 const job = this._queue[jobIndex];
f7eb47b… lmata 69 let workerIndex = this._workerSlots.findIndex((w) => !w.busy && w.worker && w.worker.hash() === job.workerHash && !w.worker.didSendStop());
f7eb47b… lmata 70 if (workerIndex === -1)
f7eb47b… lmata 71 workerIndex = this._workerSlots.findIndex((w) => !w.busy);
f7eb47b… lmata 72 if (workerIndex === -1) {
f7eb47b… lmata 73 return;
f7eb47b… lmata 74 }
f7eb47b… lmata 75 this._queue.splice(jobIndex, 1);
f7eb47b… lmata 76 const jobDispatcher = new JobDispatcher(job, this._config, this._reporter, this._failureTracker, () => this.stop().catch(() => {
f7eb47b… lmata 77 }));
f7eb47b… lmata 78 this._workerSlots[workerIndex].busy = true;
f7eb47b… lmata 79 this._workerSlots[workerIndex].jobDispatcher = jobDispatcher;
f7eb47b… lmata 80 void this._runJobInWorker(workerIndex, jobDispatcher).then(() => {
f7eb47b… lmata 81 this._workerSlots[workerIndex].jobDispatcher = void 0;
f7eb47b… lmata 82 this._workerSlots[workerIndex].busy = false;
f7eb47b… lmata 83 this._checkFinished();
f7eb47b… lmata 84 this._scheduleJob();
f7eb47b… lmata 85 });
f7eb47b… lmata 86 }
f7eb47b… lmata 87 async _runJobInWorker(index, jobDispatcher) {
f7eb47b… lmata 88 const job = jobDispatcher.job;
f7eb47b… lmata 89 if (jobDispatcher.skipWholeJob())
f7eb47b… lmata 90 return;
f7eb47b… lmata 91 let worker = this._workerSlots[index].worker;
f7eb47b… lmata 92 if (worker && (worker.hash() !== job.workerHash || worker.didSendStop())) {
f7eb47b… lmata 93 await worker.stop();
f7eb47b… lmata 94 worker = void 0;
f7eb47b… lmata 95 if (this._isStopped)
f7eb47b… lmata 96 return;
f7eb47b… lmata 97 }
f7eb47b… lmata 98 let startError;
f7eb47b… lmata 99 if (!worker) {
f7eb47b… lmata 100 worker = this._createWorker(job, index, (0, import_ipc.serializeConfig)(this._config, true));
f7eb47b… lmata 101 this._workerSlots[index].worker = worker;
f7eb47b… lmata 102 worker.on("exit", () => this._workerSlots[index].worker = void 0);
f7eb47b… lmata 103 startError = await worker.start();
f7eb47b… lmata 104 if (this._isStopped)
f7eb47b… lmata 105 return;
f7eb47b… lmata 106 }
f7eb47b… lmata 107 if (startError)
f7eb47b… lmata 108 jobDispatcher.onExit(startError);
f7eb47b… lmata 109 else
f7eb47b… lmata 110 jobDispatcher.runInWorker(worker);
f7eb47b… lmata 111 const result = await jobDispatcher.jobResult;
f7eb47b… lmata 112 this._updateCounterForWorkerHash(job.workerHash, -1);
f7eb47b… lmata 113 if (result.didFail)
f7eb47b… lmata 114 void worker.stop(
f7eb47b… lmata 115 true
f7eb47b… lmata 116 /* didFail */
f7eb47b… lmata 117 );
f7eb47b… lmata 118 else if (this._isWorkerRedundant(worker))
f7eb47b… lmata 119 void worker.stop();
f7eb47b… lmata 120 if (!this._isStopped && result.newJob) {
f7eb47b… lmata 121 this._queue.unshift(result.newJob);
f7eb47b… lmata 122 this._updateCounterForWorkerHash(result.newJob.workerHash, 1);
f7eb47b… lmata 123 }
f7eb47b… lmata 124 }
f7eb47b… lmata 125 _checkFinished() {
f7eb47b… lmata 126 if (this._finished.isDone())
f7eb47b… lmata 127 return;
f7eb47b… lmata 128 if (this._queue.length && !this._isStopped)
f7eb47b… lmata 129 return;
f7eb47b… lmata 130 if (this._workerSlots.some((w) => w.busy))
f7eb47b… lmata 131 return;
f7eb47b… lmata 132 this._finished.resolve();
f7eb47b… lmata 133 }
f7eb47b… lmata 134 _isWorkerRedundant(worker) {
f7eb47b… lmata 135 let workersWithSameHash = 0;
f7eb47b… lmata 136 for (const slot of this._workerSlots) {
f7eb47b… lmata 137 if (slot.worker && !slot.worker.didSendStop() && slot.worker.hash() === worker.hash())
f7eb47b… lmata 138 workersWithSameHash++;
f7eb47b… lmata 139 }
f7eb47b… lmata 140 return workersWithSameHash > this._queuedOrRunningHashCount.get(worker.hash());
f7eb47b… lmata 141 }
f7eb47b… lmata 142 _updateCounterForWorkerHash(hash, delta) {
f7eb47b… lmata 143 this._queuedOrRunningHashCount.set(hash, delta + (this._queuedOrRunningHashCount.get(hash) || 0));
f7eb47b… lmata 144 }
f7eb47b… lmata 145 async run(testGroups, extraEnvByProjectId) {
f7eb47b… lmata 146 this._extraEnvByProjectId = extraEnvByProjectId;
f7eb47b… lmata 147 this._queue = testGroups;
f7eb47b… lmata 148 for (const group of testGroups)
f7eb47b… lmata 149 this._updateCounterForWorkerHash(group.workerHash, 1);
f7eb47b… lmata 150 this._isStopped = false;
f7eb47b… lmata 151 this._workerSlots = [];
f7eb47b… lmata 152 if (this._failureTracker.hasReachedMaxFailures())
f7eb47b… lmata 153 void this.stop();
f7eb47b… lmata 154 for (let i = 0; i < this._config.config.workers; i++)
f7eb47b… lmata 155 this._workerSlots.push({ busy: false });
f7eb47b… lmata 156 for (let i = 0; i < this._workerSlots.length; i++)
f7eb47b… lmata 157 this._scheduleJob();
f7eb47b… lmata 158 this._checkFinished();
f7eb47b… lmata 159 await this._finished;
f7eb47b… lmata 160 }
f7eb47b… lmata 161 _createWorker(testGroup, parallelIndex, loaderData) {
f7eb47b… lmata 162 const projectConfig = this._config.projects.find((p) => p.id === testGroup.projectId);
f7eb47b… lmata 163 const outputDir = projectConfig.project.outputDir;
f7eb47b… lmata 164 const worker = new import_workerHost.WorkerHost(testGroup, {
f7eb47b… lmata 165 parallelIndex,
f7eb47b… lmata 166 config: loaderData,
f7eb47b… lmata 167 extraEnv: this._extraEnvByProjectId.get(testGroup.projectId) || {},
f7eb47b… lmata 168 outputDir,
f7eb47b… lmata 169 pauseOnError: this._failureTracker.pauseOnError(),
f7eb47b… lmata 170 pauseAtEnd: this._failureTracker.pauseAtEnd(projectConfig)
f7eb47b… lmata 171 });
f7eb47b… lmata 172 const handleOutput = (params) => {
f7eb47b… lmata 173 const chunk = chunkFromParams(params);
f7eb47b… lmata 174 if (worker.didFail()) {
f7eb47b… lmata 175 return { chunk };
f7eb47b… lmata 176 }
f7eb47b… lmata 177 const currentlyRunning = this._workerSlots[parallelIndex].jobDispatcher?.currentlyRunning();
f7eb47b… lmata 178 if (!currentlyRunning)
f7eb47b… lmata 179 return { chunk };
f7eb47b… lmata 180 return { chunk, test: currentlyRunning.test, result: currentlyRunning.result };
f7eb47b… lmata 181 };
f7eb47b… lmata 182 worker.on("stdOut", (params) => {
f7eb47b… lmata 183 const { chunk, test, result } = handleOutput(params);
f7eb47b… lmata 184 result?.stdout.push(chunk);
f7eb47b… lmata 185 this._reporter.onStdOut?.(chunk, test, result);
f7eb47b… lmata 186 });
f7eb47b… lmata 187 worker.on("stdErr", (params) => {
f7eb47b… lmata 188 const { chunk, test, result } = handleOutput(params);
f7eb47b… lmata 189 result?.stderr.push(chunk);
f7eb47b… lmata 190 this._reporter.onStdErr?.(chunk, test, result);
f7eb47b… lmata 191 });
f7eb47b… lmata 192 worker.on("teardownErrors", (params) => {
f7eb47b… lmata 193 this._failureTracker.onWorkerError();
f7eb47b… lmata 194 for (const error of params.fatalErrors)
f7eb47b… lmata 195 this._reporter.onError?.(error);
f7eb47b… lmata 196 });
f7eb47b… lmata 197 worker.on("exit", () => {
f7eb47b… lmata 198 const producedEnv = this._producedEnvByProjectId.get(testGroup.projectId) || {};
f7eb47b… lmata 199 this._producedEnvByProjectId.set(testGroup.projectId, { ...producedEnv, ...worker.producedEnv() });
f7eb47b… lmata 200 });
f7eb47b… lmata 201 worker.onRequest("cloneStorage", async (params) => {
f7eb47b… lmata 202 return await import_storage.Storage.clone(params.storageFile, outputDir);
f7eb47b… lmata 203 });
f7eb47b… lmata 204 worker.onRequest("upstreamStorage", async (params) => {
f7eb47b… lmata 205 await import_storage.Storage.upstream(params.storageFile, params.storageOutFile);
f7eb47b… lmata 206 });
f7eb47b… lmata 207 return worker;
f7eb47b… lmata 208 }
f7eb47b… lmata 209 producedEnvByProjectId() {
f7eb47b… lmata 210 return this._producedEnvByProjectId;
f7eb47b… lmata 211 }
f7eb47b… lmata 212 async stop() {
f7eb47b… lmata 213 if (this._isStopped)
f7eb47b… lmata 214 return;
f7eb47b… lmata 215 this._isStopped = true;
f7eb47b… lmata 216 await Promise.all(this._workerSlots.map(({ worker }) => worker?.stop()));
f7eb47b… lmata 217 this._checkFinished();
f7eb47b… lmata 218 }
f7eb47b… lmata 219 }
f7eb47b… lmata 220 class JobDispatcher {
f7eb47b… lmata 221 constructor(job, config, reporter, failureTracker, stopCallback) {
f7eb47b… lmata 222 this.jobResult = new import_utils.ManualPromise();
f7eb47b… lmata 223 this._listeners = [];
f7eb47b… lmata 224 this._failedTests = /* @__PURE__ */ new Set();
f7eb47b… lmata 225 this._failedWithNonRetriableError = /* @__PURE__ */ new Set();
f7eb47b… lmata 226 this._remainingByTestId = /* @__PURE__ */ new Map();
f7eb47b… lmata 227 this._dataByTestId = /* @__PURE__ */ new Map();
f7eb47b… lmata 228 this._parallelIndex = 0;
f7eb47b… lmata 229 this._workerIndex = 0;
f7eb47b… lmata 230 this.job = job;
f7eb47b… lmata 231 this._config = config;
f7eb47b… lmata 232 this._reporter = reporter;
f7eb47b… lmata 233 this._failureTracker = failureTracker;
f7eb47b… lmata 234 this._stopCallback = stopCallback;
f7eb47b… lmata 235 this._remainingByTestId = new Map(this.job.tests.map((e) => [e.id, e]));
f7eb47b… lmata 236 }
f7eb47b… lmata 237 _onTestBegin(params) {
f7eb47b… lmata 238 const test = this._remainingByTestId.get(params.testId);
f7eb47b… lmata 239 if (!test) {
f7eb47b… lmata 240 return;
f7eb47b… lmata 241 }
f7eb47b… lmata 242 const result = test._appendTestResult();
f7eb47b… lmata 243 this._dataByTestId.set(test.id, { test, result, steps: /* @__PURE__ */ new Map() });
f7eb47b… lmata 244 result.parallelIndex = this._parallelIndex;
f7eb47b… lmata 245 result.workerIndex = this._workerIndex;
f7eb47b… lmata 246 result.startTime = new Date(params.startWallTime);
f7eb47b… lmata 247 this._reporter.onTestBegin?.(test, result);
f7eb47b… lmata 248 this._currentlyRunning = { test, result };
f7eb47b… lmata 249 }
f7eb47b… lmata 250 _onTestEnd(params) {
f7eb47b… lmata 251 if (this._failureTracker.hasReachedMaxFailures()) {
f7eb47b… lmata 252 params.status = "interrupted";
f7eb47b… lmata 253 params.errors = [];
f7eb47b… lmata 254 }
f7eb47b… lmata 255 const data = this._dataByTestId.get(params.testId);
f7eb47b… lmata 256 if (!data) {
f7eb47b… lmata 257 return;
f7eb47b… lmata 258 }
f7eb47b… lmata 259 this._dataByTestId.delete(params.testId);
f7eb47b… lmata 260 this._remainingByTestId.delete(params.testId);
f7eb47b… lmata 261 const { result, test } = data;
f7eb47b… lmata 262 result.duration = params.duration;
f7eb47b… lmata 263 result.errors = params.errors;
f7eb47b… lmata 264 result.error = result.errors[0];
f7eb47b… lmata 265 result.status = params.status;
f7eb47b… lmata 266 result.annotations = params.annotations;
f7eb47b… lmata 267 test.annotations = [...params.annotations];
f7eb47b… lmata 268 test.expectedStatus = params.expectedStatus;
f7eb47b… lmata 269 test.timeout = params.timeout;
f7eb47b… lmata 270 const isFailure = result.status !== "skipped" && result.status !== test.expectedStatus;
f7eb47b… lmata 271 if (isFailure)
f7eb47b… lmata 272 this._failedTests.add(test);
f7eb47b… lmata 273 if (params.hasNonRetriableError)
f7eb47b… lmata 274 this._addNonretriableTestAndSerialModeParents(test);
f7eb47b… lmata 275 this._reportTestEnd(test, result);
f7eb47b… lmata 276 this._currentlyRunning = void 0;
f7eb47b… lmata 277 }
f7eb47b… lmata 278 _addNonretriableTestAndSerialModeParents(test) {
f7eb47b… lmata 279 this._failedWithNonRetriableError.add(test);
f7eb47b… lmata 280 for (let parent = test.parent; parent; parent = parent.parent) {
f7eb47b… lmata 281 if (parent._parallelMode === "serial")
f7eb47b… lmata 282 this._failedWithNonRetriableError.add(parent);
f7eb47b… lmata 283 }
f7eb47b… lmata 284 }
f7eb47b… lmata 285 _onStepBegin(params) {
f7eb47b… lmata 286 const data = this._dataByTestId.get(params.testId);
f7eb47b… lmata 287 if (!data) {
f7eb47b… lmata 288 return;
f7eb47b… lmata 289 }
f7eb47b… lmata 290 const { result, steps, test } = data;
f7eb47b… lmata 291 const parentStep = params.parentStepId ? steps.get(params.parentStepId) : void 0;
f7eb47b… lmata 292 const step = {
f7eb47b… lmata 293 title: params.title,
f7eb47b… lmata 294 titlePath: () => {
f7eb47b… lmata 295 const parentPath = parentStep?.titlePath() || [];
f7eb47b… lmata 296 return [...parentPath, params.title];
f7eb47b… lmata 297 },
f7eb47b… lmata 298 parent: parentStep,
f7eb47b… lmata 299 category: params.category,
f7eb47b… lmata 300 startTime: new Date(params.wallTime),
f7eb47b… lmata 301 duration: -1,
f7eb47b… lmata 302 steps: [],
f7eb47b… lmata 303 attachments: [],
f7eb47b… lmata 304 annotations: [],
f7eb47b… lmata 305 location: params.location
f7eb47b… lmata 306 };
f7eb47b… lmata 307 steps.set(params.stepId, step);
f7eb47b… lmata 308 (parentStep || result).steps.push(step);
f7eb47b… lmata 309 this._reporter.onStepBegin?.(test, result, step);
f7eb47b… lmata 310 }
f7eb47b… lmata 311 _onStepEnd(params) {
f7eb47b… lmata 312 const data = this._dataByTestId.get(params.testId);
f7eb47b… lmata 313 if (!data) {
f7eb47b… lmata 314 return;
f7eb47b… lmata 315 }
f7eb47b… lmata 316 const { result, steps, test } = data;
f7eb47b… lmata 317 const step = steps.get(params.stepId);
f7eb47b… lmata 318 if (!step) {
f7eb47b… lmata 319 this._reporter.onStdErr?.("Internal error: step end without step begin: " + params.stepId, test, result);
f7eb47b… lmata 320 return;
f7eb47b… lmata 321 }
f7eb47b… lmata 322 step.duration = params.wallTime - step.startTime.getTime();
f7eb47b… lmata 323 if (params.error)
f7eb47b… lmata 324 step.error = params.error;
f7eb47b… lmata 325 if (params.suggestedRebaseline)
f7eb47b… lmata 326 (0, import_rebase.addSuggestedRebaseline)(step.location, params.suggestedRebaseline);
f7eb47b… lmata 327 step.annotations = params.annotations;
f7eb47b… lmata 328 steps.delete(params.stepId);
f7eb47b… lmata 329 this._reporter.onStepEnd?.(test, result, step);
f7eb47b… lmata 330 }
f7eb47b… lmata 331 _onAttach(params) {
f7eb47b… lmata 332 const data = this._dataByTestId.get(params.testId);
f7eb47b… lmata 333 if (!data) {
f7eb47b… lmata 334 return;
f7eb47b… lmata 335 }
f7eb47b… lmata 336 const attachment = {
f7eb47b… lmata 337 name: params.name,
f7eb47b… lmata 338 path: params.path,
f7eb47b… lmata 339 contentType: params.contentType,
f7eb47b… lmata 340 body: params.body !== void 0 ? Buffer.from(params.body, "base64") : void 0
f7eb47b… lmata 341 };
f7eb47b… lmata 342 data.result.attachments.push(attachment);
f7eb47b… lmata 343 if (params.stepId) {
f7eb47b… lmata 344 const step = data.steps.get(params.stepId);
f7eb47b… lmata 345 if (step)
f7eb47b… lmata 346 step.attachments.push(attachment);
f7eb47b… lmata 347 else
f7eb47b… lmata 348 this._reporter.onStdErr?.("Internal error: step id not found: " + params.stepId);
f7eb47b… lmata 349 }
f7eb47b… lmata 350 }
f7eb47b… lmata 351 _failTestWithErrors(test, errors) {
f7eb47b… lmata 352 const runData = this._dataByTestId.get(test.id);
f7eb47b… lmata 353 let result;
f7eb47b… lmata 354 if (runData) {
f7eb47b… lmata 355 result = runData.result;
f7eb47b… lmata 356 } else {
f7eb47b… lmata 357 result = test._appendTestResult();
f7eb47b… lmata 358 this._reporter.onTestBegin?.(test, result);
f7eb47b… lmata 359 }
f7eb47b… lmata 360 result.errors = [...errors];
f7eb47b… lmata 361 result.error = result.errors[0];
f7eb47b… lmata 362 result.status = errors.length ? "failed" : "skipped";
f7eb47b… lmata 363 this._reportTestEnd(test, result);
f7eb47b… lmata 364 this._failedTests.add(test);
f7eb47b… lmata 365 }
f7eb47b… lmata 366 _massSkipTestsFromRemaining(testIds, errors) {
f7eb47b… lmata 367 for (const test of this._remainingByTestId.values()) {
f7eb47b… lmata 368 if (!testIds.has(test.id))
f7eb47b… lmata 369 continue;
f7eb47b… lmata 370 if (!this._failureTracker.hasReachedMaxFailures()) {
f7eb47b… lmata 371 this._failTestWithErrors(test, errors);
f7eb47b… lmata 372 errors = [];
f7eb47b… lmata 373 }
f7eb47b… lmata 374 this._remainingByTestId.delete(test.id);
f7eb47b… lmata 375 }
f7eb47b… lmata 376 if (errors.length) {
f7eb47b… lmata 377 this._failureTracker.onWorkerError();
f7eb47b… lmata 378 for (const error of errors)
f7eb47b… lmata 379 this._reporter.onError?.(error);
f7eb47b… lmata 380 }
f7eb47b… lmata 381 }
f7eb47b… lmata 382 _onDone(params) {
f7eb47b… lmata 383 if (!this._remainingByTestId.size && !this._failedTests.size && !params.fatalErrors.length && !params.skipTestsDueToSetupFailure.length && !params.fatalUnknownTestIds && !params.unexpectedExitError && !params.stoppedDueToUnhandledErrorInTestFail) {
f7eb47b… lmata 384 this._finished({ didFail: false });
f7eb47b… lmata 385 return;
f7eb47b… lmata 386 }
f7eb47b… lmata 387 for (const testId of params.fatalUnknownTestIds || []) {
f7eb47b… lmata 388 const test = this._remainingByTestId.get(testId);
f7eb47b… lmata 389 if (test) {
f7eb47b… lmata 390 this._remainingByTestId.delete(testId);
f7eb47b… lmata 391 this._failTestWithErrors(test, [{ message: `Test not found in the worker process. Make sure test title does not change.` }]);
f7eb47b… lmata 392 }
f7eb47b… lmata 393 }
f7eb47b… lmata 394 if (params.fatalErrors.length) {
f7eb47b… lmata 395 this._massSkipTestsFromRemaining(new Set(this._remainingByTestId.keys()), params.fatalErrors);
f7eb47b… lmata 396 }
f7eb47b… lmata 397 this._massSkipTestsFromRemaining(new Set(params.skipTestsDueToSetupFailure), []);
f7eb47b… lmata 398 if (params.unexpectedExitError) {
f7eb47b… lmata 399 if (this._currentlyRunning)
f7eb47b… lmata 400 this._massSkipTestsFromRemaining(/* @__PURE__ */ new Set([this._currentlyRunning.test.id]), [params.unexpectedExitError]);
f7eb47b… lmata 401 else
f7eb47b… lmata 402 this._massSkipTestsFromRemaining(new Set(this._remainingByTestId.keys()), [params.unexpectedExitError]);
f7eb47b… lmata 403 }
f7eb47b… lmata 404 const retryCandidates = /* @__PURE__ */ new Set();
f7eb47b… lmata 405 const serialSuitesWithFailures = /* @__PURE__ */ new Set();
f7eb47b… lmata 406 for (const failedTest of this._failedTests) {
f7eb47b… lmata 407 if (this._failedWithNonRetriableError.has(failedTest))
f7eb47b… lmata 408 continue;
f7eb47b… lmata 409 retryCandidates.add(failedTest);
f7eb47b… lmata 410 let outermostSerialSuite;
f7eb47b… lmata 411 for (let parent = failedTest.parent; parent; parent = parent.parent) {
f7eb47b… lmata 412 if (parent._parallelMode === "serial")
f7eb47b… lmata 413 outermostSerialSuite = parent;
f7eb47b… lmata 414 }
f7eb47b… lmata 415 if (outermostSerialSuite && !this._failedWithNonRetriableError.has(outermostSerialSuite))
f7eb47b… lmata 416 serialSuitesWithFailures.add(outermostSerialSuite);
f7eb47b… lmata 417 }
f7eb47b… lmata 418 const testsBelongingToSomeSerialSuiteWithFailures = [...this._remainingByTestId.values()].filter((test) => {
f7eb47b… lmata 419 let parent = test.parent;
f7eb47b… lmata 420 while (parent && !serialSuitesWithFailures.has(parent))
f7eb47b… lmata 421 parent = parent.parent;
f7eb47b… lmata 422 return !!parent;
f7eb47b… lmata 423 });
f7eb47b… lmata 424 this._massSkipTestsFromRemaining(new Set(testsBelongingToSomeSerialSuiteWithFailures.map((test) => test.id)), []);
f7eb47b… lmata 425 for (const serialSuite of serialSuitesWithFailures) {
f7eb47b… lmata 426 serialSuite.allTests().forEach((test) => retryCandidates.add(test));
f7eb47b… lmata 427 }
f7eb47b… lmata 428 const remaining = [...this._remainingByTestId.values()];
f7eb47b… lmata 429 for (const test of retryCandidates) {
f7eb47b… lmata 430 if (test.results.length < test.retries + 1)
f7eb47b… lmata 431 remaining.push(test);
f7eb47b… lmata 432 }
f7eb47b… lmata 433 const newJob = remaining.length ? { ...this.job, tests: remaining } : void 0;
f7eb47b… lmata 434 this._finished({ didFail: true, newJob });
f7eb47b… lmata 435 }
f7eb47b… lmata 436 onExit(data) {
f7eb47b… lmata 437 const unexpectedExitError = data.unexpectedly ? {
f7eb47b… lmata 438 message: `Error: worker process exited unexpectedly (code=${data.code}, signal=${data.signal})`
f7eb47b… lmata 439 } : void 0;
f7eb47b… lmata 440 this._onDone({ skipTestsDueToSetupFailure: [], fatalErrors: [], unexpectedExitError });
f7eb47b… lmata 441 }
f7eb47b… lmata 442 _finished(result) {
f7eb47b… lmata 443 import_utils.eventsHelper.removeEventListeners(this._listeners);
f7eb47b… lmata 444 this.jobResult.resolve(result);
f7eb47b… lmata 445 }
f7eb47b… lmata 446 runInWorker(worker) {
f7eb47b… lmata 447 this._parallelIndex = worker.parallelIndex;
f7eb47b… lmata 448 this._workerIndex = worker.workerIndex;
f7eb47b… lmata 449 const runPayload = {
f7eb47b… lmata 450 file: this.job.requireFile,
f7eb47b… lmata 451 entries: this.job.tests.map((test) => {
f7eb47b… lmata 452 return { testId: test.id, retry: test.results.length };
f7eb47b… lmata 453 })
f7eb47b… lmata 454 };
f7eb47b… lmata 455 worker.runTestGroup(runPayload);
f7eb47b… lmata 456 this._listeners = [
f7eb47b… lmata 457 import_utils.eventsHelper.addEventListener(worker, "testBegin", this._onTestBegin.bind(this)),
f7eb47b… lmata 458 import_utils.eventsHelper.addEventListener(worker, "testEnd", this._onTestEnd.bind(this)),
f7eb47b… lmata 459 import_utils.eventsHelper.addEventListener(worker, "stepBegin", this._onStepBegin.bind(this)),
f7eb47b… lmata 460 import_utils.eventsHelper.addEventListener(worker, "stepEnd", this._onStepEnd.bind(this)),
f7eb47b… lmata 461 import_utils.eventsHelper.addEventListener(worker, "attach", this._onAttach.bind(this)),
f7eb47b… lmata 462 import_utils.eventsHelper.addEventListener(worker, "testPaused", this._onTestPaused.bind(this, worker)),
f7eb47b… lmata 463 import_utils.eventsHelper.addEventListener(worker, "done", this._onDone.bind(this)),
f7eb47b… lmata 464 import_utils.eventsHelper.addEventListener(worker, "exit", this.onExit.bind(this))
f7eb47b… lmata 465 ];
f7eb47b… lmata 466 }
f7eb47b… lmata 467 _onTestPaused(worker, params) {
f7eb47b… lmata 468 const data = this._dataByTestId.get(params.testId);
f7eb47b… lmata 469 if (!data)
f7eb47b… lmata 470 return;
f7eb47b… lmata 471 const { result, test } = data;
f7eb47b… lmata 472 const sendMessage = async (message) => {
f7eb47b… lmata 473 try {
f7eb47b… lmata 474 if (this.jobResult.isDone())
f7eb47b… lmata 475 throw new Error("Test has already stopped");
f7eb47b… lmata 476 const response = await worker.sendCustomMessage({ testId: test.id, request: message.request });
f7eb47b… lmata 477 if (response.error)
f7eb47b… lmata 478 (0, import_internalReporter.addLocationAndSnippetToError)(this._config.config, response.error);
f7eb47b… lmata 479 return response;
f7eb47b… lmata 480 } catch (e) {
f7eb47b… lmata 481 const error = (0, import_util.serializeError)(e);
f7eb47b… lmata 482 (0, import_internalReporter.addLocationAndSnippetToError)(this._config.config, error);
f7eb47b… lmata 483 return { response: void 0, error };
f7eb47b… lmata 484 }
f7eb47b… lmata 485 };
f7eb47b… lmata 486 result.status = params.status;
f7eb47b… lmata 487 result.errors = params.errors;
f7eb47b… lmata 488 result.error = result.errors[0];
f7eb47b… lmata 489 void this._reporter.onTestPaused?.(test, result).then(() => {
f7eb47b… lmata 490 worker.sendResume({});
f7eb47b… lmata 491 });
f7eb47b… lmata 492 this._failureTracker.onTestPaused?.({ ...params, sendMessage });
f7eb47b… lmata 493 }
f7eb47b… lmata 494 skipWholeJob() {
f7eb47b… lmata 495 const allTestsSkipped = this.job.tests.every((test) => test.expectedStatus === "skipped");
f7eb47b… lmata 496 if (allTestsSkipped && !this._failureTracker.hasReachedMaxFailures()) {
f7eb47b… lmata 497 for (const test of this.job.tests) {
f7eb47b… lmata 498 const result = test._appendTestResult();
f7eb47b… lmata 499 this._reporter.onTestBegin?.(test, result);
f7eb47b… lmata 500 result.status = "skipped";
f7eb47b… lmata 501 result.annotations = [...test.annotations];
f7eb47b… lmata 502 this._reportTestEnd(test, result);
f7eb47b… lmata 503 }
f7eb47b… lmata 504 return true;
f7eb47b… lmata 505 }
f7eb47b… lmata 506 return false;
f7eb47b… lmata 507 }
f7eb47b… lmata 508 currentlyRunning() {
f7eb47b… lmata 509 return this._currentlyRunning;
f7eb47b… lmata 510 }
f7eb47b… lmata 511 _reportTestEnd(test, result) {
f7eb47b… lmata 512 this._reporter.onTestEnd?.(test, result);
f7eb47b… lmata 513 const hadMaxFailures = this._failureTracker.hasReachedMaxFailures();
f7eb47b… lmata 514 this._failureTracker.onTestEnd(test, result);
f7eb47b… lmata 515 if (this._failureTracker.hasReachedMaxFailures()) {
f7eb47b… lmata 516 this._stopCallback();
f7eb47b… lmata 517 if (!hadMaxFailures)
f7eb47b… lmata 518 this._reporter.onError?.({ message: import_utils2.colors.red(`Testing stopped early after ${this._failureTracker.maxFailures()} maximum allowed failures.`) });
f7eb47b… lmata 519 }
f7eb47b… lmata 520 }
f7eb47b… lmata 521 }
f7eb47b… lmata 522 function chunkFromParams(params) {
f7eb47b… lmata 523 if (typeof params.text === "string")
f7eb47b… lmata 524 return params.text;
f7eb47b… lmata 525 return Buffer.from(params.buffer, "base64");
f7eb47b… lmata 526 }
f7eb47b… lmata 527 // Annotate the CommonJS export names for ESM import in node:
f7eb47b… lmata 528 0 && (module.exports = {
f7eb47b… lmata 529 Dispatcher
f7eb47b… lmata 530 });

Keyboard Shortcuts

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