How to use buildTestEndPayload method in Playwright Internal

Best JavaScript code snippet using playwright-internal

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

workerRunner.js

Source: workerRunner.js Github

copy
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4  value: true
5});
6exports.WorkerRunner = void 0;
7
8var _fs = _interopRequireDefault(require("fs"));
9
10var _path = _interopRequireDefault(require("path"));
11
12var _rimraf = _interopRequireDefault(require("rimraf"));
13
14var _util = _interopRequireDefault(require("util"));
15
16var _events = require("events");
17
18var _util2 = require("./util");
19
20var _globals = require("./globals");
21
22var _loader = require("./loader");
23
24var _test = require("./test");
25
26var _fixtures = require("./fixtures");
27
28function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
29
30/**
31 * Copyright Microsoft Corporation. All rights reserved.
32 *
33 * Licensed under the Apache License, Version 2.0 (the "License");
34 * you may not use this file except in compliance with the License.
35 * You may obtain a copy of the License at
36 *
37 *     http://www.apache.org/licenses/LICENSE-2.0
38 *
39 * Unless required by applicable law or agreed to in writing, software
40 * distributed under the License is distributed on an "AS IS" BASIS,
41 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42 * See the License for the specific language governing permissions and
43 * limitations under the License.
44 */
45const removeFolderAsync = _util.default.promisify(_rimraf.default);
46
47class WorkerRunner extends _events.EventEmitter {
48  constructor(params) {
49    super();
50    this._params = void 0;
51    this._loader = void 0;
52    this._project = void 0;
53    this._workerInfo = void 0;
54    this._projectNamePathSegment = '';
55    this._uniqueProjectNamePathSegment = '';
56    this._fixtureRunner = void 0;
57    this._failedTestId = void 0;
58    this._fatalError = void 0;
59    this._entries = new Map();
60    this._isStopped = false;
61    this._runFinished = Promise.resolve();
62    this._currentDeadlineRunner = void 0;
63    this._currentTest = null;
64    this._params = params;
65    this._fixtureRunner = new _fixtures.FixtureRunner();
66  }
67
68  stop() {
69    if (!this._isStopped) {
70      var _this$_currentDeadlin;
71
72      this._isStopped = true; // Interrupt current action.
73
74      (_this$_currentDeadlin = this._currentDeadlineRunner) === null || _this$_currentDeadlin === void 0 ? void 0 : _this$_currentDeadlin.setDeadline(0); // TODO: mark test as 'interrupted' instead.
75
76      if (this._currentTest && this._currentTest.testInfo.status === 'passed') this._currentTest.testInfo.status = 'skipped';
77    }
78
79    return this._runFinished;
80  }
81
82  async cleanup() {
83    // We have to load the project to get the right deadline below.
84    await this._loadIfNeeded(); // TODO: separate timeout for teardown?
85
86    const result = await (0, _util2.raceAgainstDeadline)((async () => {
87      await this._fixtureRunner.teardownScope('test');
88      await this._fixtureRunner.teardownScope('worker');
89    })(), this._deadline());
90    if (result.timedOut) throw new Error(`Timeout of ${this._project.config.timeout}ms exceeded while shutting down environment`);
91  }
92
93  unhandledError(error) {
94    if (this._currentTest && this._currentTest.type === 'test') {
95      if (!this._currentTest.testInfo.error) {
96        this._currentTest.testInfo.status = 'failed';
97        this._currentTest.testInfo.error = (0, _util2.serializeError)(error);
98      }
99    } else {
100      // No current test - fatal error.
101      if (!this._fatalError) this._fatalError = (0, _util2.serializeError)(error);
102    }
103
104    this.stop();
105  }
106
107  _deadline() {
108    return this._project.config.timeout ? (0, _util2.monotonicTime)() + this._project.config.timeout : undefined;
109  }
110
111  async _loadIfNeeded() {
112    if (this._loader) return;
113    this._loader = await _loader.Loader.deserialize(this._params.loader);
114    this._project = this._loader.projects()[this._params.projectIndex];
115    this._projectNamePathSegment = (0, _util2.sanitizeForFilePath)(this._project.config.name);
116
117    const sameName = this._loader.projects().filter(project => project.config.name === this._project.config.name);
118
119    if (sameName.length > 1) this._uniqueProjectNamePathSegment = this._project.config.name + (sameName.indexOf(this._project) + 1);else this._uniqueProjectNamePathSegment = this._project.config.name;
120    this._uniqueProjectNamePathSegment = (0, _util2.sanitizeForFilePath)(this._uniqueProjectNamePathSegment);
121    this._workerInfo = {
122      workerIndex: this._params.workerIndex,
123      project: this._project.config,
124      config: this._loader.fullConfig()
125    };
126  }
127
128  async run(runPayload) {
129    let runFinishedCalback = () => {};
130
131    this._runFinished = new Promise(f => runFinishedCalback = f);
132
133    try {
134      this._entries = new Map(runPayload.entries.map(e => [e.testId, e]));
135      await this._loadIfNeeded();
136      const fileSuite = await this._loader.loadTestFile(runPayload.file);
137      let anyPool;
138
139      const suite = this._project.cloneFileSuite(fileSuite, this._params.repeatEachIndex, test => {
140        if (!this._entries.has(test._id)) return false;
141        anyPool = test._pool;
142        return true;
143      });
144
145      if (suite && anyPool) {
146        this._fixtureRunner.setPool(anyPool);
147
148        await this._runSuite(suite, []);
149      }
150    } catch (e) {
151      // In theory, we should run above code without any errors.
152      // However, in the case we screwed up, or loadTestFile failed in the worker
153      // but not in the runner, let's do a fatal error.
154      this.unhandledError(e);
155    } finally {
156      this._reportDone();
157
158      runFinishedCalback();
159    }
160  }
161
162  async _runSuite(suite, annotations) {
163    // When stopped, do not run a suite. But if we have started running the suite with hooks,
164    // always finish the hooks.
165    if (this._isStopped) return;
166    annotations = annotations.concat(suite._annotations);
167
168    for (const beforeAllModifier of suite._modifiers) {
169      if (!this._fixtureRunner.dependsOnWorkerFixturesOnly(beforeAllModifier.fn, beforeAllModifier.location)) continue; // TODO: separate timeout for beforeAll modifiers?
170
171      const result = await (0, _util2.raceAgainstDeadline)(this._fixtureRunner.resolveParametersAndRunHookOrTest(beforeAllModifier.fn, this._workerInfo, undefined), this._deadline());
172
173      if (result.timedOut) {
174        this._fatalError = (0, _util2.serializeError)(new Error(`Timeout of ${this._project.config.timeout}ms exceeded while running ${beforeAllModifier.type} modifier`));
175        this.stop();
176      }
177
178      if (!!result.result) annotations.push({
179        type: beforeAllModifier.type,
180        description: beforeAllModifier.description
181      });
182    }
183
184    for (const hook of suite._allHooks) {
185      var _this$_entries$get;
186
187      if (hook._type !== 'beforeAll') continue;
188      const firstTest = suite.allTests()[0];
189      await this._runTestOrAllHook(hook, annotations, ((_this$_entries$get = this._entries.get(firstTest._id)) === null || _this$_entries$get === void 0 ? void 0 : _this$_entries$get.retry) || 0);
190    }
191
192    for (const entry of suite._entries) {
193      if (entry instanceof _test.Suite) {
194        await this._runSuite(entry, annotations);
195      } else {
196        const runEntry = this._entries.get(entry._id);
197
198        if (runEntry && !this._isStopped) await this._runTestOrAllHook(entry, annotations, runEntry.retry);
199      }
200    }
201
202    for (const hook of suite._allHooks) {
203      if (hook._type !== 'afterAll') continue;
204      await this._runTestOrAllHook(hook, annotations, 0);
205    }
206  }
207
208  async _runTestOrAllHook(test, annotations, retry) {
209    const reportEvents = test._type === 'test';
210    const startTime = (0, _util2.monotonicTime)();
211    const startWallTime = Date.now();
212    let deadlineRunner;
213    const testId = test._id;
214
215    const baseOutputDir = (() => {
216      const relativeTestFilePath = _path.default.relative(this._project.config.testDir, test._requireFile.replace(/\.(spec|test)\.(js|ts|mjs)$/, ''));
217
218      const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === 'win32' ? new RegExp('\\\\', 'g') : new RegExp('/', 'g'), '-');
219      let testOutputDir = sanitizedRelativePath + '-' + (0, _util2.sanitizeForFilePath)(test.title);
220      if (this._uniqueProjectNamePathSegment) testOutputDir += '-' + this._uniqueProjectNamePathSegment;
221      if (retry) testOutputDir += '-retry' + retry;
222      if (this._params.repeatEachIndex) testOutputDir += '-repeat' + this._params.repeatEachIndex;
223      return _path.default.join(this._project.config.outputDir, testOutputDir);
224    })();
225
226    let testFinishedCallback = () => {};
227
228    let lastStepId = 0;
229    const testInfo = {
230      workerIndex: this._params.workerIndex,
231      project: this._project.config,
232      config: this._loader.fullConfig(),
233      title: test.title,
234      file: test.location.file,
235      line: test.location.line,
236      column: test.location.column,
237      fn: test.fn,
238      repeatEachIndex: this._params.repeatEachIndex,
239      retry,
240      expectedStatus: test.expectedStatus,
241      annotations: [],
242      attachments: [],
243      duration: 0,
244      status: 'passed',
245      stdout: [],
246      stderr: [],
247      timeout: this._project.config.timeout,
248      snapshotSuffix: '',
249      outputDir: baseOutputDir,
250      outputPath: (...pathSegments) => {
251        _fs.default.mkdirSync(baseOutputDir, {
252          recursive: true
253        });
254
255        return _path.default.join(baseOutputDir, ...pathSegments);
256      },
257      snapshotPath: snapshotName => {
258        let suffix = '';
259        if (this._projectNamePathSegment) suffix += '-' + this._projectNamePathSegment;
260        if (testInfo.snapshotSuffix) suffix += '-' + testInfo.snapshotSuffix;
261
262        const ext = _path.default.extname(snapshotName);
263
264        if (ext) snapshotName = (0, _util2.sanitizeForFilePath)(snapshotName.substring(0, snapshotName.length - ext.length)) + suffix + ext;else snapshotName = (0, _util2.sanitizeForFilePath)(snapshotName) + suffix;
265        return _path.default.join(test._requireFile + '-snapshots', snapshotName);
266      },
267      skip: (...args) => modifier(testInfo, 'skip', args),
268      fixme: (...args) => modifier(testInfo, 'fixme', args),
269      fail: (...args) => modifier(testInfo, 'fail', args),
270      slow: (...args) => modifier(testInfo, 'slow', args),
271      setTimeout: timeout => {
272        testInfo.timeout = timeout;
273        if (deadlineRunner) deadlineRunner.setDeadline(deadline());
274      },
275      _testFinished: new Promise(f => testFinishedCallback = f),
276      _addStep: (category, title) => {
277        const stepId = `${category}@${title}@${++lastStepId}`;
278        const payload = {
279          testId,
280          stepId,
281          category,
282          title,
283          wallTime: Date.now()
284        };
285        if (reportEvents) this.emit('stepBegin', payload);
286        let callbackHandled = false;
287        return error => {
288          if (callbackHandled) return;
289          callbackHandled = true;
290          if (error instanceof Error) error = (0, _util2.serializeError)(error);
291          const payload = {
292            testId,
293            stepId,
294            wallTime: Date.now(),
295            error
296          };
297          if (reportEvents) this.emit('stepEnd', payload);
298        };
299      }
300    }; // Inherit test.setTimeout() from parent suites.
301
302    for (let suite = test.parent; suite; suite = suite.parent) {
303      if (suite._timeout !== undefined) {
304        testInfo.setTimeout(suite._timeout);
305        break;
306      }
307    } // Process annotations defined on parent suites.
308
309
310    for (const annotation of annotations) {
311      testInfo.annotations.push(annotation);
312
313      switch (annotation.type) {
314        case 'fixme':
315        case 'skip':
316          testInfo.expectedStatus = 'skipped';
317          break;
318
319        case 'fail':
320          if (testInfo.expectedStatus !== 'skipped') testInfo.expectedStatus = 'failed';
321          break;
322
323        case 'slow':
324          testInfo.setTimeout(testInfo.timeout * 3);
325          break;
326      }
327    }
328
329    this._currentTest = {
330      testInfo,
331      testId,
332      type: test._type
333    };
334    (0, _globals.setCurrentTestInfo)(testInfo);
335
336    const deadline = () => {
337      return testInfo.timeout ? startTime + testInfo.timeout : undefined;
338    };
339
340    if (reportEvents) this.emit('testBegin', buildTestBeginPayload(testId, testInfo, startWallTime));
341
342    if (testInfo.expectedStatus === 'skipped') {
343      testInfo.status = 'skipped';
344      if (reportEvents) this.emit('testEnd', buildTestEndPayload(testId, testInfo));
345      return;
346    } // Update the fixture pool - it may differ between tests, but only in test-scoped fixtures.
347
348
349    this._fixtureRunner.setPool(test._pool);
350
351    this._currentDeadlineRunner = deadlineRunner = new _util2.DeadlineRunner(this._runTestWithBeforeHooks(test, testInfo), deadline());
352    const result = await deadlineRunner.result; // Do not overwrite test failure upon hook timeout.
353
354    if (result.timedOut && testInfo.status === 'passed') testInfo.status = 'timedOut';
355    testFinishedCallback();
356
357    if (!result.timedOut) {
358      this._currentDeadlineRunner = deadlineRunner = new _util2.DeadlineRunner(this._runAfterHooks(test, testInfo), deadline());
359      deadlineRunner.setDeadline(deadline());
360      const hooksResult = await deadlineRunner.result; // Do not overwrite test failure upon hook timeout.
361
362      if (hooksResult.timedOut && testInfo.status === 'passed') testInfo.status = 'timedOut';
363    } else {
364      // A timed-out test gets a full additional timeout to run after hooks.
365      const newDeadline = this._deadline();
366
367      this._currentDeadlineRunner = deadlineRunner = new _util2.DeadlineRunner(this._runAfterHooks(test, testInfo), newDeadline);
368      await deadlineRunner.result;
369    }
370
371    this._currentDeadlineRunner = undefined;
372    testInfo.duration = (0, _util2.monotonicTime)() - startTime;
373    if (reportEvents) this.emit('testEnd', buildTestEndPayload(testId, testInfo));
374    const isFailure = testInfo.status === 'timedOut' || testInfo.status === 'failed' && testInfo.expectedStatus !== 'failed';
375    const preserveOutput = this._loader.fullConfig().preserveOutput === 'always' || this._loader.fullConfig().preserveOutput === 'failures-only' && isFailure;
376    if (!preserveOutput) await removeFolderAsync(testInfo.outputDir).catch(e => {});
377    this._currentTest = null;
378    (0, _globals.setCurrentTestInfo)(null);
379
380    if (testInfo.status !== 'passed' && testInfo.status !== 'skipped') {
381      if (test._type === 'test') this._failedTestId = testId;else this._fatalError = testInfo.error;
382      this.stop();
383    }
384  }
385
386  async _runBeforeHooks(test, testInfo) {
387    try {
388      const beforeEachModifiers = [];
389
390      for (let s = test.parent; s; s = s.parent) {
391        const modifiers = s._modifiers.filter(modifier => !this._fixtureRunner.dependsOnWorkerFixturesOnly(modifier.fn, modifier.location));
392
393        beforeEachModifiers.push(...modifiers.reverse());
394      }
395
396      beforeEachModifiers.reverse();
397
398      for (const modifier of beforeEachModifiers) {
399        const result = await this._fixtureRunner.resolveParametersAndRunHookOrTest(modifier.fn, this._workerInfo, testInfo);
400        testInfo[modifier.type](!!result, modifier.description);
401      }
402
403      await this._runHooks(test.parent, 'beforeEach', testInfo);
404    } catch (error) {
405      if (error instanceof SkipError) {
406        if (testInfo.status === 'passed') testInfo.status = 'skipped';
407      } else {
408        testInfo.status = 'failed';
409        testInfo.error = (0, _util2.serializeError)(error);
410      } // Continue running afterEach hooks even after the failure.
411
412    }
413  }
414
415  async _runTestWithBeforeHooks(test, testInfo) {
416    const completeStep = testInfo._addStep('hook', 'Before Hooks');
417
418    if (test._type === 'test') await this._runBeforeHooks(test, testInfo); // Do not run the test when beforeEach hook fails.
419
420    if (testInfo.status === 'failed' || testInfo.status === 'skipped') {
421      completeStep === null || completeStep === void 0 ? void 0 : completeStep(testInfo.error);
422      return;
423    }
424
425    try {
426      await this._fixtureRunner.resolveParametersAndRunHookOrTest(test.fn, this._workerInfo, testInfo, completeStep);
427    } catch (error) {
428      if (error instanceof SkipError) {
429        if (testInfo.status === 'passed') testInfo.status = 'skipped';
430      } else {
431        // We might fail after the timeout, e.g. due to fixture teardown.
432        // Do not overwrite the timeout status.
433        if (testInfo.status === 'passed') testInfo.status = 'failed'; // Keep the error even in the case of timeout, if there was no error before.
434
435        if (!('error' in testInfo)) testInfo.error = (0, _util2.serializeError)(error);
436      }
437    } finally {
438      completeStep === null || completeStep === void 0 ? void 0 : completeStep(testInfo.error);
439    }
440  }
441
442  async _runAfterHooks(test, testInfo) {
443    var _completeStep;
444
445    let completeStep;
446    let teardownError;
447
448    try {
449      completeStep = testInfo._addStep('hook', 'After Hooks');
450      if (test._type === 'test') await this._runHooks(test.parent, 'afterEach', testInfo);
451    } catch (error) {
452      if (!(error instanceof SkipError)) {
453        if (testInfo.status === 'passed') testInfo.status = 'failed'; // Do not overwrite test failure error.
454
455        if (!('error' in testInfo)) testInfo.error = (0, _util2.serializeError)(error); // Continue running even after the failure.
456      }
457    }
458
459    try {
460      await this._fixtureRunner.teardownScope('test');
461    } catch (error) {
462      if (testInfo.status === 'passed') testInfo.status = 'failed'; // Do not overwrite test failure error.
463
464      if (!('error' in testInfo)) {
465        testInfo.error = (0, _util2.serializeError)(error);
466        teardownError = testInfo.error;
467      }
468    }
469
470    (_completeStep = completeStep) === null || _completeStep === void 0 ? void 0 : _completeStep(teardownError);
471  }
472
473  async _runHooks(suite, type, testInfo) {
474    const all = [];
475
476    for (let s = suite; s; s = s.parent) {
477      const funcs = s._eachHooks.filter(e => e.type === type).map(e => e.fn);
478
479      all.push(...funcs.reverse());
480    }
481
482    if (type === 'beforeEach') all.reverse();
483    let error;
484
485    for (const hook of all) {
486      try {
487        await this._fixtureRunner.resolveParametersAndRunHookOrTest(hook, this._workerInfo, testInfo);
488      } catch (e) {
489        // Always run all the hooks, and capture the first error.
490        error = error || e;
491      }
492    }
493
494    if (error) throw error;
495  }
496
497  _reportDone() {
498    const donePayload = {
499      failedTestId: this._failedTestId,
500      fatalError: this._fatalError
501    };
502    this.emit('done', donePayload);
503  }
504
505}
506
507exports.WorkerRunner = WorkerRunner;
508
509function buildTestBeginPayload(testId, testInfo, startWallTime) {
510  return {
511    testId,
512    workerIndex: testInfo.workerIndex,
513    startWallTime
514  };
515}
516
517function buildTestEndPayload(testId, testInfo) {
518  return {
519    testId,
520    duration: testInfo.duration,
521    status: testInfo.status,
522    error: testInfo.error,
523    expectedStatus: testInfo.expectedStatus,
524    annotations: testInfo.annotations,
525    timeout: testInfo.timeout,
526    attachments: testInfo.attachments.map(a => {
527      var _a$body;
528
529      return {
530        name: a.name,
531        contentType: a.contentType,
532        path: a.path,
533        body: (_a$body = a.body) === null || _a$body === void 0 ? void 0 : _a$body.toString('base64')
534      };
535    })
536  };
537}
538
539function modifier(testInfo, type, modifierArgs) {
540  if (typeof modifierArgs[1] === 'function') {
541    throw new Error(['It looks like you are calling test.skip() inside the test and pass a callback.', 'Pass a condition instead and optional description instead:', `test('my test', async ({ page, isMobile }) => {`, `  test.skip(isMobile, 'This test is not applicable on mobile');`, `});`].join('\n'));
542  }
543
544  if (modifierArgs.length >= 1 && !modifierArgs[0]) return;
545  const description = modifierArgs[1];
546  testInfo.annotations.push({
547    type,
548    description
549  });
550
551  if (type === 'slow') {
552    testInfo.setTimeout(testInfo.timeout * 3);
553  } else if (type === 'skip' || type === 'fixme') {
554    testInfo.expectedStatus = 'skipped';
555    throw new SkipError('Test is skipped: ' + (description || ''));
556  } else if (type === 'fail') {
557    if (testInfo.expectedStatus !== 'skipped') testInfo.expectedStatus = 'failed';
558  }
559}
560
561class SkipError extends Error {}
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Run JavaScript Tests on LambdaTest Cloud Grid

Execute automation tests with Playwright Internal on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)