workerMain.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.create = exports.WorkerMain = void 0;
  6. var _utilsBundle = require("playwright-core/lib/utilsBundle");
  7. var _util = require("../util");
  8. var _ipc = require("../common/ipc");
  9. var _globals = require("../common/globals");
  10. var _configLoader = require("../common/configLoader");
  11. var _fixtureRunner = require("./fixtureRunner");
  12. var _utils = require("playwright-core/lib/utils");
  13. var _testInfo = require("./testInfo");
  14. var _timeoutManager = require("./timeoutManager");
  15. var _process = require("../common/process");
  16. var _testLoader = require("../common/testLoader");
  17. var _suiteUtils = require("../common/suiteUtils");
  18. var _poolBuilder = require("../common/poolBuilder");
  19. /**
  20. * Copyright Microsoft Corporation. All rights reserved.
  21. *
  22. * Licensed under the Apache License, Version 2.0 (the "License");
  23. * you may not use this file except in compliance with the License.
  24. * You may obtain a copy of the License at
  25. *
  26. * http://www.apache.org/licenses/LICENSE-2.0
  27. *
  28. * Unless required by applicable law or agreed to in writing, software
  29. * distributed under the License is distributed on an "AS IS" BASIS,
  30. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  31. * See the License for the specific language governing permissions and
  32. * limitations under the License.
  33. */
  34. class WorkerMain extends _process.ProcessRunner {
  35. constructor(params) {
  36. super();
  37. this._params = void 0;
  38. this._config = void 0;
  39. this._project = void 0;
  40. this._poolBuilder = void 0;
  41. this._fixtureRunner = void 0;
  42. // Accumulated fatal errors that cannot be attributed to a test.
  43. this._fatalErrors = [];
  44. // Whether we should skip running remaining tests in this suite because
  45. // of a setup error, usually beforeAll hook.
  46. this._skipRemainingTestsInSuite = void 0;
  47. // The stage of the full cleanup. Once "finished", we can safely stop running anything.
  48. this._didRunFullCleanup = false;
  49. // Whether the worker was requested to stop.
  50. this._isStopped = false;
  51. // This promise resolves once the single "run test group" call finishes.
  52. this._runFinished = new _utils.ManualPromise();
  53. this._currentTest = null;
  54. this._lastRunningTests = [];
  55. this._totalRunningTests = 0;
  56. // Dynamic annotations originated by modifiers with a callback, e.g. `test.skip(() => true)`.
  57. this._extraSuiteAnnotations = new Map();
  58. // Suites that had their beforeAll hooks, but not afterAll hooks executed.
  59. // These suites still need afterAll hooks to be executed for the proper cleanup.
  60. this._activeSuites = new Set();
  61. process.env.TEST_WORKER_INDEX = String(params.workerIndex);
  62. process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex);
  63. process.env.TEST_ARTIFACTS_DIR = params.artifactsDir;
  64. (0, _globals.setIsWorkerProcess)();
  65. this._params = params;
  66. this._fixtureRunner = new _fixtureRunner.FixtureRunner();
  67. // Resolve this promise, so worker does not stall waiting for the non-existent run to finish,
  68. // when it was sopped before running any test group.
  69. this._runFinished.resolve();
  70. process.on('unhandledRejection', reason => this.unhandledError(reason));
  71. process.on('uncaughtException', error => this.unhandledError(error));
  72. process.stdout.write = chunk => {
  73. var _this$_currentTest;
  74. this.dispatchEvent('stdOut', (0, _ipc.stdioChunkToParams)(chunk));
  75. (_this$_currentTest = this._currentTest) === null || _this$_currentTest === void 0 ? void 0 : _this$_currentTest._tracing.appendStdioToTrace('stdout', chunk);
  76. return true;
  77. };
  78. if (!process.env.PW_RUNNER_DEBUG) {
  79. process.stderr.write = chunk => {
  80. var _this$_currentTest2;
  81. this.dispatchEvent('stdErr', (0, _ipc.stdioChunkToParams)(chunk));
  82. (_this$_currentTest2 = this._currentTest) === null || _this$_currentTest2 === void 0 ? void 0 : _this$_currentTest2._tracing.appendStdioToTrace('stderr', chunk);
  83. return true;
  84. };
  85. }
  86. }
  87. _stop() {
  88. if (!this._isStopped) {
  89. var _this$_currentTest3;
  90. this._isStopped = true;
  91. (_this$_currentTest3 = this._currentTest) === null || _this$_currentTest3 === void 0 ? void 0 : _this$_currentTest3._interrupt();
  92. }
  93. return this._runFinished;
  94. }
  95. async gracefullyClose() {
  96. try {
  97. await this._stop();
  98. // We have to load the project to get the right deadline below.
  99. await this._loadIfNeeded();
  100. await this._teardownScopes();
  101. // Close any other browsers launched in this process. This includes anything launched
  102. // manually in the test/hooks and internal browsers like Playwright Inspector.
  103. await (0, _utils.gracefullyCloseAll)();
  104. } catch (e) {
  105. this._fatalErrors.push((0, _util.serializeError)(e));
  106. }
  107. if (this._fatalErrors.length) {
  108. this._appendProcessTeardownDiagnostics(this._fatalErrors[this._fatalErrors.length - 1]);
  109. const payload = {
  110. fatalErrors: this._fatalErrors
  111. };
  112. this.dispatchEvent('teardownErrors', payload);
  113. }
  114. }
  115. _appendProcessTeardownDiagnostics(error) {
  116. if (!this._lastRunningTests.length) return;
  117. const count = this._totalRunningTests === 1 ? '1 test' : `${this._totalRunningTests} tests`;
  118. let lastMessage = '';
  119. if (this._lastRunningTests.length < this._totalRunningTests) lastMessage = `, last ${this._lastRunningTests.length} tests were`;
  120. const message = ['', '', _utilsBundle.colors.red(`Failed worker ran ${count}${lastMessage}:`), ...this._lastRunningTests.map(testInfo => formatTestTitle(testInfo._test, testInfo.project.name))].join('\n');
  121. if (error.message) {
  122. if (error.stack) {
  123. let index = error.stack.indexOf(error.message);
  124. if (index !== -1) {
  125. index += error.message.length;
  126. error.stack = error.stack.substring(0, index) + message + error.stack.substring(index);
  127. }
  128. }
  129. error.message += message;
  130. } else if (error.value) {
  131. error.value += message;
  132. }
  133. }
  134. async _teardownScopes() {
  135. // TODO: separate timeout for teardown?
  136. const timeoutManager = new _timeoutManager.TimeoutManager(this._project.project.timeout);
  137. await timeoutManager.withRunnable({
  138. type: 'teardown'
  139. }, async () => {
  140. const timeoutError = await timeoutManager.runWithTimeout(async () => {
  141. await this._fixtureRunner.teardownScope('test', timeoutManager);
  142. await this._fixtureRunner.teardownScope('worker', timeoutManager);
  143. });
  144. if (timeoutError) this._fatalErrors.push(timeoutError);
  145. });
  146. }
  147. unhandledError(error) {
  148. // No current test - fatal error.
  149. if (!this._currentTest) {
  150. if (!this._fatalErrors.length) this._fatalErrors.push((0, _util.serializeError)(error));
  151. void this._stop();
  152. return;
  153. }
  154. // We do not differentiate between errors in the control flow
  155. // and unhandled errors - both lead to the test failing. This is good for regular tests,
  156. // so that you can, e.g. expect() from inside an event handler. The test fails,
  157. // and we restart the worker.
  158. this._currentTest._failWithError(error, true /* isHardError */);
  159. // For tests marked with test.fail(), this might be a problem when unhandled error
  160. // is not coming from the user test code (legit failure), but from fixtures or test runner.
  161. //
  162. // Ideally, we would mark this test as "failed unexpectedly" and show that in the report.
  163. // However, we do not have such a special test status, so the test will be considered ok (failed as expected).
  164. //
  165. // To avoid messing up future tests, we forcefully stop the worker, unless it is
  166. // an expect() error which we know does not mess things up.
  167. const isExpectError = error instanceof Error && !!error.matcherResult;
  168. const shouldContinueInThisWorker = this._currentTest.expectedStatus === 'failed' && isExpectError;
  169. if (!shouldContinueInThisWorker) void this._stop();
  170. }
  171. async _loadIfNeeded() {
  172. if (this._config) return;
  173. this._config = await _configLoader.ConfigLoader.deserialize(this._params.config);
  174. this._project = this._config.projects.find(p => p.id === this._params.projectId);
  175. this._poolBuilder = _poolBuilder.PoolBuilder.createForWorker(this._project);
  176. }
  177. async runTestGroup(runPayload) {
  178. this._runFinished = new _utils.ManualPromise();
  179. const entries = new Map(runPayload.entries.map(e => [e.testId, e]));
  180. let fatalUnknownTestIds;
  181. try {
  182. await this._loadIfNeeded();
  183. const fileSuite = await (0, _testLoader.loadTestFile)(runPayload.file, this._config.config.rootDir);
  184. const suite = (0, _suiteUtils.bindFileSuiteToProject)(this._project, fileSuite);
  185. if (this._params.repeatEachIndex) (0, _suiteUtils.applyRepeatEachIndex)(this._project, suite, this._params.repeatEachIndex);
  186. const hasEntries = (0, _suiteUtils.filterTestsRemoveEmptySuites)(suite, test => entries.has(test.id));
  187. if (hasEntries) {
  188. this._poolBuilder.buildPools(suite);
  189. this._extraSuiteAnnotations = new Map();
  190. this._activeSuites = new Set();
  191. this._didRunFullCleanup = false;
  192. const tests = suite.allTests();
  193. for (let i = 0; i < tests.length; i++) {
  194. // Do not run tests after full cleanup, because we are entirely done.
  195. if (this._isStopped && this._didRunFullCleanup) break;
  196. const entry = entries.get(tests[i].id);
  197. entries.delete(tests[i].id);
  198. (0, _util.debugTest)(`test started "${tests[i].title}"`);
  199. await this._runTest(tests[i], entry.retry, tests[i + 1]);
  200. (0, _util.debugTest)(`test finished "${tests[i].title}"`);
  201. }
  202. } else {
  203. fatalUnknownTestIds = runPayload.entries.map(e => e.testId);
  204. void this._stop();
  205. }
  206. } catch (e) {
  207. // In theory, we should run above code without any errors.
  208. // However, in the case we screwed up, or loadTestFile failed in the worker
  209. // but not in the runner, let's do a fatal error.
  210. this._fatalErrors.push((0, _util.serializeError)(e));
  211. void this._stop();
  212. } finally {
  213. const donePayload = {
  214. fatalErrors: this._fatalErrors,
  215. skipTestsDueToSetupFailure: [],
  216. fatalUnknownTestIds
  217. };
  218. for (const test of ((_this$_skipRemainingT = this._skipRemainingTestsInSuite) === null || _this$_skipRemainingT === void 0 ? void 0 : _this$_skipRemainingT.allTests()) || []) {
  219. var _this$_skipRemainingT;
  220. if (entries.has(test.id)) donePayload.skipTestsDueToSetupFailure.push(test.id);
  221. }
  222. this.dispatchEvent('done', donePayload);
  223. this._fatalErrors = [];
  224. this._skipRemainingTestsInSuite = undefined;
  225. this._runFinished.resolve();
  226. }
  227. }
  228. async _runTest(test, retry, nextTest) {
  229. const testInfo = new _testInfo.TestInfoImpl(this._config, this._project, this._params, test, retry, stepBeginPayload => this.dispatchEvent('stepBegin', stepBeginPayload), stepEndPayload => this.dispatchEvent('stepEnd', stepEndPayload), attachment => this.dispatchEvent('attach', attachment));
  230. const processAnnotation = annotation => {
  231. testInfo.annotations.push(annotation);
  232. switch (annotation.type) {
  233. case 'fixme':
  234. case 'skip':
  235. testInfo.expectedStatus = 'skipped';
  236. break;
  237. case 'fail':
  238. if (testInfo.expectedStatus !== 'skipped') testInfo.expectedStatus = 'failed';
  239. break;
  240. case 'slow':
  241. testInfo.slow();
  242. break;
  243. }
  244. };
  245. if (!this._isStopped) this._fixtureRunner.setPool(test._pool);
  246. const suites = getSuites(test);
  247. const reversedSuites = suites.slice().reverse();
  248. const nextSuites = new Set(getSuites(nextTest));
  249. testInfo._timeoutManager.setTimeout(test.timeout);
  250. for (const annotation of test._staticAnnotations) processAnnotation(annotation);
  251. // Process existing annotations dynamically set for parent suites.
  252. for (const suite of suites) {
  253. const extraAnnotations = this._extraSuiteAnnotations.get(suite) || [];
  254. for (const annotation of extraAnnotations) processAnnotation(annotation);
  255. }
  256. this._currentTest = testInfo;
  257. (0, _globals.setCurrentTestInfo)(testInfo);
  258. this.dispatchEvent('testBegin', buildTestBeginPayload(testInfo));
  259. const isSkipped = testInfo.expectedStatus === 'skipped';
  260. const hasAfterAllToRunBeforeNextTest = reversedSuites.some(suite => {
  261. return this._activeSuites.has(suite) && !nextSuites.has(suite) && suite._hooks.some(hook => hook.type === 'afterAll');
  262. });
  263. if (isSkipped && nextTest && !hasAfterAllToRunBeforeNextTest) {
  264. // Fast path - this test is skipped, and there are more tests that will handle cleanup.
  265. testInfo.status = 'skipped';
  266. this.dispatchEvent('testEnd', buildTestEndPayload(testInfo));
  267. return;
  268. }
  269. this._totalRunningTests++;
  270. this._lastRunningTests.push(testInfo);
  271. if (this._lastRunningTests.length > 10) this._lastRunningTests.shift();
  272. let didFailBeforeAllForSuite;
  273. let shouldRunAfterEachHooks = false;
  274. await testInfo._runWithTimeout(async () => {
  275. if (this._isStopped || isSkipped) {
  276. // Two reasons to get here:
  277. // - Last test is skipped, so we should not run the test, but run the cleanup.
  278. // - Worker is requested to stop, but was not able to run full cleanup yet.
  279. // We should skip the test, but run the cleanup.
  280. testInfo.status = 'skipped';
  281. didFailBeforeAllForSuite = undefined;
  282. return;
  283. }
  284. await (0, _utils.removeFolders)([testInfo.outputDir]);
  285. let testFunctionParams = null;
  286. await testInfo._runAsStep({
  287. category: 'hook',
  288. title: 'Before Hooks'
  289. }, async step => {
  290. testInfo._beforeHooksStep = step;
  291. // Note: wrap all preparation steps together, because failure/skip in any of them
  292. // prevents further setup and/or test from running.
  293. const beforeHooksError = await testInfo._runAndFailOnError(async () => {
  294. // Run "beforeAll" modifiers on parent suites, unless already run during previous tests.
  295. for (const suite of suites) {
  296. if (this._extraSuiteAnnotations.has(suite)) continue;
  297. const extraAnnotations = [];
  298. this._extraSuiteAnnotations.set(suite, extraAnnotations);
  299. didFailBeforeAllForSuite = suite; // Assume failure, unless reset below.
  300. // Separate timeout for each "beforeAll" modifier.
  301. const timeSlot = {
  302. timeout: this._project.project.timeout,
  303. elapsed: 0
  304. };
  305. await this._runModifiersForSuite(suite, testInfo, 'worker', timeSlot, extraAnnotations);
  306. }
  307. // Run "beforeAll" hooks, unless already run during previous tests.
  308. for (const suite of suites) {
  309. didFailBeforeAllForSuite = suite; // Assume failure, unless reset below.
  310. await this._runBeforeAllHooksForSuite(suite, testInfo);
  311. }
  312. // Running "beforeAll" succeeded for all suites!
  313. didFailBeforeAllForSuite = undefined;
  314. // Run "beforeEach" modifiers.
  315. for (const suite of suites) await this._runModifiersForSuite(suite, testInfo, 'test', undefined);
  316. // Run "beforeEach" hooks. Once started with "beforeEach", we must run all "afterEach" hooks as well.
  317. shouldRunAfterEachHooks = true;
  318. await this._runEachHooksForSuites(suites, 'beforeEach', testInfo);
  319. // Setup fixtures required by the test.
  320. testFunctionParams = await this._fixtureRunner.resolveParametersForFunction(test.fn, testInfo, 'test');
  321. }, 'allowSkips');
  322. if (beforeHooksError) step.complete({
  323. error: beforeHooksError
  324. });
  325. });
  326. if (testFunctionParams === null) {
  327. // Fixture setup failed, we should not run the test now.
  328. return;
  329. }
  330. await testInfo._runAndFailOnError(async () => {
  331. // Now run the test itself.
  332. (0, _util.debugTest)(`test function started`);
  333. const fn = test.fn; // Extract a variable to get a better stack trace ("myTest" vs "TestCase.myTest [as fn]").
  334. await fn(testFunctionParams, testInfo);
  335. (0, _util.debugTest)(`test function finished`);
  336. }, 'allowSkips');
  337. for (const error of testInfo.errors) testInfo._tracing.appendForError(error);
  338. });
  339. if (didFailBeforeAllForSuite) {
  340. // This will inform dispatcher that we should not run more tests from this group
  341. // because we had a beforeAll error.
  342. // This behavior avoids getting the same common error for each test.
  343. this._skipRemainingTestsInSuite = didFailBeforeAllForSuite;
  344. }
  345. // A timed-out test gets a full additional timeout to run after hooks.
  346. const afterHooksSlot = testInfo._didTimeout ? {
  347. timeout: this._project.project.timeout,
  348. elapsed: 0
  349. } : undefined;
  350. await testInfo._runAsStepWithRunnable({
  351. category: 'hook',
  352. title: 'After Hooks',
  353. runnableType: 'afterHooks',
  354. runnableSlot: afterHooksSlot
  355. }, async step => {
  356. testInfo._afterHooksStep = step;
  357. let firstAfterHooksError;
  358. await testInfo._runWithTimeout(async () => {
  359. // Note: do not wrap all teardown steps together, because failure in any of them
  360. // does not prevent further teardown steps from running.
  361. // Run "immediately upon test function finish" callback.
  362. (0, _util.debugTest)(`on-test-function-finish callback started`);
  363. const didFinishTestFunctionError = await testInfo._runAndFailOnError(async () => {
  364. var _testInfo$_onDidFinis;
  365. return (_testInfo$_onDidFinis = testInfo._onDidFinishTestFunction) === null || _testInfo$_onDidFinis === void 0 ? void 0 : _testInfo$_onDidFinis.call(testInfo);
  366. });
  367. firstAfterHooksError = firstAfterHooksError || didFinishTestFunctionError;
  368. (0, _util.debugTest)(`on-test-function-finish callback finished`);
  369. // Run "afterEach" hooks, unless we failed at beforeAll stage.
  370. if (shouldRunAfterEachHooks) {
  371. const afterEachError = await testInfo._runAndFailOnError(() => this._runEachHooksForSuites(reversedSuites, 'afterEach', testInfo));
  372. firstAfterHooksError = firstAfterHooksError || afterEachError;
  373. }
  374. // Teardown test-scoped fixtures. Attribute to 'test' so that users understand
  375. // they should probably increase the test timeout to fix this issue.
  376. (0, _util.debugTest)(`tearing down test scope started`);
  377. const testScopeError = await testInfo._runAndFailOnError(() => {
  378. return this._fixtureRunner.teardownScope('test', testInfo._timeoutManager);
  379. });
  380. (0, _util.debugTest)(`tearing down test scope finished`);
  381. firstAfterHooksError = firstAfterHooksError || testScopeError;
  382. // Run "afterAll" hooks for suites that are not shared with the next test.
  383. // In case of failure the worker will be stopped and we have to make sure that afterAll
  384. // hooks run before worker fixtures teardown.
  385. for (const suite of reversedSuites) {
  386. if (!nextSuites.has(suite) || testInfo._isFailure()) {
  387. const afterAllError = await this._runAfterAllHooksForSuite(suite, testInfo);
  388. firstAfterHooksError = firstAfterHooksError || afterAllError;
  389. }
  390. }
  391. });
  392. if (testInfo._isFailure()) this._isStopped = true;
  393. if (this._isStopped) {
  394. // Run all remaining "afterAll" hooks and teardown all fixtures when worker is shutting down.
  395. // Mark as "cleaned up" early to avoid running cleanup twice.
  396. this._didRunFullCleanup = true;
  397. // Give it more time for the full cleanup.
  398. await testInfo._runWithTimeout(async () => {
  399. (0, _util.debugTest)(`running full cleanup after the failure`);
  400. const teardownSlot = {
  401. timeout: this._project.project.timeout,
  402. elapsed: 0
  403. };
  404. await testInfo._timeoutManager.withRunnable({
  405. type: 'teardown',
  406. slot: teardownSlot
  407. }, async () => {
  408. // Attribute to 'test' so that users understand they should probably increate the test timeout to fix this issue.
  409. (0, _util.debugTest)(`tearing down test scope started`);
  410. const testScopeError = await testInfo._runAndFailOnError(() => {
  411. return this._fixtureRunner.teardownScope('test', testInfo._timeoutManager);
  412. });
  413. (0, _util.debugTest)(`tearing down test scope finished`);
  414. firstAfterHooksError = firstAfterHooksError || testScopeError;
  415. for (const suite of reversedSuites) {
  416. const afterAllError = await this._runAfterAllHooksForSuite(suite, testInfo);
  417. firstAfterHooksError = firstAfterHooksError || afterAllError;
  418. }
  419. // Attribute to 'teardown' because worker fixtures are not perceived as a part of a test.
  420. (0, _util.debugTest)(`tearing down worker scope started`);
  421. const workerScopeError = await testInfo._runAndFailOnError(() => {
  422. return this._fixtureRunner.teardownScope('worker', testInfo._timeoutManager);
  423. });
  424. (0, _util.debugTest)(`tearing down worker scope finished`);
  425. firstAfterHooksError = firstAfterHooksError || workerScopeError;
  426. });
  427. });
  428. }
  429. if (firstAfterHooksError) step.complete({
  430. error: firstAfterHooksError
  431. });
  432. });
  433. this._currentTest = null;
  434. (0, _globals.setCurrentTestInfo)(null);
  435. this.dispatchEvent('testEnd', buildTestEndPayload(testInfo));
  436. const preserveOutput = this._config.config.preserveOutput === 'always' || this._config.config.preserveOutput === 'failures-only' && testInfo._isFailure();
  437. if (!preserveOutput) await (0, _utils.removeFolders)([testInfo.outputDir]);
  438. }
  439. async _runModifiersForSuite(suite, testInfo, scope, timeSlot, extraAnnotations) {
  440. for (const modifier of suite._modifiers) {
  441. const actualScope = this._fixtureRunner.dependsOnWorkerFixturesOnly(modifier.fn, modifier.location) ? 'worker' : 'test';
  442. if (actualScope !== scope) continue;
  443. (0, _util.debugTest)(`modifier at "${(0, _util.formatLocation)(modifier.location)}" started`);
  444. const result = await testInfo._runAsStepWithRunnable({
  445. category: 'hook',
  446. title: `${modifier.type} modifier`,
  447. location: modifier.location,
  448. runnableType: modifier.type,
  449. runnableSlot: timeSlot
  450. }, () => this._fixtureRunner.resolveParametersAndRunFunction(modifier.fn, testInfo, scope));
  451. (0, _util.debugTest)(`modifier at "${(0, _util.formatLocation)(modifier.location)}" finished`);
  452. if (result && extraAnnotations) extraAnnotations.push({
  453. type: modifier.type,
  454. description: modifier.description
  455. });
  456. testInfo[modifier.type](!!result, modifier.description);
  457. }
  458. }
  459. async _runBeforeAllHooksForSuite(suite, testInfo) {
  460. if (this._activeSuites.has(suite)) return;
  461. this._activeSuites.add(suite);
  462. let beforeAllError;
  463. for (const hook of suite._hooks) {
  464. if (hook.type !== 'beforeAll') continue;
  465. (0, _util.debugTest)(`${hook.type} hook at "${(0, _util.formatLocation)(hook.location)}" started`);
  466. try {
  467. // Separate time slot for each "beforeAll" hook.
  468. const timeSlot = {
  469. timeout: this._project.project.timeout,
  470. elapsed: 0
  471. };
  472. await testInfo._runAsStepWithRunnable({
  473. category: 'hook',
  474. title: `${hook.title}`,
  475. location: hook.location,
  476. runnableType: 'beforeAll',
  477. runnableSlot: timeSlot
  478. }, async () => {
  479. try {
  480. await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, 'all-hooks-only');
  481. } finally {
  482. // Each beforeAll hook has its own scope for test fixtures. Attribute to the same runnable and timeSlot.
  483. // Note: we must teardown even after beforeAll fails, because we'll run more beforeAlls.
  484. await this._fixtureRunner.teardownScope('test', testInfo._timeoutManager);
  485. }
  486. });
  487. } catch (e) {
  488. // Always run all the hooks, and capture the first error.
  489. beforeAllError = beforeAllError || e;
  490. }
  491. (0, _util.debugTest)(`${hook.type} hook at "${(0, _util.formatLocation)(hook.location)}" finished`);
  492. }
  493. if (beforeAllError) throw beforeAllError;
  494. }
  495. async _runAfterAllHooksForSuite(suite, testInfo) {
  496. if (!this._activeSuites.has(suite)) return;
  497. this._activeSuites.delete(suite);
  498. let firstError;
  499. for (const hook of suite._hooks) {
  500. if (hook.type !== 'afterAll') continue;
  501. (0, _util.debugTest)(`${hook.type} hook at "${(0, _util.formatLocation)(hook.location)}" started`);
  502. const afterAllError = await testInfo._runAndFailOnError(async () => {
  503. // Separate time slot for each "afterAll" hook.
  504. const timeSlot = {
  505. timeout: this._project.project.timeout,
  506. elapsed: 0
  507. };
  508. await testInfo._runAsStepWithRunnable({
  509. category: 'hook',
  510. title: `${hook.title}`,
  511. location: hook.location,
  512. runnableType: 'afterAll',
  513. runnableSlot: timeSlot
  514. }, async () => {
  515. try {
  516. await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, 'all-hooks-only');
  517. } finally {
  518. // Each afterAll hook has its own scope for test fixtures. Attribute to the same runnable and timeSlot.
  519. // Note: we must teardown even after afterAll fails, because we'll run more afterAlls.
  520. await this._fixtureRunner.teardownScope('test', testInfo._timeoutManager);
  521. }
  522. });
  523. });
  524. firstError = firstError || afterAllError;
  525. (0, _util.debugTest)(`${hook.type} hook at "${(0, _util.formatLocation)(hook.location)}" finished`);
  526. }
  527. return firstError;
  528. }
  529. async _runEachHooksForSuites(suites, type, testInfo) {
  530. const hooks = suites.map(suite => suite._hooks.filter(hook => hook.type === type)).flat();
  531. let error;
  532. for (const hook of hooks) {
  533. try {
  534. await testInfo._runAsStepWithRunnable({
  535. category: 'hook',
  536. title: `${hook.title}`,
  537. location: hook.location,
  538. runnableType: type
  539. }, () => this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, 'test'));
  540. } catch (e) {
  541. // Always run all the hooks, and capture the first error.
  542. error = error || e;
  543. }
  544. }
  545. if (error) throw error;
  546. }
  547. }
  548. exports.WorkerMain = WorkerMain;
  549. function buildTestBeginPayload(testInfo) {
  550. return {
  551. testId: testInfo._test.id,
  552. startWallTime: testInfo._startWallTime
  553. };
  554. }
  555. function buildTestEndPayload(testInfo) {
  556. return {
  557. testId: testInfo._test.id,
  558. duration: testInfo.duration,
  559. status: testInfo.status,
  560. errors: testInfo.errors,
  561. expectedStatus: testInfo.expectedStatus,
  562. annotations: testInfo.annotations,
  563. timeout: testInfo.timeout
  564. };
  565. }
  566. function getSuites(test) {
  567. const suites = [];
  568. for (let suite = test === null || test === void 0 ? void 0 : test.parent; suite; suite = suite.parent) suites.push(suite);
  569. suites.reverse(); // Put root suite first.
  570. return suites;
  571. }
  572. function formatTestTitle(test, projectName) {
  573. // file, ...describes, test
  574. const [, ...titles] = test.titlePath();
  575. const location = `${(0, _util.relativeFilePath)(test.location.file)}:${test.location.line}:${test.location.column}`;
  576. const projectTitle = projectName ? `[${projectName}] › ` : '';
  577. return `${projectTitle}${location} › ${titles.join(' › ')}`;
  578. }
  579. const create = params => new WorkerMain(params);
  580. exports.create = create;