@zohodesk/testinglibrary 0.0.65-n20-experimental → 0.0.68-n20-experimental

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _caseTimeoutResolver = require("../helpers/caseTimeoutResolver");
8
+ var _default = exports.default = {
9
+ caseTimeout: [async ({
10
+ $tags
11
+ }, use, testInfo) => {
12
+ const timeoutMs = (0, _caseTimeoutResolver.resolveCaseTimeoutMs)($tags, testInfo.project.name);
13
+ if (timeoutMs !== null) {
14
+ testInfo.setTimeout(timeoutMs);
15
+ }
16
+ await use();
17
+ }, {
18
+ auto: true
19
+ }]
20
+ };
@@ -1,22 +1,13 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
3
  Object.defineProperty(exports, "__esModule", {
5
4
  value: true
6
5
  });
7
6
  exports.default = void 0;
8
- var _path = _interopRequireDefault(require("path"));
9
7
  var _readConfigFile = require("../readConfigFile");
10
- var _logCollector = require("../helpers/logCollector");
11
8
  const {
12
- testSetup,
13
- uatDirectory,
14
- runtimeLogs = {}
9
+ testSetup
15
10
  } = (0, _readConfigFile.generateConfigFromFile)();
16
-
17
- // Keep runtime logs OUTSIDE reportPath (HTML reporter wipes it on onEnd) and
18
- // outside test-results (Playwright outputDir is cleaned each run).
19
- const LOGS_DIR = _path.default.join(uatDirectory, 'runtime-logs');
20
11
  async function performDefaultContextSteps({
21
12
  context
22
13
  }) {
@@ -29,37 +20,13 @@ async function performDefaultContextSteps({
29
20
  var _default = exports.default = {
30
21
  context: async ({
31
22
  context
32
- }, use, testInfo) => {
23
+ }, use) => {
33
24
  await context.addInitScript(() =>
34
25
  // eslint-disable-next-line no-undef
35
26
  window.localStorage.setItem('isDnBannerHide', true));
36
27
  await performDefaultContextSteps({
37
28
  context
38
29
  });
39
- const collector = new _logCollector.LogCollector(runtimeLogs.limits);
40
- collector.attachToContext(context);
41
- testInfo.runtimeLogs = collector;
42
- try {
43
- await use(context);
44
- } finally {
45
- collector.detach();
46
- const failureOnly = runtimeLogs.mode === 'failure-only';
47
- const isFailure = testInfo.status && testInfo.status !== testInfo.expectedStatus;
48
- const shouldFlush = !failureOnly || isFailure;
49
- const summary = collector.toSummary();
50
- let filePath = null;
51
- if (shouldFlush) {
52
- const base = `w${testInfo.workerIndex}-${testInfo.testId}-${testInfo.retry}`;
53
- filePath = collector.flushToFile(LOGS_DIR, base);
54
- }
55
- await testInfo.attach('runtime-log', {
56
- contentType: 'application/json',
57
- body: Buffer.from(JSON.stringify({
58
- schemaVersion: 1,
59
- summary,
60
- file: filePath
61
- }))
62
- });
63
- }
30
+ await use(context);
64
31
  }
65
32
  };
@@ -9,6 +9,7 @@ var _page = _interopRequireDefault(require("./page"));
9
9
  var _context = _interopRequireDefault(require("./context"));
10
10
  var _cacheLayer = _interopRequireDefault(require("./cacheLayer"));
11
11
  var _addTags = _interopRequireDefault(require("./addTags"));
12
+ var _caseTimeout = _interopRequireDefault(require("./caseTimeout"));
12
13
  var _i18N = _interopRequireDefault(require("./i18N"));
13
14
  var _unauthenticatedPage = _interopRequireDefault(require("./unauthenticatedPage"));
14
15
  var _executionContext = _interopRequireDefault(require("./executionContext"));
@@ -24,7 +25,8 @@ function getBuiltInFixtures(bddMode) {
24
25
  ..._cacheLayer.default,
25
26
  ..._i18N.default,
26
27
  ..._unauthenticatedPage.default,
27
- ..._executionContext.default
28
+ ..._executionContext.default,
29
+ ..._caseTimeout.default
28
30
  };
29
31
  if (bddMode) {
30
32
  builtInFixtures = {
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getBrowserTimeoutFactor = getBrowserTimeoutFactor;
7
+ var _browserTypes = require("../constants/browserTypes");
8
+ function getBrowserTimeoutFactor(projectName) {
9
+ if (projectName === _browserTypes.BROWSER_PROJECT_MAPPING.FIREFOX) {
10
+ return 2;
11
+ }
12
+ if (projectName === _browserTypes.BROWSER_PROJECT_MAPPING.SAFARI) {
13
+ return 4;
14
+ }
15
+ return 1;
16
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.TIMEOUT_TAG_PATTERN = void 0;
7
+ exports.findTimeoutTags = findTimeoutTags;
8
+ exports.parseTimeoutSeconds = parseTimeoutSeconds;
9
+ exports.resolveCaseTimeoutMs = resolveCaseTimeoutMs;
10
+ var _logger = require("../../../utils/logger");
11
+ var _browserTimeoutFactor = require("./browserTimeoutFactor");
12
+ const TIMEOUT_TAG_PATTERN = exports.TIMEOUT_TAG_PATTERN = /^@timeout_(\d+)$/;
13
+ function findTimeoutTags(tags) {
14
+ if (!Array.isArray(tags)) {
15
+ return [];
16
+ }
17
+ return tags.filter(tag => typeof tag === 'string' && TIMEOUT_TAG_PATTERN.test(tag));
18
+ }
19
+ function parseTimeoutSeconds(tag) {
20
+ const match = tag.match(TIMEOUT_TAG_PATTERN);
21
+ return match ? Number(match[1]) : null;
22
+ }
23
+ function resolveCaseTimeoutMs(tags, projectName) {
24
+ const matched = findTimeoutTags(tags);
25
+ if (matched.length === 0) {
26
+ return null;
27
+ }
28
+ if (matched.length > 1) {
29
+ _logger.Logger.log(_logger.Logger.INFO_TYPE, `Multiple @timeout_* tags found (${matched.join(', ')}). Using the first one: ${matched[0]}`);
30
+ }
31
+ const seconds = parseTimeoutSeconds(matched[0]);
32
+ if (seconds === 0) {
33
+ return 0;
34
+ }
35
+ const factor = (0, _browserTimeoutFactor.getBrowserTimeoutFactor)(projectName);
36
+ return seconds * 1000 * factor;
37
+ }
@@ -55,11 +55,7 @@ function getDefaultConfig() {
55
55
  featureFilesFolder: 'feature-files',
56
56
  stepDefinitionsFolder: 'steps',
57
57
  testSetup: {},
58
- editionOrder: ['Free', 'Express', 'Standard', 'Professional', 'Enterprise'],
59
- showCaseTimings: true,
60
- runtimeLogs: {
61
- mode: 'failure-only'
62
- }
58
+ editionOrder: ['Free', 'Express', 'Standard', 'Professional', 'Enterprise']
63
59
  };
64
60
  }
65
61
  function combineDefaultConfigWithUserConfig(userConfiguration) {
@@ -109,7 +105,7 @@ function combineDefaultConfigWithUserConfig(userConfiguration) {
109
105
  * @property {any} reportPath : directory where report is generate
110
106
  * @property {boolean} bddMode: Feature files needs to be processed
111
107
  * @property {number} expectTimeout: time in milliseconds which the expect condition should fail
112
- * @property {number} testTimeout: time in milliseconds which the test should fail
108
+ * @property {number} testTimeout: time in milliseconds which the test should fail. Can be overridden per-scenario by tagging the scenario with `@timeout_<seconds>` (e.g. `@timeout_180`). `@timeout_0` disables the timeout. Browser scaling (Firefox 2x, Safari 4x) is preserved.
113
109
  * @property {Object} additionalPages: custom pages configuration
114
110
  * @property {string} featureFilesFolder: folder name under which feature-files will be placed. Default is feature-files
115
111
  * @property {string} stepDefinitionsFolder: folder name under which step implementations will be placed. Default is steps
@@ -117,8 +113,6 @@ function combineDefaultConfigWithUserConfig(userConfiguration) {
117
113
  * @property {string} testIdAttribute: Change the default data-testid attribute. configure what attribute to search while calling getByTestId
118
114
  * @property {Array} editionOrder: Order in the form of larger editions in the back. Edition with the most privelages should be last
119
115
  * @property {testSetupConfig} testSetup: Specify page and context functions that will be called while intilaizing fixtures.
120
- * @property {boolean} showCaseTimings: When true, the console reporter prints per-case start/end wall-clock times (h:mm:ss AM/PM) and a case-timings.json sidecar is written to reportPath for the HTML report addon. Default: true.
121
- * @property {Object} runtimeLogs: Runtime browser log capture. { mode: 'failure-only' | 'always', limits?: { maxEvents, captureHeaders, ignoreUrlPatterns } }. Default: { mode: 'failure-only' }.
122
116
  */
123
117
 
124
118
  /**
@@ -15,6 +15,7 @@ var _readConfigFile = require("../readConfigFile");
15
15
  var _playwrightBdd = require("playwright-bdd");
16
16
  var _logger = require("../../../utils/logger");
17
17
  var _browserTypes = require("../constants/browserTypes");
18
+ var _browserTimeoutFactor = require("../helpers/browserTimeoutFactor");
18
19
  var _fileUtils = require("../../../utils/fileUtils");
19
20
  var _ConfigurationHelper = require("../configuration/ConfigurationHelper");
20
21
  var _configConstants = _interopRequireDefault(require("../constants/configConstants"));
@@ -69,11 +70,12 @@ function getBrowserConfig({
69
70
  dependencies
70
71
  };
71
72
  } else if (browser === 'firefox') {
73
+ const factor = (0, _browserTimeoutFactor.getBrowserTimeoutFactor)(_browserTypes.BROWSER_PROJECT_MAPPING.FIREFOX);
72
74
  return {
73
75
  name: _browserTypes.BROWSER_PROJECT_MAPPING.FIREFOX,
74
- timeout: 2 * testTimeout,
76
+ timeout: factor * testTimeout,
75
77
  expect: {
76
- timeout: 2 * expectTimeout
78
+ timeout: factor * expectTimeout
77
79
  },
78
80
  use: {
79
81
  ..._test.devices['Desktop Firefox'],
@@ -82,11 +84,12 @@ function getBrowserConfig({
82
84
  dependencies
83
85
  };
84
86
  } else if (browser === 'safari') {
87
+ const factor = (0, _browserTimeoutFactor.getBrowserTimeoutFactor)(_browserTypes.BROWSER_PROJECT_MAPPING.SAFARI);
85
88
  return {
86
89
  name: _browserTypes.BROWSER_PROJECT_MAPPING.SAFARI,
87
- timeout: 4 * testTimeout,
90
+ timeout: factor * testTimeout,
88
91
  expect: {
89
- timeout: 4 * expectTimeout
92
+ timeout: factor * expectTimeout
90
93
  },
91
94
  use: {
92
95
  ..._test.devices['Desktop Safari'],
@@ -12,22 +12,6 @@ var _readConfigFile = require("../readConfigFile");
12
12
  var _logger = require("../../../utils/logger");
13
13
  var _configFileNameProvider = require("../helpers/configFileNameProvider");
14
14
  var _mergeAbortedTests = _interopRequireDefault(require("../reporter/helpers/mergeAbortedTests"));
15
- function formatTime(date) {
16
- return date.toLocaleTimeString('en-US', {
17
- hour: 'numeric',
18
- minute: '2-digit',
19
- second: '2-digit',
20
- hour12: true
21
- });
22
- }
23
- function formatDuration(ms) {
24
- if (ms < 1000) return `${ms}ms`;
25
- const totalSeconds = ms / 1000;
26
- if (totalSeconds < 60) return `${totalSeconds.toFixed(2)}s`;
27
- const minutes = Math.floor(totalSeconds / 60);
28
- const seconds = (totalSeconds - minutes * 60).toFixed(2);
29
- return `${minutes}m ${seconds}s`;
30
- }
31
15
  class JSONSummaryReporter {
32
16
  constructor() {
33
17
  this.durationInMS = -1;
@@ -42,22 +26,11 @@ class JSONSummaryReporter {
42
26
  this.failedSteps = [];
43
27
  this.status = 'unknown';
44
28
  this.startedAt = 0;
45
- const config = (0, _readConfigFile.generateConfigFromFile)();
46
- this._open = config.openReportOn;
47
- this._showCaseTimings = config.showCaseTimings !== false;
48
- this._caseTimings = [];
29
+ this._open = (0, _readConfigFile.generateConfigFromFile)().openReportOn;
49
30
  }
50
31
  onBegin() {
51
32
  this.startedAt = Date.now();
52
33
  }
53
- onTestBegin(test) {
54
- if (!this._showCaseTimings) return;
55
- const {
56
- fullTitle
57
- } = this.getTitle(test);
58
- const startedAtLabel = formatTime(new Date());
59
- _logger.Logger.log(_logger.Logger.INFO_TYPE, `▶ ${fullTitle} — started at ${startedAtLabel}`);
60
- }
61
34
  getTitle(test) {
62
35
  const title = [];
63
36
  const fileName = [];
@@ -103,55 +76,6 @@ class JSONSummaryReporter {
103
76
  this[status].push(fileName);
104
77
  }
105
78
  this[status].push(fileName);
106
- if (this._showCaseTimings && result.startTime) {
107
- const startDate = new Date(result.startTime);
108
- const endDate = new Date(startDate.getTime() + (result.duration || 0));
109
- const startLabel = formatTime(startDate);
110
- const endLabel = formatTime(endDate);
111
- const durationLabel = formatDuration(result.duration || 0);
112
- const statusGlyph = result.status === 'passed' ? '✓' : result.status === 'skipped' ? '○' : '✗';
113
- const logType = result.status === 'passed' ? _logger.Logger.SUCCESS_TYPE : result.status === 'skipped' ? _logger.Logger.INFO_TYPE : _logger.Logger.FAILURE_TYPE;
114
- _logger.Logger.log(logType, `${statusGlyph} ${fullTitle} — ended at ${endLabel} (started ${startLabel}, took ${durationLabel})`);
115
- const isFailure = result.status !== 'passed' && result.status !== 'skipped';
116
- if (isFailure) {
117
- var _result$error;
118
- const runtimeAttachment = (result.attachments || []).find(a => a.name === 'runtime-log' && a.body);
119
- let runtime = null;
120
- if (runtimeAttachment) {
121
- try {
122
- runtime = JSON.parse(runtimeAttachment.body.toString('utf8'));
123
- } catch {
124
- // malformed attachment; skip
125
- }
126
- }
127
- this._caseTimings.push({
128
- title: fullTitle,
129
- fileName,
130
- status: result.status,
131
- retry: result.retry,
132
- startTime: startDate.toISOString(),
133
- endTime: endDate.toISOString(),
134
- startTimeFormatted: startLabel,
135
- endTimeFormatted: endLabel,
136
- duration: result.duration || 0,
137
- durationFormatted: durationLabel,
138
- errorMessage: (_result$error = result.error) === null || _result$error === void 0 ? void 0 : _result$error.message,
139
- failedSteps: (result.steps || []).filter(step => step.error).map(step => {
140
- var _step$error;
141
- return {
142
- title: step.title,
143
- error: (_step$error = step.error) === null || _step$error === void 0 ? void 0 : _step$error.message
144
- };
145
- }),
146
- ...(runtime ? {
147
- runtime: {
148
- summary: runtime.summary,
149
- logFile: runtime.file
150
- }
151
- } : {})
152
- });
153
- }
154
- }
155
79
  }
156
80
  onError(error) {
157
81
  this.errored.push({
@@ -198,16 +122,6 @@ class JSONSummaryReporter {
198
122
  reportPath
199
123
  } = (0, _readConfigFile.generateConfigFromFile)();
200
124
  (0, _fileUtils.writeFileContents)(_path.default.join(reportPath, './', (0, _configFileNameProvider.getReportFileName)()), JSON.stringify(this, null, ' '));
201
- if (this._showCaseTimings && this._caseTimings.length > 0) {
202
- const timingsPayload = {
203
- suiteStartedAt: new Date(this.startedAt).toISOString(),
204
- suiteEndedAt: new Date(this.startedAt + this.durationInMS).toISOString(),
205
- suiteDurationMs: this.durationInMS,
206
- failedCount: this._caseTimings.length,
207
- cases: this._caseTimings
208
- };
209
- (0, _fileUtils.writeFileContents)(_path.default.join(reportPath, 'case-timings.json'), JSON.stringify(timingsPayload, null, ' '));
210
- }
211
125
  }
212
126
  onExit() {
213
127
  // Update .last-run.json with aborted tests due to timing out or interruption
@@ -31,7 +31,7 @@
31
31
  * @property {any} reportPath : directory where report is generate
32
32
  * @property {boolean} bddMode: Feature files needs to be processed
33
33
  * @property {number} expectTimeout: time in milliseconds which the expect condition should fail
34
- * @property {number} testTimeout: time in milliseconds which the test should fail
34
+ * @property {number} testTimeout: time in milliseconds which the test should fail. Can be overridden per-scenario by tagging the scenario with `@timeout_<seconds>` (e.g. `@timeout_180`). `@timeout_0` disables the timeout. Browser scaling (Firefox 2x, Safari 4x) is preserved.
35
35
  * @property {Object} additionalPages: custom pages configuration
36
36
  * @property {string} featureFilesFolder: folder name under which feature-files will be placed. Default is feature-files
37
37
  * @property {string} stepDefinitionsFolder: folder name under which step implementations will be placed. Default is steps
@@ -19,7 +19,7 @@ const testSetup = require('../../fixtures/testSetup');
19
19
  * @property {any} reportPath : directory where report is generate
20
20
  * @property {boolean} bddMode: Feature files needs to be processed
21
21
  * @property {number} expectTimeout: time in milliseconds which the expect condition should fail
22
- * @property {number} testTimeout: time in milliseconds which the test should fail
22
+ * @property {number} testTimeout: time in milliseconds which the test should fail. Can be overridden per-scenario by tagging the scenario with `@timeout_<seconds>` (e.g. `@timeout_180`). `@timeout_0` disables the timeout. Browser scaling (Firefox 2x, Safari 4x) is preserved.
23
23
  * @property {Object} additionalPages: custom pages configuration
24
24
  * @property {string} featureFilesFolder: folder name under which feature-files will be placed. Default is feature-files
25
25
  * @property {string} stepDefinitionsFolder: folder name under which step implementations will be placed. Default is steps
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _caseTimeout = _interopRequireDefault(require("../../../../../core/playwright/builtInFixtures/caseTimeout"));
5
+ var _caseTimeoutResolver = require("../../../../../core/playwright/helpers/caseTimeoutResolver");
6
+ var _browserTypes = require("../../../../../core/playwright/constants/browserTypes");
7
+ var _logger = require("../../../../../utils/logger");
8
+ jest.mock('../../../../../utils/logger');
9
+ describe('resolveCaseTimeoutMs', () => {
10
+ beforeEach(() => {
11
+ jest.clearAllMocks();
12
+ });
13
+ test('returns null when no timeout tag is present', () => {
14
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(['@smoke', '@edition_Free'], _browserTypes.BROWSER_PROJECT_MAPPING.CHROME)).toBeNull();
15
+ });
16
+ test('returns null when tags is not an array', () => {
17
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(undefined, _browserTypes.BROWSER_PROJECT_MAPPING.CHROME)).toBeNull();
18
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(null, _browserTypes.BROWSER_PROJECT_MAPPING.CHROME)).toBeNull();
19
+ });
20
+ test('parses @timeout_<n> as seconds for Chrome (factor 1)', () => {
21
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(['@timeout_180'], _browserTypes.BROWSER_PROJECT_MAPPING.CHROME)).toBe(180000);
22
+ });
23
+ test('applies Firefox 2x multiplier', () => {
24
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(['@timeout_180'], _browserTypes.BROWSER_PROJECT_MAPPING.FIREFOX)).toBe(360000);
25
+ });
26
+ test('returns 0 (no timeout) when tag is @timeout_0, regardless of browser', () => {
27
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(['@timeout_0'], _browserTypes.BROWSER_PROJECT_MAPPING.CHROME)).toBe(0);
28
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(['@timeout_0'], _browserTypes.BROWSER_PROJECT_MAPPING.FIREFOX)).toBe(0);
29
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(['@timeout_0'], _browserTypes.BROWSER_PROJECT_MAPPING.SAFARI)).toBe(0);
30
+ });
31
+ test('uses the first matching tag and logs a warning when multiple are present', () => {
32
+ const result = (0, _caseTimeoutResolver.resolveCaseTimeoutMs)(['@timeout_120', '@timeout_300'], _browserTypes.BROWSER_PROJECT_MAPPING.CHROME);
33
+ expect(result).toBe(120_000);
34
+ expect(_logger.Logger.log).toHaveBeenCalledWith(_logger.Logger.INFO_TYPE, expect.stringContaining('Multiple @timeout_* tags found'));
35
+ });
36
+ test('ignores malformed timeout-like tags', () => {
37
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(['@timeout_', '@timeout_abc', '@timeout_-5', '@timeoutfoo'], _browserTypes.BROWSER_PROJECT_MAPPING.CHROME)).toBeNull();
38
+ });
39
+ test('ignores non-string entries in tags', () => {
40
+ expect((0, _caseTimeoutResolver.resolveCaseTimeoutMs)(['@timeout_60', null, undefined, 42], _browserTypes.BROWSER_PROJECT_MAPPING.CHROME)).toBe(60_000);
41
+ });
42
+ });
43
+ describe('caseTimeout fixture', () => {
44
+ beforeEach(() => {
45
+ jest.clearAllMocks();
46
+ });
47
+ function getFixture() {
48
+ return _caseTimeout.default.caseTimeout[0];
49
+ }
50
+ test('is registered as an auto fixture', () => {
51
+ expect(_caseTimeout.default.caseTimeout[1]).toEqual({
52
+ auto: true
53
+ });
54
+ });
55
+ test('calls testInfo.setTimeout with scaled value when tag matches', async () => {
56
+ const use = jest.fn();
57
+ const testInfo = {
58
+ project: {
59
+ name: _browserTypes.BROWSER_PROJECT_MAPPING.FIREFOX
60
+ },
61
+ setTimeout: jest.fn()
62
+ };
63
+ await getFixture()({
64
+ $tags: ['@timeout_30']
65
+ }, use, testInfo);
66
+ expect(testInfo.setTimeout).toHaveBeenCalledWith(60_000);
67
+ expect(use).toHaveBeenCalled();
68
+ });
69
+ test('does not call testInfo.setTimeout when no tag is present', async () => {
70
+ const use = jest.fn();
71
+ const testInfo = {
72
+ project: {
73
+ name: _browserTypes.BROWSER_PROJECT_MAPPING.CHROME
74
+ },
75
+ setTimeout: jest.fn()
76
+ };
77
+ await getFixture()({
78
+ $tags: ['@smoke']
79
+ }, use, testInfo);
80
+ expect(testInfo.setTimeout).not.toHaveBeenCalled();
81
+ expect(use).toHaveBeenCalled();
82
+ });
83
+ test('passes 0 through to testInfo.setTimeout for @timeout_0', async () => {
84
+ const use = jest.fn();
85
+ const testInfo = {
86
+ project: {
87
+ name: _browserTypes.BROWSER_PROJECT_MAPPING.SAFARI
88
+ },
89
+ setTimeout: jest.fn()
90
+ };
91
+ await getFixture()({
92
+ $tags: ['@timeout_0']
93
+ }, use, testInfo);
94
+ expect(testInfo.setTimeout).toHaveBeenCalledWith(0);
95
+ });
96
+ });