@zohodesk/testinglibrary 0.1.1 → 0.1.3

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.
@@ -34,7 +34,7 @@ function showPackageVersion(packageName) {
34
34
  * to aneble using directly from /dist in tests.
35
35
  */
36
36
  function getOwnVersion() {
37
- return '5.3.0';
37
+ return '5.4.0';
38
38
  }
39
39
  function showPlaywrightConfigPath(cliConfigPath) {
40
40
  const resolvedConfigFile = (0, _loadConfig.resolveConfigFile)(cliConfigPath);
@@ -11,40 +11,40 @@ var _events = require("events");
11
11
  var _path = _interopRequireDefault(require("path"));
12
12
  var _commander = require("commander");
13
13
  var _gen = require("../../gen");
14
- var _utils = require("../../utils");
15
14
  var _loadConfig = require("../../playwright/loadConfig");
16
15
  var _env = require("../../config/env");
17
16
  var _config = require("../../config");
18
17
  var _options = require("../options");
18
+ var _exit = require("../../utils/exit");
19
19
  const GEN_WORKER_PATH = _path.default.resolve(__dirname, '..', 'worker.js');
20
20
  const testCommand = new _commander.Command('test').description('Generate Playwright test files from Gherkin documents').addOption(_options.configOption).option('--tags <expression>', `Tags expression to filter scenarios for generation`).option('--verbose', `Verbose mode (default: ${Boolean(_config.defaults.verbose)})`).action(async opts => {
21
21
  await (0, _loadConfig.loadConfig)(opts.config);
22
- const configs = Object.values((0, _env.getEnvConfigs)());
23
- assertConfigsCount(configs);
24
- const cliOptions = buildCliOptions(opts);
25
- await generateFilesForConfigs(configs, cliOptions);
22
+ const configs = readConfigsFromEnv();
23
+ mergeCliOptions(configs, opts);
24
+ await generateFilesForConfigs(configs);
26
25
  });
27
26
  exports.testCommand = testCommand;
28
- function buildCliOptions(opts) {
29
- const config = {};
30
- if ('tags' in opts) config.tags = opts.tags;
31
- if ('verbose' in opts) config.verbose = Boolean(opts.verbose);
32
- return config;
27
+ function readConfigsFromEnv() {
28
+ const configs = Object.values((0, _env.getEnvConfigs)());
29
+ assertConfigsCount(configs);
30
+ return configs;
31
+ }
32
+ function mergeCliOptions(configs, opts) {
33
+ configs.forEach(config => {
34
+ if ('tags' in opts) config.tags = opts.tags;
35
+ if ('verbose' in opts) config.verbose = Boolean(opts.verbose);
36
+ });
33
37
  }
34
38
  function assertConfigsCount(configs) {
35
39
  if (configs.length === 0) {
36
- (0, _utils.exitWithMessage)(`No BDD configs found. Did you use defineBddConfig() in playwright.config.ts?`);
40
+ (0, _exit.exit)(`No BDD configs found. Did you use defineBddConfig() in playwright.config.ts?`);
37
41
  }
38
42
  }
39
- async function generateFilesForConfigs(configs, cliConfig) {
43
+ async function generateFilesForConfigs(configs) {
40
44
  // run first config in main thread and other in workers (to have fresh require cache)
41
45
  // See: https://github.com/vitalets/playwright-bdd/issues/32
42
46
  const tasks = configs.map((config, index) => {
43
- const finalConfig = {
44
- ...config,
45
- ...cliConfig
46
- };
47
- return index === 0 ? new _gen.TestFilesGenerator(finalConfig).generate() : runInWorker(finalConfig);
47
+ return index === 0 ? new _gen.TestFilesGenerator(config).generate() : runInWorker(config);
48
48
  });
49
49
  return Promise.all(tasks);
50
50
  }
@@ -54,6 +54,6 @@ async function runInWorker(config) {
54
54
  config
55
55
  }
56
56
  });
57
- // todo: check if worker exited with error?
58
- await (0, _events.once)(worker, 'exit');
57
+ const [exitCode] = await (0, _events.once)(worker, 'exit');
58
+ if (exitCode) (0, _exit.exit)();
59
59
  }
@@ -8,7 +8,7 @@ exports.getConfigFromEnv = getConfigFromEnv;
8
8
  exports.getEnvConfigs = getEnvConfigs;
9
9
  exports.saveConfigToEnv = saveConfigToEnv;
10
10
  var _path = _interopRequireDefault(require("path"));
11
- var _utils = require("../utils");
11
+ var _exit = require("../utils/exit");
12
12
  /**
13
13
  * Storing configs in env var PLAYWRIGHT_BDD_CONFIGS as JSON-stringified values.
14
14
  * For passing configs to playwright workers and bddgen.
@@ -22,7 +22,7 @@ function saveConfigToEnv(config) {
22
22
  // Throw error only if different calls of defineBddConfig() use the same outputDir.
23
23
  // See: https://github.com/vitalets/playwright-bdd/issues/39#issuecomment-1653805368
24
24
  if (!isSameConfigs(config, existingConfig)) {
25
- (0, _utils.exitWithMessage)(`When using several calls of defineBddConfig()`, `please manually provide different "outputDir" option.`);
25
+ (0, _exit.exit)(`When using several calls of defineBddConfig()`, `please manually provide different "outputDir" option.`);
26
26
  }
27
27
  return;
28
28
  }
@@ -34,7 +34,7 @@ function getConfigFromEnv(outputDir) {
34
34
  outputDir = _path.default.resolve(outputDir);
35
35
  const config = envConfigs[outputDir];
36
36
  if (!config) {
37
- (0, _utils.exitWithMessage)(`Config not found for outputDir: "${outputDir}".`, `Available dirs: ${Object.keys(envConfigs).join('\n')}`);
37
+ (0, _exit.exit)(`Config not found for outputDir: "${outputDir}".`, `Available dirs: ${Object.keys(envConfigs).join('\n')}`);
38
38
  }
39
39
  return config;
40
40
  }
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.loadFeatures = loadFeatures;
7
7
  var _loadSources = require("./loadSources");
8
- var _utils = require("../utils");
8
+ var _exit = require("../utils/exit");
9
9
  async function loadFeatures(runConfiguration, environment) {
10
10
  const {
11
11
  filteredPickles,
@@ -34,6 +34,6 @@ function handleParseErrors(parseErrors) {
34
34
  const message = parseErrors.map(parseError => {
35
35
  return `Parse error in "${parseError.source.uri}" ${parseError.message}`;
36
36
  }).join('\n');
37
- (0, _utils.exitWithMessage)(message);
37
+ (0, _exit.exit)(message);
38
38
  }
39
39
  }
@@ -7,8 +7,8 @@ exports.findStepDefinition = findStepDefinition;
7
7
  exports.hasTsNodeRegister = hasTsNodeRegister;
8
8
  exports.loadSteps = loadSteps;
9
9
  var _api = require("@cucumber/cucumber/api");
10
- var _utils = require("../utils");
11
10
  var _transform = require("../playwright/transform");
11
+ var _exit = require("../utils/exit");
12
12
  const cache = new Map();
13
13
  async function loadSteps(runConfiguration, environment = {}) {
14
14
  const cacheKey = JSON.stringify(runConfiguration);
@@ -26,7 +26,7 @@ function findStepDefinition(supportCodeLibrary, stepText, file) {
26
26
  return step.matchesStepName(stepText);
27
27
  });
28
28
  if (matchedSteps.length === 0) return;
29
- if (matchedSteps.length > 1) (0, _utils.exitWithMessage)([`Several step definitions found for text: ${stepText} (${file})`, ...matchedSteps.map(s => `- ${s.pattern}`)].join('\n'));
29
+ if (matchedSteps.length > 1) (0, _exit.exit)([`Several step definitions found for text: ${stepText} (${file})`, ...matchedSteps.map(s => `- ${s.pattern}`)].join('\n'));
30
30
  // todo: check stepDefinition.keyword with PickleStepType
31
31
  return matchedSteps[0];
32
32
  }
@@ -13,13 +13,13 @@ var _loadConfig = require("../cucumber/loadConfig");
13
13
  var _loadFeatures = require("../cucumber/loadFeatures");
14
14
  var _loadSteps = require("../cucumber/loadSteps");
15
15
  var _config = require("../config");
16
- var _utils = require("../utils");
17
16
  var _snippets = require("../snippets");
18
17
  var _steps = require("../stepDefinitions/decorators/steps");
19
18
  var _transform = require("../playwright/transform");
20
19
  var _dir = require("../config/dir");
21
20
  var _logger = require("../utils/logger");
22
21
  var _tagExpressions = _interopRequireDefault(require("@cucumber/tag-expressions"));
22
+ var _exit = require("../utils/exit");
23
23
  /**
24
24
  * Generate playwright test files from Gherkin documents.
25
25
  */
@@ -41,13 +41,15 @@ class TestFilesGenerator {
41
41
  if (config.tags) this.tagsExpression = (0, _tagExpressions.default)(config.tags);
42
42
  }
43
43
  async generate() {
44
- await this.loadCucumberConfig();
45
- await Promise.all([this.loadFeatures(), this.loadSteps()]);
46
- this.buildFiles();
47
- await this.checkUndefinedSteps();
48
- this.checkImportCustomTest();
49
- await this.clearOutputDir();
50
- await this.saveFiles();
44
+ await (0, _exit.withExitHandler)(async () => {
45
+ await this.loadCucumberConfig();
46
+ await Promise.all([this.loadFeatures(), this.loadSteps()]);
47
+ this.buildFiles();
48
+ await this.checkUndefinedSteps();
49
+ this.checkImportCustomTest();
50
+ await this.clearOutputDir();
51
+ await this.saveFiles();
52
+ });
51
53
  }
52
54
  async extractSteps() {
53
55
  await this.loadCucumberConfig();
@@ -118,7 +120,7 @@ class TestFilesGenerator {
118
120
  const absFeaturePath = _path.default.resolve(configDir, relFeaturePath);
119
121
  const relOutputPath = _path.default.relative(this.config.featuresRoot, absFeaturePath);
120
122
  if (relOutputPath.startsWith('..')) {
121
- (0, _utils.exitWithMessage)(`All feature files should be located underneath featuresRoot.`, `Please change featuresRoot or paths in configuration.\n`, `featureFile: ${absFeaturePath}\n`, `featuresRoot: ${this.config.featuresRoot}\n`);
123
+ (0, _exit.exit)(`All feature files should be located underneath featuresRoot.`, `Please change featuresRoot or paths in configuration.\n`, `featureFile: ${absFeaturePath}\n`, `featuresRoot: ${this.config.featuresRoot}\n`);
122
124
  }
123
125
  const absOutputPath = _path.default.resolve(this.config.outputDir, relOutputPath);
124
126
  return `${absOutputPath}.spec.js`;
@@ -127,14 +129,15 @@ class TestFilesGenerator {
127
129
  const undefinedSteps = this.files.reduce((sum, file) => sum + file.undefinedSteps.length, 0);
128
130
  if (undefinedSteps > 0) {
129
131
  const snippets = new _snippets.Snippets(this.files, this.runConfiguration, this.supportCodeLibrary);
130
- await snippets.printSnippetsAndExit();
132
+ await snippets.print();
133
+ (0, _exit.exit)();
131
134
  }
132
135
  }
133
136
  checkImportCustomTest() {
134
137
  if (this.config.importTestFrom) return;
135
138
  const hasCustomTest = this.files.some(file => file.hasCustomTest);
136
139
  if (hasCustomTest) {
137
- (0, _utils.exitWithMessage)(`When using custom "test" function in createBdd() you should`, `set "importTestFrom" config option that points to file exporting custom test.`);
140
+ (0, _exit.exit)(`When using custom "test" function in createBdd() you should`, `set "importTestFrom" config option that points to file exporting custom test.`);
138
141
  }
139
142
  }
140
143
  async saveFiles() {
@@ -16,6 +16,7 @@ var _utils = require("../utils");
16
16
  var _testPoms = require("./testPoms");
17
17
  var _testNode = require("./testNode");
18
18
  var _stepConfig = require("../stepDefinitions/stepConfig");
19
+ var _exit = require("../utils/exit");
19
20
  /**
20
21
  * Generate test code.
21
22
  */
@@ -116,7 +117,7 @@ class TestFile {
116
117
  throw new Error(`Empty child: ${JSON.stringify(child)}`);
117
118
  }
118
119
  getScenarioLines(scenario, parent) {
119
- return isOutline(scenario) ? this.getOutlineSuite(scenario, parent) : this.getTest(scenario, parent);
120
+ return this.isOutline(scenario) ? this.getOutlineSuite(scenario, parent) : this.getTest(scenario, parent);
120
121
  }
121
122
  /**
122
123
  * Generate test.beforeEach for Background
@@ -293,12 +294,7 @@ class TestFile {
293
294
  }
294
295
  getStepKeyword(step) {
295
296
  const origKeyword = step.keyword.trim();
296
- let enKeyword;
297
- if (origKeyword === '*') {
298
- enKeyword = 'And';
299
- } else {
300
- enKeyword = this.i18nKeywordsMap ? this.i18nKeywordsMap.get(origKeyword) : origKeyword;
301
- }
297
+ const enKeyword = origKeyword === '*' ? 'And' : this.getEnglishKeyword(origKeyword);
302
298
  if (!enKeyword) throw new Error(`Keyword not found: ${origKeyword}`);
303
299
  return enKeyword;
304
300
  }
@@ -312,7 +308,7 @@ class TestFile {
312
308
  if (resolvedFixtures.length !== 1) {
313
309
  const suggestedTags = resolvedFixtures.filter(f => !f.byTag).map(f => (0, _testPoms.buildFixtureTag)(f.name)).join(', ');
314
310
  const suggestedTagsStr = suggestedTags.length ? ` or set one of the following tags: ${suggestedTags}` : '.';
315
- (0, _utils.exitWithMessage)(`Can't guess fixture for decorator step "${pickleStep.text}" in file: ${this.sourceFile}.`, `Please refactor your Page Object classes${suggestedTagsStr}`);
311
+ (0, _exit.exit)(`Can't guess fixture for decorator step "${pickleStep.text}" in file: ${this.sourceFile}.`, `Please refactor your Page Object classes${suggestedTagsStr}`);
316
312
  }
317
313
  const fixtureName = resolvedFixtures[0].name;
318
314
  return {
@@ -349,8 +345,12 @@ class TestFile {
349
345
  // see: https://github.com/cucumber/tag-expressions/tree/main/javascript
350
346
  return ((_this$options$tagsExp = this.options.tagsExpression) === null || _this$options$tagsExp === void 0 ? void 0 : _this$options$tagsExp.evaluate(node.tags)) === false;
351
347
  }
348
+ isOutline(scenario) {
349
+ const keyword = this.getEnglishKeyword(scenario.keyword);
350
+ return keyword === 'ScenarioOutline' || keyword === 'Scenario Outline' || keyword === 'Scenario Template';
351
+ }
352
+ getEnglishKeyword(keyword) {
353
+ return this.i18nKeywordsMap ? this.i18nKeywordsMap.get(keyword) : keyword;
354
+ }
352
355
  }
353
- exports.TestFile = TestFile;
354
- function isOutline(scenario) {
355
- return scenario.keyword === 'Scenario Outline' || scenario.keyword === 'Scenario Template';
356
- }
356
+ exports.TestFile = TestFile;
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.TestPoms = void 0;
7
7
  exports.buildFixtureTag = buildFixtureTag;
8
8
  var _poms = require("../stepDefinitions/decorators/poms");
9
- var _utils = require("../utils");
9
+ var _exit = require("../utils/exit");
10
10
  /**
11
11
  * Track PomNodes used in the particular test.
12
12
  * To select correct fixture for decorator steps.
@@ -110,7 +110,7 @@ class TestPoms {
110
110
  if (!usedPom.byTag) return;
111
111
  const childFixturesBySteps = childFixtures.filter(f => !f.byTag);
112
112
  if (childFixturesBySteps.length) {
113
- (0, _utils.exitWithMessage)(`Scenario "${this.title}" contains ${childFixturesBySteps.length} step(s)`, `not compatible with required fixture "${pomNode.fixtureName}"`);
113
+ (0, _exit.exit)(`Scenario "${this.title}" contains ${childFixturesBySteps.length} step(s)`, `not compatible with required fixture "${pomNode.fixtureName}"`);
114
114
  }
115
115
  }
116
116
  }
@@ -10,7 +10,7 @@ var _path = _interopRequireDefault(require("path"));
10
10
  var _fs = _interopRequireDefault(require("fs"));
11
11
  var _utils = require("./utils");
12
12
  var _transform = require("./transform");
13
- var _utils2 = require("../utils");
13
+ var _exit = require("../utils/exit");
14
14
  /**
15
15
  * Loading Playwright config.
16
16
  * See: https://github.com/microsoft/playwright/blob/main/packages/playwright-test/src/common/configLoader.ts
@@ -37,6 +37,6 @@ function getConfigFilePath(cliConfigPath) {
37
37
  function assertConfigFileExists(resolvedConfigFile, cliConfigPath) {
38
38
  if (!resolvedConfigFile || !_fs.default.existsSync(resolvedConfigFile)) {
39
39
  const configFilePath = getConfigFilePath(cliConfigPath);
40
- (0, _utils2.exitWithMessage)(`Can't find Playwright config file in: ${configFilePath}`);
40
+ (0, _exit.exit)(`Can't find Playwright config file in: ${configFilePath}`);
41
41
  }
42
42
  }
@@ -3,8 +3,8 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.getTestImpl = getTestImpl;
7
6
  exports.isParentChildTest = isParentChildTest;
7
+ exports.runStepWithCustomLocation = runStepWithCustomLocation;
8
8
  var _test = require("@playwright/test");
9
9
  var _utils = require("../utils");
10
10
  /**
@@ -22,6 +22,22 @@ function getTestFixtures(test) {
22
22
  function getTestImpl(test) {
23
23
  return test[testTypeSymbol];
24
24
  }
25
+ /**
26
+ * Run step with location pointing to Given, When, Then call.
27
+ */
28
+ // eslint-disable-next-line max-params
29
+ 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
+ });
40
+ }
25
41
  /**
26
42
  * Returns true if all fixtures of parent test found in child test.
27
43
  */
@@ -28,7 +28,10 @@ function getPlaywrightRoot() {
28
28
  // See: https://github.com/microsoft/playwright/pull/26946
29
29
  const playwrightTestRoot = (0, _utils.resolvePackageRoot)('@playwright/test');
30
30
  const libDir = _path.default.join(playwrightTestRoot, 'lib');
31
- playwrightRoot = _fs.default.existsSync(libDir) ? playwrightTestRoot : (0, _utils.resolvePackageRoot)('playwright');
31
+
32
+ // Recently added functionality in playwright
33
+ const playwrightDir = _path.default.join(playwrightTestRoot, 'node_modules', 'playwright');
34
+ playwrightRoot = _fs.default.existsSync(libDir) ? playwrightTestRoot : playwrightDir;
32
35
  }
33
36
  return playwrightRoot;
34
37
  }
@@ -27,7 +27,8 @@ class BddWorld extends _cucumber.World {
27
27
  // attach custom fixtures to world - the only way to pass them to cucumber step fn
28
28
  this.customFixtures = customFixtures;
29
29
  const code = (0, _defineStep.getStepCode)(stepDefinition);
30
- // get location of step call in generated test file
30
+ // Get location of step call in generated test file.
31
+ // This call must be exactly here to have correct call stack.
31
32
  const location = (0, _getLocationInFile.getLocationInFile)(this.test.info().file);
32
33
  const {
33
34
  parameters
@@ -39,7 +40,7 @@ class BddWorld extends _cucumber.World {
39
40
  },
40
41
  world: this
41
42
  });
42
- const res = await (0, _testTypeImpl.getTestImpl)(this.test)._step(location, text, () => code.apply(this, parameters));
43
+ const res = await (0, _testTypeImpl.runStepWithCustomLocation)(this.test, text, location, () => code.apply(this, parameters));
43
44
  delete this.customFixtures;
44
45
  return res;
45
46
  }
@@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.Snippets = void 0;
7
7
  var _loadSnippetBuilder = require("../cucumber/loadSnippetBuilder");
8
- var _utils = require("../utils");
9
8
  var _logger = require("../utils/logger");
10
9
  var _stepConfig = require("../stepDefinitions/stepConfig");
11
10
  /**
@@ -23,13 +22,15 @@ class Snippets {
23
22
  this.runConfiguration = runConfiguration;
24
23
  this.supportCodeLibrary = supportCodeLibrary;
25
24
  }
26
- async printSnippetsAndExit() {
25
+ async print() {
27
26
  this.snippetBuilder = await this.createSnippetBuilder();
28
27
  const snippets = this.getSnippets();
29
28
  this.printHeader();
30
29
  this.printSnippets(snippets);
31
30
  this.printFooter(snippets);
31
+ // exit();
32
32
  }
33
+
33
34
  async createSnippetBuilder() {
34
35
  const {
35
36
  snippetInterface
@@ -117,15 +118,18 @@ class Snippets {
117
118
  _logger.logger.error(snippets.concat(['']).join('\n\n'));
118
119
  }
119
120
  printFooter(snippets) {
120
- (0, _utils.exitWithMessage)(`Missing step definitions (${snippets.length}).`, 'Use snippets above to create them.', this.getWarnOnZeroScannedFiles());
121
+ _logger.logger.error(`Missing step definitions (${snippets.length}).`, `Use snippets above to create them.`);
122
+ this.printWarningOnZeroScannedFiles();
121
123
  }
122
- getWarnOnZeroScannedFiles() {
124
+ printWarningOnZeroScannedFiles() {
123
125
  const {
124
126
  requirePaths,
125
127
  importPaths
126
128
  } = this.supportCodeLibrary.originalCoordinates;
127
129
  const scannedFilesCount = requirePaths.length + importPaths.length;
128
- return scannedFilesCount === 0 && !this.isDecorators() ? `\nNote that 0 step definition files found, check the config.` : '';
130
+ if (scannedFilesCount === 0 && !this.isDecorators()) {
131
+ _logger.logger.error(`Note that 0 step definition files found, check the config.`);
132
+ }
129
133
  }
130
134
  }
131
135
  exports.Snippets = Snippets;
@@ -6,10 +6,10 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.createBdd = createBdd;
7
7
  exports.extractFixtureNames = extractFixtureNames;
8
8
  var _fixtureParameterNames = require("../playwright/fixtureParameterNames");
9
- var _utils = require("../utils");
10
9
  var _bddFixtures = require("../run/bddFixtures");
11
10
  var _testTypeImpl = require("../playwright/testTypeImpl");
12
11
  var _defineStep = require("./defineStep");
12
+ var _exit = require("../utils/exit");
13
13
  /**
14
14
  * Stuff related to writing steps in Playwright-style.
15
15
  */
@@ -43,7 +43,7 @@ function extractFixtureNames(fn) {
43
43
  function isCustomTest(customTest) {
44
44
  const isCustomTest = Boolean(customTest && customTest !== _bddFixtures.test);
45
45
  if (isCustomTest && customTest && !(0, _testTypeImpl.isParentChildTest)(_bddFixtures.test, customTest)) {
46
- (0, _utils.exitWithMessage)(`createBdd() should use test extended from "playwright-bdd"`);
46
+ (0, _exit.exit)(`createBdd() should use test extended from "@zohodesk/testinglibrary"`);
47
47
  }
48
48
  return isCustomTest;
49
49
  }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.Fixture = Fixture;
7
7
  exports.getPomNodeByFixtureName = getPomNodeByFixtureName;
8
8
  var _steps = require("./steps");
9
+ var _exit = require("../../utils/exit");
9
10
  /**
10
11
  * POM classes marked with @Fixture
11
12
  */
@@ -20,25 +21,38 @@ const pomGraph = new Map();
20
21
  */
21
22
  function Fixture(fixtureName) {
22
23
  // context parameter is required for decorator by TS even though it's not used
23
- // eslint-disable-next-line no-unused-vars
24
- return (Ctor, _context) => {
24
+ return Ctor => {
25
25
  createPomNode(Ctor, fixtureName);
26
26
  };
27
27
  }
28
28
  function createPomNode(Ctor, fixtureName) {
29
29
  const pomNode = {
30
30
  fixtureName,
31
+ className: Ctor.name,
31
32
  children: new Set()
32
33
  };
34
+ ensureUniqueFixtureName(pomNode);
33
35
  pomGraph.set(Ctor, pomNode);
34
36
  (0, _steps.linkStepsWithPomNode)(Ctor, pomNode);
35
37
  linkParentWithPomNode(Ctor, pomNode);
36
38
  return pomNode;
37
39
  }
40
+ function ensureUniqueFixtureName({
41
+ fixtureName,
42
+ className
43
+ }) {
44
+ if (!fixtureName) {
45
+ return;
46
+ }
47
+ const existingPom = getPomNodeByFixtureName(fixtureName);
48
+ if (existingPom) {
49
+ (0, _exit.exit)(`Duplicate fixture name "${fixtureName}"`, `defined for classes: ${existingPom.className}, ${className}`);
50
+ }
51
+ }
38
52
  function linkParentWithPomNode(Ctor, pomNode) {
39
53
  const parentCtor = Object.getPrototypeOf(Ctor);
40
54
  if (!parentCtor) return;
41
- // if parentCtor is not in pomGraph, add it as well
55
+ // if parentCtor is not in pomGraph, add it.
42
56
  // Case: parent class is not marked with @Fixture, but has decorator steps (base class)
43
57
  const parentPomNode = pomGraph.get(parentCtor) || createPomNode(parentCtor, '');
44
58
  parentPomNode.children.add(pomNode);
@@ -7,7 +7,7 @@ exports.buildCucumberStepCode = buildCucumberStepCode;
7
7
  exports.defineStep = defineStep;
8
8
  exports.getStepCode = getStepCode;
9
9
  var _cucumber = require("@cucumber/cucumber");
10
- var _utils = require("../utils");
10
+ var _exit = require("../utils/exit");
11
11
  /**
12
12
  * Defines step by config.
13
13
  * Calls cucumber's Given(), When(), Then() under the hood.
@@ -26,7 +26,7 @@ function defineStep(stepConfig) {
26
26
  // and skip/delay registering cucumber steps until cucumber is loaded
27
27
  const isMissingCucumber = /Cucumber that isn't running/i.test(e.message);
28
28
  if (isMissingCucumber) {
29
- (0, _utils.exitWithMessage)(`Option "importTestFrom" should point to separate file without step definitions`, `(e.g. without calls of Given, When, Then)`);
29
+ (0, _exit.exit)(`Option "importTestFrom" should point to a separate file without step definitions`, `(e.g. without calls of Given, When, Then)`);
30
30
  } else {
31
31
  throw e;
32
32
  }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.exit = exit;
7
+ exports.withExitHandler = withExitHandler;
8
+ var _logger = require("./logger");
9
+ var _worker_threads = require("worker_threads");
10
+ /**
11
+ * Exit utils.
12
+ *
13
+ * When calling process.exit() in worker thread used for file generation,
14
+ * logs are not flushed (https://github.com/vitalets/playwright-bdd/issues/59).
15
+ * That's why instead of process.exit we throw ExitError
16
+ * that just sets process.exitCode = 1 and allow program to exit normally.
17
+ *
18
+ * On the other hand, when running in main thread, especially inside Playwright,
19
+ * thrown error is captured by Playwright and show with additional messages (e.g. no tests found).
20
+ * That's why in main thread we to call process.exit() to show only needed error.
21
+ *
22
+ * Relevant discussions:
23
+ * - https://github.com/nodejs/node/issues/6379
24
+ * - https://github.com/nodejs/node-v0.x-archive/issues/3737
25
+ * - https://github.com/cucumber/cucumber-js/pull/123
26
+ */
27
+
28
+ class ExitError extends Error {
29
+ get stack() {
30
+ return '';
31
+ }
32
+ }
33
+ async function withExitHandler(fn) {
34
+ try {
35
+ return await fn();
36
+ } catch (e) {
37
+ if (e instanceof ExitError) {
38
+ process.exitCode = 1;
39
+ } else {
40
+ throw e;
41
+ }
42
+ }
43
+ }
44
+ function exit(...messages) {
45
+ messages = messages.filter(Boolean);
46
+ if (_worker_threads.isMainThread) {
47
+ if (messages.length) _logger.logger.error('Error:', ...messages);
48
+ process.exit(1);
49
+ } else {
50
+ throw new ExitError(messages.join(' '));
51
+ }
52
+ }
@@ -4,7 +4,6 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.exitWithMessage = exitWithMessage;
8
7
  exports.getPackageVersion = getPackageVersion;
9
8
  exports.getSymbolByName = getSymbolByName;
10
9
  exports.removeDuplicates = removeDuplicates;
@@ -12,11 +11,6 @@ exports.resolvePackageRoot = resolvePackageRoot;
12
11
  exports.template = template;
13
12
  var _fs = _interopRequireDefault(require("fs"));
14
13
  var _path = _interopRequireDefault(require("path"));
15
- var _logger = require("./logger");
16
- function exitWithMessage(...messages) {
17
- _logger.logger.error('ERROR:', ...messages.filter(Boolean));
18
- process.exit(1);
19
- }
20
14
  // See: https://stackoverflow.com/questions/50453640/how-can-i-get-the-value-of-a-symbol-property
21
15
  function getSymbolByName(target, name) {
22
16
  const ownKeys = Reflect.ownKeys(target);
@@ -36,9 +36,20 @@ function getDefaultConfig() {
36
36
  }
37
37
  function combineDefaultConfigWithUserConfig(userConfiguration) {
38
38
  let defaultConfig = getDefaultConfig();
39
+ let configurationObj = {};
40
+ Object.keys(userConfiguration).forEach(configKey => {
41
+ let configValue = userConfiguration[configKey];
42
+ if (configValue && configValue !== null) {
43
+ configurationObj[configKey] = configValue;
44
+ } else if (defaultConfig[configKey]) {
45
+ configurationObj[configKey] = defaultConfig[configKey];
46
+ } else {
47
+ _logger.Logger.log(_logger.Logger.INFO_TYPE, `key - ${configKey} is not yet supported in uat configuration. This will not be used while creating playwright configuration`);
48
+ }
49
+ });
39
50
  return {
40
51
  ...defaultConfig,
41
- ...userConfiguration
52
+ ...configurationObj
42
53
  };
43
54
  }
44
55
 
@@ -20,7 +20,9 @@ const {
20
20
  expectTimeout,
21
21
  testTimeout,
22
22
  authFilePath,
23
- viewport
23
+ viewport,
24
+ featureFilesFolder,
25
+ stepDefinitionsFolder
24
26
  } = (0, _readConfigFile.generateConfigFromFile)();
25
27
  const projects = (0, _configUtils.getProjects)({
26
28
  browsers,
@@ -30,7 +32,10 @@ const projects = (0, _configUtils.getProjects)({
30
32
  testTimeout,
31
33
  viewport
32
34
  });
33
- const testDir = (0, _configUtils.getTestDir)(bddMode, process.cwd());
35
+ const testDir = (0, _configUtils.getTestDir)(bddMode, process.cwd(), {
36
+ featureFilesFolder,
37
+ stepDefinitionsFolder
38
+ });
34
39
  function getPlaywrightConfig() {
35
40
  return {
36
41
  testDir,
@@ -98,10 +98,12 @@ function getProjects({
98
98
  viewport
99
99
  })).filter(Boolean);
100
100
  }
101
- function getTestDir(bddMode, cwd) {
101
+ function getTestDir(bddMode, cwd, {
102
+ stepDefinitionsFolder
103
+ }) {
102
104
  return bddMode ? (0, _bddFramework.defineBddConfig)({
103
105
  paths: [_path.default.join(cwd, 'uat', '**', '*.feature')],
104
- import: [_path.default.join(cwd, 'uat', '**', 'steps', '*.spec.js')],
106
+ import: [_path.default.join(cwd, 'uat', '**', stepDefinitionsFolder, '*.spec.js')],
105
107
  featuresRoot: _path.default.join(cwd, 'uat'),
106
108
  outputDir: _path.default.join(cwd, 'uat', '.features-gen'),
107
109
  publish: true
@@ -73,6 +73,14 @@ function runPlaywright(command, args) {
73
73
  resolve();
74
74
  }
75
75
  });
76
+ process.on('exit', () => {
77
+ //childProcessForRunningPlaywright.kill();
78
+ reject('Terminating Playwright Process...');
79
+ });
80
+ process.on('SIGINT', () => {
81
+ //childProcessForRunningPlaywright.kill();
82
+ reject('Cleaning up...');
83
+ });
76
84
  });
77
85
  }
78
86
  function main() {
@@ -97,7 +105,7 @@ function main() {
97
105
  }
98
106
  Promise.all(promises).then(() => runPlaywright(command, args)).catch(err => {
99
107
  _logger.Logger.log(_logger.Logger.FAILURE_TYPE, err);
100
- process.exit(1);
108
+ process.exit();
101
109
  });
102
110
  }
103
111
  var _default = main;
@@ -36,7 +36,8 @@ module.exports = {
36
36
  authFilePath: 'uat/playwright/.auth/user.json',
37
37
  trace: true,
38
38
  video: true,
39
+ bddMode: true,
39
40
  featureFilesFolder: 'feature-files',
40
41
  stepDefinitionsFolder: 'steps',
41
- viewport: null
42
+ viewport: { width: 1280, height: 720 }
42
43
  }
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## Framework that abstracts the configuration for playwright and Jest
4
4
 
5
+ # 0.1.3
6
+
7
+ - uat config sample file updated with `bddMode` and `viewport` values
8
+ - user configuration issue fix when the value is undefined
9
+
10
+ # 0.1.2
11
+
12
+ - Bdd version updated to `5.4.0`
13
+ - Playwright version updated to `1.39.0`
14
+
5
15
  # 0.1.1
6
16
 
7
17
  - Fixed post install script error
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zohodesk/testinglibrary",
3
- "version": "0.1.0-exp.4",
3
+ "version": "0.1.1-exp.1",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
@@ -2577,13 +2577,22 @@
2577
2577
  "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q=="
2578
2578
  },
2579
2579
  "@playwright/test": {
2580
- "version": "1.37.1",
2581
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.1.tgz",
2582
- "integrity": "sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg==",
2580
+ "version": "1.39.0",
2581
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz",
2582
+ "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==",
2583
2583
  "requires": {
2584
- "@types/node": "*",
2585
- "fsevents": "2.3.2",
2586
- "playwright-core": "1.37.1"
2584
+ "playwright": "1.39.0"
2585
+ },
2586
+ "dependencies": {
2587
+ "playwright": {
2588
+ "version": "1.39.0",
2589
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz",
2590
+ "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==",
2591
+ "requires": {
2592
+ "fsevents": "2.3.2",
2593
+ "playwright-core": "1.39.0"
2594
+ }
2595
+ }
2587
2596
  }
2588
2597
  },
2589
2598
  "@sinclair/typebox": {
@@ -6715,9 +6724,9 @@
6715
6724
  }
6716
6725
  },
6717
6726
  "playwright-core": {
6718
- "version": "1.37.1",
6719
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz",
6720
- "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA=="
6727
+ "version": "1.39.0",
6728
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
6729
+ "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw=="
6721
6730
  },
6722
6731
  "pretty-format": {
6723
6732
  "version": "29.7.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zohodesk/testinglibrary",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "@babel/preset-react": "7.22.5",
24
24
  "@cucumber/cucumber": "9.2.0",
25
- "@playwright/test": "1.37.1",
25
+ "@playwright/test": "1.39.0",
26
26
  "@testing-library/jest-dom": "5.11.9",
27
27
  "@testing-library/react": "11.2.7",
28
28
  "@testing-library/react-hooks": "7.0.2",