@zohodesk/testinglibrary 0.1.9 → 0.2.0

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.
Files changed (52) hide show
  1. package/build/bdd-framework/cli/commands/env.js +1 -1
  2. package/build/bdd-framework/cli/commands/export.js +18 -3
  3. package/build/bdd-framework/decorators.js +2 -2
  4. package/build/bdd-framework/gen/formatter.js +57 -13
  5. package/build/bdd-framework/gen/index.js +21 -9
  6. package/build/bdd-framework/gen/specialTags.js +70 -0
  7. package/build/bdd-framework/gen/testFile.js +10 -5
  8. package/build/bdd-framework/gen/testNode.js +4 -29
  9. package/build/bdd-framework/gen/testPoms.js +1 -1
  10. package/build/bdd-framework/index.js +1 -1
  11. package/build/bdd-framework/playwright/testTypeImpl.js +28 -10
  12. package/build/bdd-framework/playwright/types.js +8 -1
  13. package/build/bdd-framework/playwright/utils.js +22 -0
  14. package/build/bdd-framework/reporter/cucumber/base.js +2 -7
  15. package/build/bdd-framework/reporter/cucumber/html.js +9 -4
  16. package/build/bdd-framework/reporter/cucumber/messagesBuilder/AttachmentMapper.js +28 -10
  17. package/build/bdd-framework/reporter/cucumber/messagesBuilder/Builder.js +21 -20
  18. package/build/bdd-framework/reporter/cucumber/messagesBuilder/Hook.js +3 -3
  19. package/build/bdd-framework/reporter/cucumber/messagesBuilder/TestCaseRun.js +46 -18
  20. package/build/bdd-framework/reporter/cucumber/messagesBuilder/TestCaseRunHooks.js +41 -20
  21. package/build/bdd-framework/reporter/cucumber/messagesBuilder/TestStepAttachments.js +19 -2
  22. package/build/bdd-framework/reporter/cucumber/messagesBuilder/TestStepRun.js +42 -16
  23. package/build/bdd-framework/reporter/cucumber/messagesBuilder/{pwUtils.js → pwStepUtils.js} +33 -14
  24. package/build/bdd-framework/run/StepInvoker.js +8 -7
  25. package/build/bdd-framework/run/bddData/index.js +59 -0
  26. package/build/bdd-framework/run/bddData/types.js +5 -0
  27. package/build/bdd-framework/run/bddFixtures.js +5 -4
  28. package/build/bdd-framework/run/bddWorld.js +3 -3
  29. package/build/bdd-framework/run/bddWorldInternal.js +1 -5
  30. package/build/bdd-framework/snippets/index.js +1 -1
  31. package/build/bdd-framework/steps/createBdd.js +78 -0
  32. package/build/bdd-framework/steps/decorators/class.js +68 -0
  33. package/build/bdd-framework/steps/decorators/steps.js +98 -0
  34. package/build/bdd-framework/steps/defineStep.js +62 -0
  35. package/build/bdd-framework/steps/stepConfig.js +24 -0
  36. package/build/core/playwright/builtInFixtures/page.js +32 -14
  37. package/build/core/playwright/env-initializer.js +21 -6
  38. package/build/core/playwright/helpers/auth/checkAuthCookies.js +8 -5
  39. package/build/core/playwright/helpers/auth/getUsers.js +70 -31
  40. package/build/core/playwright/helpers/auth/index.js +16 -4
  41. package/build/core/playwright/helpers/auth/loginSteps.js +2 -2
  42. package/build/core/playwright/helpers/configFileNameProvider.js +9 -1
  43. package/build/core/playwright/index.js +14 -59
  44. package/build/core/playwright/readConfigFile.js +13 -2
  45. package/build/core/playwright/test-runner.js +1 -2
  46. package/build/index.d.ts +17 -18
  47. package/build/index.js +16 -4
  48. package/build/setup-folder-structure/samples/auth-setup-sample.js +20 -21
  49. package/changelog.md +14 -0
  50. package/npm-shrinkwrap.json +1 -1
  51. package/package.json +3 -2
  52. package/build/bdd-framework/run/bddDataAttachment.js +0 -46
@@ -33,7 +33,7 @@ function showPackageVersion(packageName) {
33
33
  * to aneble using directly from /dist in tests.
34
34
  */
35
35
  function getOwnVersion() {
36
- return '6.0.1';
36
+ return '6.1.1';
37
37
  }
38
38
  function showPlaywrightConfigPath(cliConfigPath) {
39
39
  const resolvedConfigFile = (0, _loadConfig.resolveConfigFile)(cliConfigPath);
@@ -16,14 +16,18 @@ var _gen = require("../../gen");
16
16
  const logger = new _logger.Logger({
17
17
  verbose: true
18
18
  });
19
- const exportCommand = exports.exportCommand = new _commander.Command('export').description('Prints all step definitions').addOption(_options.configOption).action(async opts => {
19
+ const exportCommand = exports.exportCommand = new _commander.Command('export').description('Prints step definitions').addOption(_options.configOption).option('--unused-steps', 'Output only unused steps').action(async opts => {
20
20
  const {
21
21
  resolvedConfigFile
22
22
  } = await (0, _loadConfig.loadConfig)(opts.config);
23
- logger.log(`List of all steps found by config: ${_path.default.relative(process.cwd(), resolvedConfigFile)}\n`);
23
+ logger.log(`Using config: ${_path.default.relative(process.cwd(), resolvedConfigFile)}`);
24
24
  const configs = Object.values((0, _env.getEnvConfigs)());
25
25
  (0, _test.assertConfigsCount)(configs);
26
- await showStepsForConfigs(configs);
26
+ if (opts.unusedSteps) {
27
+ await showUnusedStepsForConfigs(configs);
28
+ } else {
29
+ await showStepsForConfigs(configs);
30
+ }
27
31
  });
28
32
  async function showStepsForConfigs(configs) {
29
33
  // here we don't need workers (as in test command) because if some step files
@@ -34,6 +38,17 @@ async function showStepsForConfigs(configs) {
34
38
  stepDefinitions.forEach(s => steps.add(`* ${getStepText(s)}`));
35
39
  });
36
40
  await Promise.all(tasks);
41
+ logger.log(`List of all steps (${steps.size}):`);
42
+ steps.forEach(stepText => logger.log(stepText));
43
+ }
44
+ async function showUnusedStepsForConfigs(configs) {
45
+ const steps = new Set();
46
+ const tasks = configs.map(async config => {
47
+ const stepDefinitions = await new _gen.TestFilesGenerator(config).extractUnusedSteps();
48
+ stepDefinitions.forEach(s => steps.add(`* ${getStepText(s)}`));
49
+ });
50
+ await Promise.all(tasks);
51
+ logger.log(`List of unused steps (${steps.size}):`);
37
52
  steps.forEach(stepText => logger.log(stepText));
38
53
  }
39
54
  function getStepText({
@@ -10,8 +10,8 @@ Object.defineProperty(exports, "Fixture", {
10
10
  }
11
11
  });
12
12
  exports.When = exports.Then = exports.Step = exports.Given = void 0;
13
- var _class = require("./stepDefinitions/decorators/class");
14
- var _steps = require("./stepDefinitions/decorators/steps");
13
+ var _class = require("./steps/decorators/class");
14
+ var _steps = require("./steps/decorators/steps");
15
15
  const Given = exports.Given = (0, _steps.createStepDecorator)('Given');
16
16
  const When = exports.When = (0, _steps.createStepDecorator)('When');
17
17
  const Then = exports.Then = (0, _steps.createStepDecorator)('Then');
@@ -6,10 +6,12 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.Formatter = void 0;
7
7
  var _jsStringWrap = require("../utils/jsStringWrap");
8
8
  var _utils = require("../utils");
9
+ var _utils2 = require("../playwright/utils");
9
10
  /**
10
11
  * Helper to format Playwright test file.
11
12
  */
12
13
 
14
+ const supportsTags = _utils2.playwrightVersion >= '1.42.0';
13
15
  class Formatter {
14
16
  config;
15
17
  constructor(config) {
@@ -27,12 +29,14 @@ class Formatter {
27
29
  // this.quoted() is not possible for 'import from' as backticks not parsed
28
30
  `import { ${varName} } from ${JSON.stringify(importTestFromFile)};`, ''];
29
31
  }
30
- suite(node, children) {
31
- const firstLine = `test.describe${this.getSubFn(node)}(${this.quoted(node.title)}, () => {`;
32
+ describe(node, children) {
33
+ const firstLine = `test.${this.getFunction('describe', node)}(${this.quoted(node.title)}, () => {`;
32
34
  if (!children.length) {
33
35
  return [`${firstLine}});`, ''];
34
36
  }
35
- return [firstLine, '', ...children.map(indent), `});`, ''];
37
+ return [firstLine,
38
+ // prettier-ignore
39
+ ...this.describeConfigure(node).map(indent), '', ...children.map(indent), `});`, ''];
36
40
  }
37
41
  beforeEach(fixtures, children) {
38
42
  const fixturesStr = [...fixtures].join(', ');
@@ -42,11 +46,19 @@ class Formatter {
42
46
  test(node, fixtures, children) {
43
47
  const fixturesStr = [...fixtures].join(', ');
44
48
  const title = this.quoted(node.title);
45
- const firstLine = `test${this.getSubFn(node)}(${title}, async ({ ${fixturesStr} }) => {`;
49
+ const tags = this.testTags(node);
50
+ const firstLine = `${this.getFunction('test', node)}(${title}, ${tags}async ({ ${fixturesStr} }) => {`;
46
51
  if (!children.length) {
47
52
  return [`${firstLine}});`, ''];
48
53
  }
49
- return [firstLine, ...children.map(indent), `});`, ''];
54
+ const lines = [firstLine,
55
+ // prettier-ignore
56
+ ...children.map(indent), `});`, ''];
57
+ // wrap test into anonymous describe in case of retries / timeout tags
58
+ const specialTagsConfigure = this.describeConfigure(node);
59
+ return specialTagsConfigure.length ? ['test.describe(() => {',
60
+ // prettier-ignore
61
+ ...specialTagsConfigure.map(indent), '', ...lines.map(indent), `});`, ''] : lines;
50
62
  }
51
63
  // eslint-disable-next-line max-params
52
64
  step(keyword, text, argument, fixtureNames = []) {
@@ -93,17 +105,49 @@ class Formatter {
93
105
  langFixture(lang) {
94
106
  return [`$lang: ({}, use) => use(${this.quoted(lang)}),`];
95
107
  }
96
- getSubFn(node) {
97
- if (node.flags.only) {
98
- return '.only';
108
+ // eslint-disable-next-line complexity
109
+ getFunction(baseFn, node) {
110
+ if (node.specialTags.only) {
111
+ return `${baseFn}.only`;
112
+ }
113
+ // describe.fail is not supported
114
+ if (baseFn === 'test' && node.specialTags.fail) {
115
+ return `${baseFn}.fail`;
116
+ }
117
+ if (node.specialTags.skip) {
118
+ return `${baseFn}.skip`;
119
+ }
120
+ if (node.specialTags.fixme) {
121
+ return `${baseFn}.fixme`;
122
+ }
123
+ return baseFn;
124
+ }
125
+ describeConfigure(node) {
126
+ const options = {};
127
+ const {
128
+ retries,
129
+ timeout,
130
+ mode
131
+ } = node.specialTags;
132
+ if (retries !== undefined) {
133
+ options.retries = retries;
99
134
  }
100
- if (node.flags.skip) {
101
- return '.skip';
135
+ if (timeout !== undefined) {
136
+ options.timeout = timeout;
102
137
  }
103
- if (node.flags.fixme) {
104
- return '.fixme';
138
+ if (mode !== undefined) {
139
+ options.mode = mode;
105
140
  }
106
- return '';
141
+ return Object.keys(options).length ? [`test.describe.configure(${JSON.stringify(options)});`] : [];
142
+ }
143
+ testTags(node) {
144
+ return supportsTags && node.tags.length ? `{ tag: [${node.tags.map(tag => this.quoted(tag)).join(', ')}] }, ` : '';
145
+ }
146
+ testTimeout(node) {
147
+ const {
148
+ timeout
149
+ } = node.specialTags;
150
+ return timeout !== undefined ? [`test.setTimeout(${timeout});`] : [];
107
151
  }
108
152
  /**
109
153
  * Apply this function only to string literals (mostly titles here).
@@ -14,13 +14,13 @@ var _loadFeatures = require("../cucumber/loadFeatures");
14
14
  var _loadSteps = require("../cucumber/loadSteps");
15
15
  var _config = require("../config");
16
16
  var _snippets = require("../snippets");
17
- var _steps = require("../stepDefinitions/decorators/steps");
17
+ var _steps = require("../steps/decorators/steps");
18
18
  var _transform = require("../playwright/transform");
19
19
  var _configDir = require("../config/configDir");
20
20
  var _logger = require("../utils/logger");
21
21
  var _tagExpressions = _interopRequireDefault(require("@cucumber/tag-expressions"));
22
22
  var _exit = require("../utils/exit");
23
- var _createBdd = require("../stepDefinitions/createBdd");
23
+ var _createBdd = require("../steps/createBdd");
24
24
  var _resolveFeaturePaths = require("../cucumber/resolveFeaturePaths");
25
25
  /**
26
26
  * Generate playwright test files from Gherkin documents.
@@ -60,6 +60,16 @@ class TestFilesGenerator {
60
60
  await this.loadSteps();
61
61
  return this.supportCodeLibrary.stepDefinitions;
62
62
  }
63
+ // todo: combine with extractSteps
64
+ async extractUnusedSteps() {
65
+ await this.loadCucumberConfig();
66
+ await Promise.all([this.loadFeatures(), this.loadSteps()]);
67
+ this.buildFiles();
68
+ return this.supportCodeLibrary.stepDefinitions.filter(stepDefinition => {
69
+ const isUsed = this.files.some(file => file.usedStepDefinitions.has(stepDefinition));
70
+ return !isUsed;
71
+ });
72
+ }
63
73
  async loadCucumberConfig() {
64
74
  const environment = {
65
75
  cwd: (0, _configDir.getPlaywrightConfigDir)()
@@ -75,20 +85,22 @@ class TestFilesGenerator {
75
85
  async loadFeatures() {
76
86
  const cwd = (0, _configDir.getPlaywrightConfigDir)();
77
87
  const {
78
- paths,
79
88
  defaultDialect
80
89
  } = this.runConfiguration.sources;
81
- this.logger.log(`Loading features from: ${paths.join(', ')}`);
82
90
  const {
83
91
  featurePaths
84
92
  } = await (0, _resolveFeaturePaths.resovleFeaturePaths)(this.runConfiguration, {
85
93
  cwd
86
94
  });
87
- await this.featuresLoader.load(featurePaths, {
88
- relativeTo: cwd,
89
- defaultDialect
90
- });
91
- this.handleParseErrors();
95
+ this.logger.log(`Loading features from paths (${featurePaths.length}):`);
96
+ featurePaths.forEach(featurePath => this.logger.log(featurePath));
97
+ if (featurePaths.length) {
98
+ await this.featuresLoader.load(featurePaths, {
99
+ relativeTo: cwd,
100
+ defaultDialect
101
+ });
102
+ this.handleParseErrors();
103
+ }
92
104
  this.logger.log(`Loaded features: ${this.featuresLoader.getDocumentsCount()}`);
93
105
  }
94
106
  async loadSteps() {
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SpecialTags = void 0;
7
+ /**
8
+ * Special tags.
9
+ */
10
+ class SpecialTags {
11
+ ownTags;
12
+ allTags;
13
+ only;
14
+ skip;
15
+ fixme;
16
+ fail;
17
+ retries;
18
+ timeout;
19
+ mode;
20
+ constructor(ownTags, allTags) {
21
+ this.ownTags = ownTags;
22
+ this.allTags = allTags;
23
+ this.extractFlags();
24
+ this.extractRetries();
25
+ this.extractTimeout();
26
+ this.extractMode();
27
+ }
28
+ extractFlags() {
29
+ // order is important
30
+ const flags = ['only', 'fail', 'skip', 'fixme'];
31
+ for (const flag of flags) {
32
+ if (this.ownTags.includes(`@${flag}`)) {
33
+ this[flag] = true;
34
+ return;
35
+ }
36
+ }
37
+ // describe.fail is not supported, so mark all nested tests as failed instead
38
+ if (this.allTags.includes(`@fail`)) {
39
+ this.fail = true;
40
+ }
41
+ }
42
+ extractRetries() {
43
+ for (const tag of this.ownTags.reverse()) {
44
+ const match = tag.match(/@retries:(\d+)/i);
45
+ if (match) {
46
+ this.retries = Number(match[1]);
47
+ return;
48
+ }
49
+ }
50
+ }
51
+ extractTimeout() {
52
+ for (const tag of this.ownTags.reverse()) {
53
+ const match = tag.match(/@timeout:(\d+)/i);
54
+ if (match) {
55
+ this.timeout = Number(match[1]);
56
+ return;
57
+ }
58
+ }
59
+ }
60
+ extractMode() {
61
+ for (const tag of this.ownTags.reverse()) {
62
+ const match = tag.match(/@mode:(default|parallel|serial)/i);
63
+ if (match) {
64
+ this.mode = match[1];
65
+ return;
66
+ }
67
+ }
68
+ }
69
+ }
70
+ exports.SpecialTags = SpecialTags;
@@ -14,7 +14,7 @@ var _index = require("@cucumber/cucumber/lib/formatter/helpers/index");
14
14
  var _utils = require("../utils");
15
15
  var _testPoms = require("./testPoms");
16
16
  var _testNode = require("./testNode");
17
- var _stepConfig = require("../stepDefinitions/stepConfig");
17
+ var _stepConfig = require("../steps/stepConfig");
18
18
  var _exit = require("../utils/exit");
19
19
  var _fixtures = require("./fixtures");
20
20
  var _scenario = require("../hooks/scenario");
@@ -35,6 +35,7 @@ class TestFile {
35
35
  hasCustomTest = false;
36
36
  undefinedSteps = [];
37
37
  featureUri;
38
+ usedStepDefinitions = new Set();
38
39
  constructor(options) {
39
40
  this.options = options;
40
41
  this.formatter = new _formatter.Formatter(options.config);
@@ -67,6 +68,9 @@ class TestFile {
67
68
  return this.testMetaBuilder.testCount;
68
69
  }
69
70
  build() {
71
+ if (!this.pickles.length) {
72
+ return this;
73
+ }
70
74
  this.loadI18nKeywords();
71
75
  this.lines = [...this.getFileHeader(),
72
76
  // prettier-ignore
@@ -136,11 +140,11 @@ class TestFile {
136
140
  getSuite(feature, parent) {
137
141
  const node = new _testNode.TestNode(feature, parent);
138
142
  if (node.isSkipped()) {
139
- return this.formatter.suite(node, []);
143
+ return this.formatter.describe(node, []);
140
144
  }
141
145
  const lines = [];
142
146
  feature.children.forEach(child => lines.push(...this.getSuiteChild(child, node)));
143
- return this.formatter.suite(node, lines);
147
+ return this.formatter.describe(node, lines);
144
148
  }
145
149
  getSuiteChild(child, parent) {
146
150
  if ('rule' in child && child.rule) {
@@ -177,7 +181,7 @@ class TestFile {
177
181
  getOutlineSuite(scenario, parent) {
178
182
  const node = new _testNode.TestNode(scenario, parent);
179
183
  if (node.isSkipped()) {
180
- return this.formatter.suite(node, []);
184
+ return this.formatter.describe(node, []);
181
185
  }
182
186
  const lines = [];
183
187
  let exampleIndex = 0;
@@ -189,7 +193,7 @@ class TestFile {
189
193
  lines.push(...testLines);
190
194
  });
191
195
  });
192
- return this.formatter.suite(node, lines);
196
+ return this.formatter.describe(node, lines);
193
197
  }
194
198
  /**
195
199
  * Generate test from Examples row of Scenario Outline
@@ -304,6 +308,7 @@ class TestFile {
304
308
  });
305
309
  return this.getMissingStep(enKeyword, keywordType, pickleStep);
306
310
  }
311
+ this.usedStepDefinitions.add(stepDefinition);
307
312
  // for cucumber-style stepConfig is undefined
308
313
  const stepConfig = (0, _stepConfig.getStepConfig)(stepDefinition);
309
314
  if (stepConfig !== null && stepConfig !== void 0 && stepConfig.hasCustomTest) {
@@ -5,56 +5,31 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.TestNode = void 0;
7
7
  var _utils = require("../utils");
8
+ var _specialTags = require("./specialTags");
8
9
  /**
9
10
  * Universal TestNode class representing test or suite in a test file.
10
11
  * Holds parent-child links.
11
12
  * Allows to inherit tags and titles path.
12
13
  */
13
14
 
14
- const SPECIAL_TAGS = ['@only', '@skip', '@fixme'];
15
15
  class TestNode {
16
16
  title;
17
17
  titlePath;
18
18
  ownTags;
19
19
  tags;
20
- flags = {};
20
+ specialTags;
21
21
  constructor(gherkinNode, parent) {
22
22
  this.title = gherkinNode.name;
23
23
  this.titlePath = ((parent === null || parent === void 0 ? void 0 : parent.titlePath) || []).concat([this.title]);
24
24
  this.ownTags = (0, _utils.removeDuplicates)(getTagNames(gherkinNode.tags));
25
25
  this.tags = (0, _utils.removeDuplicates)(((parent === null || parent === void 0 ? void 0 : parent.tags) || []).concat(this.ownTags));
26
- this.initFlags();
26
+ this.specialTags = new _specialTags.SpecialTags(this.ownTags, this.tags);
27
27
  }
28
28
  isSkipped() {
29
- return this.flags.skip || this.flags.fixme;
30
- }
31
- initFlags() {
32
- this.ownTags.forEach(tag => {
33
- if (isSpecialTag(tag)) {
34
- this.setFlag(tag);
35
- }
36
- });
37
- }
38
- // eslint-disable-next-line complexity
39
- setFlag(tag) {
40
- // in case of several special tags, @only takes precendence
41
- if (tag === '@only') {
42
- this.flags = {
43
- only: true
44
- };
45
- }
46
- if (tag === '@skip' && !this.flags.only) {
47
- this.flags.skip = true;
48
- }
49
- if (tag === '@fixme' && !this.flags.only) {
50
- this.flags.fixme = true;
51
- }
29
+ return this.specialTags.skip || this.specialTags.fixme;
52
30
  }
53
31
  }
54
32
  exports.TestNode = TestNode;
55
33
  function getTagNames(tags) {
56
34
  return tags.map(tag => tag.name);
57
- }
58
- function isSpecialTag(tag) {
59
- return SPECIAL_TAGS.includes(tag);
60
35
  }
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.TestPoms = void 0;
7
7
  exports.buildFixtureTag = buildFixtureTag;
8
- var _class = require("../stepDefinitions/decorators/class");
8
+ var _class = require("../steps/decorators/class");
9
9
  var _exit = require("../utils/exit");
10
10
  /**
11
11
  * Track PomNodes used in the particular test.
@@ -46,7 +46,7 @@ Object.defineProperty(exports, "test", {
46
46
  }
47
47
  });
48
48
  var _config = require("./config");
49
- var _createBdd = require("./stepDefinitions/createBdd");
49
+ var _createBdd = require("./steps/createBdd");
50
50
  var _bddFixtures = require("./run/bddFixtures");
51
51
  var _bddWorld = require("./run/bddWorld");
52
52
  var _helper = require("./reporter/cucumber/helper");
@@ -7,6 +7,7 @@ exports.isTestContainsSubtest = isTestContainsSubtest;
7
7
  exports.runStepWithCustomLocation = runStepWithCustomLocation;
8
8
  var _test = require("@playwright/test");
9
9
  var _utils = require("../utils");
10
+ var _utils2 = require("./utils");
10
11
  /**
11
12
  * Helpers to deal with Playwright test internal stuff.
12
13
  * See: https://github.com/microsoft/playwright/blob/main/packages/playwright-test/src/common/testType.ts
@@ -27,16 +28,33 @@ function getTestImpl(test) {
27
28
  */
28
29
  // eslint-disable-next-line max-params
29
30
  async function runStepWithCustomLocation(test, stepText, location, body) {
30
- const testInfo = test.info();
31
- // See: https://github.com/microsoft/playwright/blob/main/packages/playwright/src/common/testType.ts#L221
32
- // @ts-expect-error _runAsStep is hidden from public
33
- return testInfo._runAsStep({
34
- category: 'test.step',
35
- title: stepText,
36
- location
37
- }, async () => {
38
- return await body();
39
- });
31
+ // Since PW 1.43 testInfo._runAsStep was replaced with a more complex logic.
32
+ // To run step with a custom location, we hijack testInfo._addStep()
33
+ // so that it appends location for the bdd step calls.
34
+ // Finally we call test.step(), that internally invokes testInfo._addStep().
35
+ // See: https://github.com/microsoft/playwright/issues/30160
36
+ // See: https://github.com/microsoft/playwright/blob/release-1.43/packages/playwright/src/common/testType.ts#L262
37
+ // See: https://github.com/microsoft/playwright/blob/release-1.43/packages/playwright/src/worker/testInfo.ts#L247
38
+ if (_utils2.playwrightVersion >= '1.39.0') {
39
+ const testInfo = test.info();
40
+ // here we rely on that testInfo._addStep is called synchronously in test.step()
41
+ const origAddStep = testInfo._addStep;
42
+ testInfo._addStep = function (data) {
43
+ data.location = location;
44
+ testInfo._addStep = origAddStep;
45
+ return origAddStep.call(this, data);
46
+ };
47
+ return test.step(stepText, body);
48
+ } else {
49
+ const testInfo = test.info();
50
+ return testInfo._runAsStep({
51
+ category: 'test.step',
52
+ title: stepText,
53
+ location
54
+ }, async () => {
55
+ return await body();
56
+ });
57
+ }
40
58
  }
41
59
  /**
42
60
  * Returns true if test contains all fixtures of subtest.
@@ -2,4 +2,11 @@
2
2
 
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
- });
5
+ });
6
+ Object.defineProperty(exports, "PlaywrightLocation", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _reporter.Location;
10
+ }
11
+ });
12
+ var _reporter = require("@playwright/test/reporter");
@@ -5,12 +5,15 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.getPlaywrightModulePath = getPlaywrightModulePath;
8
+ exports.playwrightVersion = void 0;
8
9
  exports.requirePlaywrightModule = requirePlaywrightModule;
10
+ exports.updateAnnotation = updateAnnotation;
9
11
  var _fs = _interopRequireDefault(require("fs"));
10
12
  var _path = _interopRequireDefault(require("path"));
11
13
  var _utils = require("../utils");
12
14
  // cache playwright root
13
15
  let playwrightRoot = '';
16
+ const playwrightVersion = exports.playwrightVersion = (0, _utils.getPackageVersion)('@playwright/test');
14
17
  /**
15
18
  * Requires Playwright's internal module that is not exported via package.exports.
16
19
  */
@@ -31,4 +34,23 @@ function getPlaywrightRoot() {
31
34
  playwrightRoot = _fs.default.existsSync(libDir) ? playwrightTestRoot : (0, _utils.resolvePackageRoot)('playwright');
32
35
  }
33
36
  return playwrightRoot;
37
+ }
38
+ /**
39
+ * Create or update annotation with provided type.
40
+ */
41
+ function updateAnnotation(testInfo, annotation, {
42
+ create = false
43
+ } = {}) {
44
+ const {
45
+ annotations
46
+ } = testInfo;
47
+ const index = annotations.findIndex(a => a.type === annotation.type);
48
+ if (index === -1 && !create) {
49
+ throw new Error(`Annotation "${annotation.type}" is not found.`);
50
+ }
51
+ if (index === -1) {
52
+ annotations.push(annotation);
53
+ } else {
54
+ annotations[index] = annotation;
55
+ }
34
56
  }
@@ -7,14 +7,12 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.default = void 0;
8
8
  var _path = _interopRequireDefault(require("path"));
9
9
  var _fs = _interopRequireDefault(require("fs"));
10
- var _stream = require("stream");
10
+ var _promises = require("node:stream/promises");
11
11
  /**
12
12
  * Base reporter for Cucumber reporters.
13
13
  * Reference: https://github.com/cucumber/cucumber-js/blob/main/src/formatter/index.ts
14
14
  */
15
15
 
16
- // Import pipeline from the 'stream' module
17
-
18
16
  class BaseReporter {
19
17
  internalOptions;
20
18
  outputStream = process.stdout;
@@ -34,10 +32,7 @@ class BaseReporter {
34
32
  async finished() {
35
33
  if (!isStdout(this.outputStream)) {
36
34
  this.outputStream.end();
37
- await new Promise((resolve, reject) => {
38
- this.outputStream.on('finish', resolve);
39
- this.outputStream.on('error', reject);
40
- });
35
+ await (0, _promises.finished)(this.outputStream);
41
36
  }
42
37
  }
43
38
  setOutputStream(outputFile) {
@@ -5,10 +5,18 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.default = void 0;
8
+ var _promises = require("node:stream/promises");
8
9
  var _htmlFormatter = _interopRequireDefault(require("@cucumber/html-formatter"));
9
10
  var _utils = require("../../utils");
10
11
  var _path = _interopRequireDefault(require("path"));
11
12
  var _base = _interopRequireDefault(require("./base"));
13
+ /**
14
+ * Cucumber html reporter.
15
+ * Based on: https://github.com/cucumber/cucumber-js/blob/main/src/formatter/html_formatter.ts
16
+ * See: https://github.com/cucumber/html-formatter
17
+ * See: https://github.com/cucumber/react-components
18
+ */
19
+
12
20
  class HtmlReporter extends _base.default {
13
21
  userOptions;
14
22
  htmlStream;
@@ -25,10 +33,7 @@ class HtmlReporter extends _base.default {
25
33
  }
26
34
  async finished() {
27
35
  this.htmlStream.end();
28
- await new Promise((resolve, reject) => {
29
- this.htmlStream.on('finish', resolve);
30
- this.htmlStream.on('error', reject);
31
- });
36
+ await (0, _promises.finished)(this.htmlStream);
32
37
  await super.finished();
33
38
  }
34
39
  }