create-appraisejs 0.1.9 → 0.1.10-alpha.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 (159) hide show
  1. package/README.md +24 -17
  2. package/dist/cli.d.ts +2 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.e2e.test.js +11 -8
  5. package/dist/cli.e2e.test.js.map +1 -1
  6. package/dist/cli.js +32 -48
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +5 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/config.test.js +9 -5
  12. package/dist/config.test.js.map +1 -1
  13. package/dist/copy-template.d.ts +1 -1
  14. package/dist/copy-template.d.ts.map +1 -1
  15. package/dist/copy-template.js +7 -3
  16. package/dist/copy-template.js.map +1 -1
  17. package/dist/copy-template.test.js +14 -9
  18. package/dist/copy-template.test.js.map +1 -1
  19. package/dist/create-project.d.ts +23 -0
  20. package/dist/create-project.d.ts.map +1 -0
  21. package/dist/create-project.js +58 -0
  22. package/dist/create-project.js.map +1 -0
  23. package/dist/create-project.test.d.ts +2 -0
  24. package/dist/create-project.test.d.ts.map +1 -0
  25. package/dist/create-project.test.js +80 -0
  26. package/dist/create-project.test.js.map +1 -0
  27. package/dist/install.d.ts +8 -4
  28. package/dist/install.d.ts.map +1 -1
  29. package/dist/install.js +22 -72
  30. package/dist/install.js.map +1 -1
  31. package/dist/install.test.js +26 -10
  32. package/dist/install.test.js.map +1 -1
  33. package/dist/package-manager.d.ts +11 -0
  34. package/dist/package-manager.d.ts.map +1 -0
  35. package/dist/package-manager.js +47 -0
  36. package/dist/package-manager.js.map +1 -0
  37. package/dist/package-manager.test.d.ts +2 -0
  38. package/dist/package-manager.test.d.ts.map +1 -0
  39. package/dist/package-manager.test.js +51 -0
  40. package/dist/package-manager.test.js.map +1 -0
  41. package/dist/prepare-template-utils.d.ts +10 -0
  42. package/dist/prepare-template-utils.d.ts.map +1 -0
  43. package/dist/prepare-template-utils.js +53 -0
  44. package/dist/prepare-template-utils.js.map +1 -0
  45. package/dist/prepare-template-utils.test.d.ts +2 -0
  46. package/dist/prepare-template-utils.test.d.ts.map +1 -0
  47. package/dist/prepare-template-utils.test.js +67 -0
  48. package/dist/prepare-template-utils.test.js.map +1 -0
  49. package/dist/prompts.d.ts +2 -0
  50. package/dist/prompts.d.ts.map +1 -1
  51. package/dist/prompts.js +11 -3
  52. package/dist/prompts.js.map +1 -1
  53. package/dist/prompts.test.js +17 -7
  54. package/dist/prompts.test.js.map +1 -1
  55. package/dist/template-sync-utils.test.d.ts +2 -0
  56. package/dist/template-sync-utils.test.d.ts.map +1 -0
  57. package/dist/template-sync-utils.test.js +41 -0
  58. package/dist/template-sync-utils.test.js.map +1 -0
  59. package/package.json +3 -2
  60. package/templates/default/.appraise-template-meta.json +5 -0
  61. package/templates/default/.env.example +1 -1
  62. package/templates/default/.vscode/settings.json +10 -3
  63. package/templates/default/README.md +27 -25
  64. package/templates/default/automation/features/base/login.feature +15 -0
  65. package/templates/default/automation/locators/base/home.json +3 -0
  66. package/templates/default/automation/locators/base/login.json +6 -0
  67. package/templates/default/automation/locators/base/test.json +1 -0
  68. package/templates/default/automation/mapping/locator-map.json +14 -0
  69. package/templates/default/{src/tests → automation}/steps/actions/click.step.ts +1 -4
  70. package/templates/default/{src/tests → automation}/steps/actions/hover.step.ts +1 -4
  71. package/templates/default/{src/tests → automation}/steps/actions/input.step.ts +1 -4
  72. package/templates/default/{src/tests → automation}/steps/actions/navigation.step.ts +1 -3
  73. package/templates/default/{src/tests → automation}/steps/actions/random_data.step.ts +1 -3
  74. package/templates/default/{src/tests → automation}/steps/actions/store.step.ts +1 -4
  75. package/templates/default/automation/steps/actions/wait.step.ts +91 -0
  76. package/templates/default/{src/tests → automation}/steps/validations/active_state_assertion.step.ts +1 -4
  77. package/templates/default/{src/tests → automation}/steps/validations/navigation_assertion.step.ts +1 -2
  78. package/templates/default/{src/tests → automation}/steps/validations/text_assertion.step.ts +1 -4
  79. package/templates/default/{src/tests → automation}/steps/validations/visibility_assertion.step.ts +1 -4
  80. package/templates/default/cucumber.mjs +6 -6
  81. package/templates/default/eslint.config.mjs +5 -4
  82. package/templates/default/package-lock.json +322 -485
  83. package/templates/default/package.json +11 -6
  84. package/templates/default/packages/cucumber-runtime/package.json +13 -0
  85. package/templates/default/packages/cucumber-runtime/src/cache.util.ts +93 -0
  86. package/templates/default/packages/cucumber-runtime/src/cli.ts +68 -0
  87. package/templates/default/packages/cucumber-runtime/src/environment.util.ts +21 -0
  88. package/templates/default/packages/cucumber-runtime/src/executor.ts +32 -0
  89. package/templates/default/{src/tests/hooks → packages/cucumber-runtime/src}/hooks.ts +17 -32
  90. package/templates/default/packages/cucumber-runtime/src/index.ts +17 -0
  91. package/templates/default/{src/tests/utils → packages/cucumber-runtime/src}/locator.util.ts +50 -64
  92. package/templates/default/packages/cucumber-runtime/src/parameter-types.ts +7 -0
  93. package/templates/default/packages/cucumber-runtime/src/paths.ts +33 -0
  94. package/templates/default/packages/cucumber-runtime/src/random-data.util.ts +35 -0
  95. package/templates/default/packages/cucumber-runtime/src/types.ts +13 -0
  96. package/templates/default/{src/tests/config/executor → packages/cucumber-runtime/src}/world.ts +4 -1
  97. package/templates/default/packages/cucumber-runtime/tsconfig.json +11 -0
  98. package/templates/default/scripts/setup-env.ts +4 -4
  99. package/templates/default/scripts/sync-appraise-base-template.ts +123 -105
  100. package/templates/default/scripts/sync-environments.ts +8 -5
  101. package/templates/default/scripts/sync-locator-groups.ts +7 -10
  102. package/templates/default/scripts/sync-locators.ts +5 -9
  103. package/templates/default/scripts/sync-modules.ts +9 -17
  104. package/templates/default/scripts/sync-tags.ts +2 -2
  105. package/templates/default/scripts/sync-template-step-groups.ts +16 -6
  106. package/templates/default/scripts/sync-template-steps.ts +16 -5
  107. package/templates/default/scripts/sync-test-cases.ts +6 -3
  108. package/templates/default/scripts/sync-test-suites.ts +7 -4
  109. package/templates/default/src/actions/environments/environment-actions.ts +6 -23
  110. package/templates/default/src/actions/locator/locator-actions.ts +36 -93
  111. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +24 -78
  112. package/templates/default/src/actions/modules/module-actions.ts +4 -2
  113. package/templates/default/src/actions/tags/tag-actions.ts +4 -1
  114. package/templates/default/src/actions/template-step/template-step-actions.ts +10 -101
  115. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +31 -130
  116. package/templates/default/src/actions/test-case/test-case-actions.ts +31 -94
  117. package/templates/default/src/actions/test-run/test-run-actions.ts +11 -13
  118. package/templates/default/src/actions/test-suite/test-suite-actions.ts +29 -82
  119. package/templates/default/src/app/(base)/locator-groups/page.tsx +1 -3
  120. package/templates/default/src/app/(base)/reports/page.tsx +1 -1
  121. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +2 -2
  122. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +1 -1
  123. package/templates/default/src/app/(base)/tags/page.tsx +2 -2
  124. package/templates/default/src/app/(base)/template-steps/page.tsx +1 -2
  125. package/templates/default/src/app/(base)/test-runs/page.tsx +2 -2
  126. package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +2 -1
  127. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +2 -1
  128. package/templates/default/src/app/page.tsx +4 -5
  129. package/templates/default/src/components/diagram/dynamic-parameters.tsx +76 -40
  130. package/templates/default/src/components/diagram/options-header-node.tsx +1 -1
  131. package/templates/default/src/components/ui/data-table.tsx +33 -39
  132. package/templates/default/src/lib/automation/paths.ts +181 -0
  133. package/templates/default/src/lib/automation/projection-service.ts +230 -0
  134. package/templates/default/src/lib/environment-file-utils.ts +14 -51
  135. package/templates/default/src/lib/executor/local-executor-adapter.ts +101 -0
  136. package/templates/default/src/lib/executor/types.ts +24 -0
  137. package/templates/default/src/lib/feature-file-generator.ts +22 -112
  138. package/templates/default/src/lib/locator-group-file-utils.ts +57 -120
  139. package/templates/default/src/lib/process/task-spawner.ts +236 -0
  140. package/templates/default/src/lib/template-sync-utils.d.ts +7 -0
  141. package/templates/default/src/lib/template-sync-utils.d.ts.map +1 -0
  142. package/templates/default/src/lib/template-sync-utils.js +47 -0
  143. package/templates/default/src/lib/template-sync-utils.js.map +1 -0
  144. package/templates/default/src/lib/template-sync-utils.ts +63 -0
  145. package/templates/default/src/lib/test-run/process-manager.ts +9 -87
  146. package/templates/default/src/lib/test-run/test-run-executor.ts +7 -136
  147. package/templates/default/src/lib/test-run/winston-logger.ts +6 -35
  148. package/templates/default/src/lib/utils/template-step-file-generator.ts +22 -85
  149. package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +7 -22
  150. package/templates/default/public/favicon.ico +0 -0
  151. package/templates/default/src/tests/executor.ts +0 -80
  152. package/templates/default/src/tests/mapping/locator-map.json +0 -1
  153. package/templates/default/src/tests/steps/actions/wait.step.ts +0 -107
  154. package/templates/default/src/tests/support/parameter-types.ts +0 -12
  155. package/templates/default/src/tests/utils/cache.util.ts +0 -260
  156. package/templates/default/src/tests/utils/cli.util.ts +0 -177
  157. package/templates/default/src/tests/utils/environment.util.ts +0 -65
  158. package/templates/default/src/tests/utils/random-data.util.ts +0 -45
  159. package/templates/default/src/tests/utils/spawner.util.ts +0 -617
@@ -278,24 +278,9 @@ interface RequiredImport {
278
278
 
279
279
  const REQUIRED_IMPORTS: RequiredImport[] = [
280
280
  {
281
- module: '@cucumber/cucumber',
282
- namedExports: ['When'],
283
- from: '@cucumber/cucumber',
284
- },
285
- {
286
- module: '../../config/executor/world',
287
- namedExports: ['CustomWorld'],
288
- from: '../../config/executor/world.js',
289
- },
290
- {
291
- module: '@/types/locator/locator.type',
292
- namedExports: ['SelectorName'],
293
- from: '@/types/locator/locator.type',
294
- },
295
- {
296
- module: '../../utils/locator.util',
297
- namedExports: ['resolveLocator'],
298
- from: '../../utils/locator.util.js',
281
+ module: '../../../packages/cucumber-runtime/src/index',
282
+ namedExports: ['When', 'Then', 'CustomWorld', 'expect', 'SelectorName', 'resolveLocator', 'getEnvironment', 'generateRandomData', 'RandomDataType'],
283
+ from: '../../../packages/cucumber-runtime/src/index.js',
299
284
  },
300
285
  ]
301
286
 
@@ -627,10 +612,7 @@ export async function createTemplateStepGroupFile(
627
612
 
628
613
  // Generate content with JSDoc at the top, then imports, then placeholder comment
629
614
  const groupJSDoc = generateGroupJSDocComment(groupName, description || null, type)
630
- const requiredImports = `import { When } from '@cucumber/cucumber';
631
- import { CustomWorld } from '../../config/executor/world.js';
632
- import { SelectorName } from '@/types/locator/locator.type';
633
- import { resolveLocator } from '../../utils/locator.util.js';
615
+ const requiredImports = `import { When, Then, CustomWorld, expect, SelectorName, resolveLocator, getEnvironment, generateRandomData, RandomDataType } from '../../../packages/cucumber-runtime/src/index.js';
634
616
 
635
617
  `
636
618
  const placeholderComment =
@@ -721,3 +703,6 @@ export async function renameTemplateStepGroupFile(
721
703
  throw new Error(`File rename failed: ${error}`)
722
704
  }
723
705
  }
706
+
707
+
708
+
@@ -1,80 +0,0 @@
1
- import { BrowserName } from '@/types/executor/browser.type'
2
- import { CliOptions, startCli } from '@/tests/utils/cli.util.js'
3
- import { spawnTask, waitForTask } from '@/tests/utils/spawner.util.js'
4
- import { config } from 'dotenv'
5
-
6
- function setEnvironmentVariables(environment: string, headless: boolean, browser: BrowserName) {
7
- process.env.ENVIRONMENT = environment
8
- process.env.HEADLESS = headless.toString()
9
- process.env.BROWSER = browser
10
- }
11
-
12
- /**
13
- * Main entry point for the CLI application
14
- * Handles CLI parsing, error handling, and application lifecycle
15
- */
16
- async function bootstrap(): Promise<void> {
17
- // Load environment variables
18
- config()
19
-
20
- try {
21
- // Parse CLI arguments and get options
22
- const { environment, tags, parallel, browser, headless }: CliOptions = startCli()
23
-
24
- // Log parsed options with better formatting
25
- console.log(
26
- `Running tests in the following configuration\nEnvironment: ${environment}\nTags: ${tags}\nParallel: ${parallel}\nBrowser: ${browser}\nHeadless: ${headless}`,
27
- )
28
-
29
- setEnvironmentVariables(environment, headless as unknown as boolean, browser as unknown as BrowserName)
30
-
31
- // Build the cucumber command with appropriate flags
32
- const cucumberArgs: string[] = []
33
-
34
- if (tags) {
35
- cucumberArgs.push('-t', tags)
36
- }
37
-
38
- if (parallel > 1) {
39
- cucumberArgs.push('--parallel', parallel.toString())
40
- }
41
-
42
- // Spawn the cucumber test process
43
- console.log('🚀 Starting cucumber test process...')
44
- await spawnTask('npm', ['run', 'test', ...cucumberArgs], {
45
- streamLogs: true,
46
- prefixLogs: true,
47
- logPrefix: 'cucumber-test',
48
- captureOutput: true,
49
- })
50
-
51
- // Wait for the test process to complete
52
- const exitCode = await waitForTask('cucumber-test')
53
-
54
- if (exitCode === 0) {
55
- console.log('✅ Tests completed successfully')
56
- } else {
57
- console.log(`❌ Tests failed with exit code: ${exitCode}`)
58
- }
59
-
60
- process.exit(exitCode || 0)
61
- } catch (error) {
62
- console.error('❌ Application failed with error:')
63
- console.error(error instanceof Error ? error.message : 'Unknown error occurred')
64
- process.exit(1)
65
- }
66
- }
67
-
68
- // Handle uncaught exceptions and unhandled rejections
69
- process.on('uncaughtException', error => {
70
- console.error('❌ Uncaught Exception:', error.message)
71
- process.exit(1)
72
- })
73
-
74
- process.on('unhandledRejection', reason => {
75
- console.error('❌ Unhandled Rejection:', reason)
76
- process.exit(1)
77
- })
78
-
79
- // Start the application
80
- bootstrap()
@@ -1,107 +0,0 @@
1
- /**
2
- * @name wait
3
- * @description Template steps that handles waiting
4
- * @type ACTION
5
- */
6
- import { SelectorName } from '../../../types/locator/locator.type';
7
- import { When } from '@cucumber/cucumber';
8
- import { CustomWorld } from '../../config/executor/world.js';
9
- import { resolveLocator } from '../../utils/locator.util.js';
10
-
11
- // This file is generated automatically. Add template steps to this group to generate content.
12
-
13
- /**
14
- * @name wait for page load
15
- * @description Template step for waiting till page becomes interactive
16
- * @icon WAIT
17
- */
18
- When(
19
- 'the user waits for the current page to be loaded',
20
- async function (this: CustomWorld) {
21
- try {
22
- await this.page.waitForLoadState('domcontentloaded');
23
- } catch (error) {
24
- throw new Error(
25
- `Failed to wait for the current page to be loaded: ${error}`
26
- );
27
- }
28
- }
29
- );
30
-
31
- /**
32
- * @name wait for url route
33
- * @description Template step for waiting for a url route to be loaded
34
- * @icon WAIT
35
- */
36
- When(
37
- 'the user waits for the route {string} to be loaded',
38
- async function (this: CustomWorld, routeName: string) {
39
- try {
40
- await this.page.waitForURL(routeName, { waitUntil: 'domcontentloaded' });
41
- } catch (error) {
42
- throw new Error(
43
- `Failed to wait for the route ${routeName} to be loaded: ${error}`
44
- );
45
- }
46
- }
47
- );
48
-
49
- /**
50
- * @name wait for element
51
- * @description Template step for waiting for element to become visible
52
- * @icon WAIT
53
- */
54
- When(
55
- 'the user waits for the element {string} to become visible',
56
- async function (this: CustomWorld, elementName: SelectorName) {
57
- try {
58
- const selector = await resolveLocator(this.page, elementName);
59
- if (!selector) {
60
- throw new Error(`Selector ${elementName} not found`);
61
- }
62
- await this.page.waitForSelector(selector, { state: 'visible' });
63
- } catch (error) {
64
- throw new Error(
65
- `Failed to wait for the element ${elementName} to become visible: ${error}`
66
- );
67
- }
68
- }
69
- );
70
-
71
- /**
72
- * @name wait for element to disappear
73
- * @description Template step for waiting for an element to disappear from viewport
74
- * @icon WAIT
75
- */
76
- When(
77
- 'the user waits for the {string} element to disappear',
78
- async function (this: CustomWorld, elementName: SelectorName) {
79
- try {
80
- const selector = await resolveLocator(this.page, elementName);
81
- if (!selector) {
82
- throw new Error(`Selector ${elementName} not found`);
83
- }
84
- await this.page.waitForSelector(selector, { state: 'hidden' });
85
- } catch (error) {
86
- throw new Error(
87
- `Failed to wait for the ${elementName} element to disappear: ${error}`
88
- );
89
- }
90
- }
91
- );
92
-
93
- /**
94
- * @name wait for specific amount of seconds
95
- * @description Template step for waiting for for an specific amount of seconds before proceeding with next action
96
- * @icon WAIT
97
- */
98
- When(
99
- 'the user waits for {int} seconds',
100
- async function (this: CustomWorld, waitTimeInSeconds: number) {
101
- try {
102
- await this.page.waitForTimeout(waitTimeInSeconds * 1000);
103
- } catch (error) {
104
- throw new Error(`Failed to wait for ${waitTimeInSeconds} seconds: ${error}`);
105
- }
106
- }
107
- );
@@ -1,12 +0,0 @@
1
- import { defineParameterType } from '@cucumber/cucumber'
2
-
3
- /**
4
- * Register custom parameter types used in step definitions.
5
- * Cucumber only has built-in types for {string}, {int}, {float}, {word} —
6
- * {boolean} must be defined here so steps like "should be true" work.
7
- */
8
- defineParameterType({
9
- name: 'boolean',
10
- regexp: /true|false/,
11
- transformer: (s: string) => s === 'true',
12
- })
@@ -1,260 +0,0 @@
1
- import { readFileSync } from 'fs'
2
- import { globSync } from 'glob'
3
- import { LocatorCollection, LocatorMap } from '@/types/locator/locator.type'
4
- import * as path from 'path'
5
-
6
- /**
7
- * Singleton cache for managing Playwright locator collections
8
- *
9
- * This class provides a lazy-loading cache for locator JSON files. It automatically
10
- * discovers all locator files in the configured directory and loads them on-demand
11
- * to optimize memory usage and startup time.
12
- *
13
- * Features:
14
- * - Singleton pattern ensures single instance across the application
15
- * - Lazy loading of locator files for better performance
16
- * - Automatic file discovery using glob patterns
17
- * - Error handling for malformed JSON files
18
- * - Memory-efficient with optional preloading
19
- */
20
- export class LocatorCache {
21
- /** Singleton instance of the LocatorCache */
22
- private static instance: LocatorCache
23
-
24
- /** In-memory cache of loaded locator collections, keyed by file name */
25
- private data: Record<string, LocatorCollection> = {}
26
-
27
- /** Map of file names to their full file paths */
28
- private filePaths: Record<string, string> = {}
29
-
30
- /** Set of file names that have been loaded into memory */
31
- private loadedFiles: Set<string> = new Set()
32
-
33
- /**
34
- * Private constructor for singleton pattern
35
- *
36
- * Automatically discovers all JSON files in the locator directory and
37
- * maps them by their base filename for easy lookup.
38
- *
39
- * @private
40
- */
41
- private constructor() {
42
- globSync(`${process.env.LOCATOR_LOCATION ?? 'src/tests/locators'}/**/*.json`).forEach(file => {
43
- const fileName = path.basename(file, path.extname(file))
44
- this.filePaths[fileName ?? file] = file
45
- })
46
- }
47
-
48
- /**
49
- * Gets the singleton instance of LocatorCache
50
- *
51
- * Creates a new instance if one doesn't exist, otherwise returns the existing instance.
52
- *
53
- * @returns The singleton LocatorCache instance
54
- *
55
- * @example
56
- * ```typescript
57
- * const cache = LocatorCache.getInstance();
58
- * const locators = cache.get('home');
59
- * ```
60
- */
61
- public static getInstance(): LocatorCache {
62
- if (!LocatorCache.instance) {
63
- LocatorCache.instance = new LocatorCache()
64
- }
65
- return LocatorCache.instance
66
- }
67
-
68
- /**
69
- * Retrieves a locator collection by key (filename without extension)
70
- *
71
- * Performs lazy loading - the file is only loaded from disk when first requested.
72
- * Returns a shallow copy of the data to prevent external modifications.
73
- *
74
- * @param key - The filename (without .json extension) of the locator file
75
- * @returns A shallow copy of the LocatorCollection, or null if not found or error
76
- *
77
- * @example
78
- * ```typescript
79
- * const homeLocators = cache.get('home');
80
- * if (homeLocators) {
81
- * console.log(homeLocators.loginButton); // '#login-btn'
82
- * }
83
- * ```
84
- */
85
- public get(key: string) {
86
- // Lazy load the file if not already loaded
87
- if (!this.loadedFiles.has(key) && this.filePaths[key]) {
88
- try {
89
- const filePath = this.filePaths[key]
90
- const data = JSON.parse(readFileSync(filePath, 'utf8'))
91
- this.data[key] = data as LocatorCollection
92
- this.loadedFiles.add(key)
93
- } catch (error) {
94
- console.error(`Error loading locator file for key "${key}":`, error)
95
- return null
96
- }
97
- }
98
-
99
- return this.data[key] ? { ...this.data[key] } : null
100
- }
101
-
102
- /**
103
- * Preloads all locator files into memory at once
104
- *
105
- * This method loads all discovered locator files into memory, which can be
106
- * useful for ensuring all files are available upfront or for performance
107
- * optimization when you know all locators will be needed.
108
- *
109
- * @example
110
- * ```typescript
111
- * const cache = LocatorCache.getInstance();
112
- * cache.preloadAll(); // Load all locator files now
113
- * ```
114
- */
115
- public preloadAll(): void {
116
- Object.keys(this.filePaths).forEach(key => {
117
- if (!this.loadedFiles.has(key)) {
118
- this.get(key) // This will trigger lazy loading
119
- }
120
- })
121
- }
122
-
123
- /**
124
- * Gets all available locator file keys without loading the files
125
- *
126
- * Returns the list of all discovered locator file names (without extensions)
127
- * that are available for loading. This is useful for listing available
128
- * locator collections or for validation purposes.
129
- *
130
- * @returns Array of available locator file names
131
- *
132
- * @example
133
- * ```typescript
134
- * const cache = LocatorCache.getInstance();
135
- * const availableKeys = cache.getAvailableKeys();
136
- * console.log('Available locators:', availableKeys); // ['home', 'login', 'dashboard']
137
- * ```
138
- */
139
- public getAvailableKeys(): string[] {
140
- return Object.keys(this.filePaths)
141
- }
142
-
143
- /**
144
- * Checks if a specific locator file has been loaded into memory
145
- *
146
- * @param key - The filename (without .json extension) to check
147
- * @returns True if the file is loaded in memory, false otherwise
148
- *
149
- * @example
150
- * ```typescript
151
- * const cache = LocatorCache.getInstance();
152
- * if (!cache.isLoaded('home')) {
153
- * console.log('Home locators not yet loaded');
154
- * }
155
- * ```
156
- */
157
- public isLoaded(key: string): boolean {
158
- return this.loadedFiles.has(key)
159
- }
160
- }
161
-
162
- /**
163
- * Singleton cache for managing locator path mappings
164
- *
165
- * This class provides a cache for the locator mapping configuration that
166
- * maps URL paths to their corresponding locator collection names. It loads
167
- * the mapping file once at initialization and provides fast lookups.
168
- *
169
- * Features:
170
- * - Singleton pattern ensures single instance across the application
171
- * - Loads mapping configuration at initialization
172
- * - Provides fast path-to-locator-name lookups
173
- * - Error handling for malformed mapping files
174
- */
175
- export class LocatorMapCache {
176
- /** Singleton instance of the LocatorMapCache */
177
- private static instance: LocatorMapCache
178
-
179
- /** Array of locator mappings loaded from the configuration file */
180
- private data: LocatorMap[] = []
181
-
182
- /**
183
- * Private constructor for singleton pattern
184
- *
185
- * Loads the locator mapping configuration file at initialization.
186
- * The mapping file contains path-to-locator-name mappings.
187
- *
188
- * @private
189
- */
190
- private constructor() {
191
- this.data = JSON.parse(
192
- readFileSync(process.env.LOCATOR_MAP_LOCATION ?? 'src/tests/mapping/locator-map.json', 'utf8'),
193
- )
194
- }
195
-
196
- /**
197
- * Gets the singleton instance of LocatorMapCache
198
- *
199
- * Creates a new instance if one doesn't exist, otherwise returns the existing instance.
200
- *
201
- * @returns The singleton LocatorMapCache instance
202
- *
203
- * @example
204
- * ```typescript
205
- * const mapCache = LocatorMapCache.getInstance();
206
- * const mapping = mapCache.get('/login');
207
- * ```
208
- */
209
- public static getInstance(): LocatorMapCache {
210
- if (!LocatorMapCache.instance) {
211
- LocatorMapCache.instance = new LocatorMapCache()
212
- }
213
- return LocatorMapCache.instance
214
- }
215
-
216
- /**
217
- * Retrieves a locator mapping by URL path
218
- *
219
- * Searches through the loaded mappings to find the one that matches the given path.
220
- * Returns a default empty mapping if not found or on error.
221
- *
222
- * @param key - The URL path to look up (e.g., '/login', '/dashboard')
223
- * @returns The LocatorMap object containing the path and corresponding locator name
224
- *
225
- * @example
226
- * ```typescript
227
- * const mapping = mapCache.get('/login');
228
- * console.log(mapping.name); // 'login'
229
- * console.log(mapping.path); // '/login'
230
- * ```
231
- */
232
- public get(key: string): LocatorMap {
233
- try {
234
- return this.data.find(map => map.path === key) as LocatorMap
235
- } catch (error) {
236
- console.error(error)
237
- return {
238
- name: '',
239
- path: '',
240
- }
241
- }
242
- }
243
-
244
- /**
245
- * Retrieves all locator mappings
246
- *
247
- * @returns Array of all LocatorMap objects
248
- *
249
- * @example
250
- * ```typescript
251
- * const allMappings = mapCache.getAll();
252
- * allMappings.forEach(mapping => {
253
- * console.log(`${mapping.path} -> ${mapping.name}`);
254
- * });
255
- * ```
256
- */
257
- public getAll(): LocatorMap[] {
258
- return this.data
259
- }
260
- }
@@ -1,177 +0,0 @@
1
- import { Command, Option, OptionValues } from 'commander'
2
- import { getAllEnvironments } from './environment.util.js'
3
- import parseTagExpression from '@cucumber/tag-expressions'
4
-
5
- /**
6
- * Configuration options for the CLI tool
7
- * @extends OptionValues - Extends Commander.js OptionValues for CLI integration
8
- */
9
- export interface CliOptions extends OptionValues {
10
- /** The target environment to run tests against */
11
- environment: string
12
- /** Cucumber tag expression to filter test scenarios */
13
- tags: string
14
- /** Number of parallel workers to run tests with */
15
- parallel: number
16
- /** Browser engine to use for test execution */
17
- browser: 'chromium' | 'firefox' | 'webkit'
18
- /** Whether to run browser in headless mode */
19
- headless: 'true' | 'false'
20
- }
21
-
22
- /** Available browser choices for test execution */
23
- const BROWSER_CHOICES = ['chromium', 'firefox', 'webkit'] as const
24
-
25
- /** Available headless mode choices */
26
- const HEADLESS_CHOICES = ['true', 'false'] as const
27
-
28
- /** Default number of parallel workers */
29
- const DEFAULT_PARALLEL_WORKERS = 1
30
-
31
- /** Default browser engine */
32
- const DEFAULT_BROWSER = 'chromium'
33
-
34
- /** Default headless mode setting */
35
- const DEFAULT_HEADLESS = 'true'
36
-
37
- /**
38
- * Commander.js program instance for CLI argument parsing
39
- * Configured with all available options and validation rules
40
- */
41
- const program = new Command()
42
-
43
- /**
44
- * Available environment names loaded from configuration
45
- * Used to validate the --environment option
46
- */
47
- let environmentNames: string[] = []
48
- try {
49
- environmentNames = Object.keys(getAllEnvironments())
50
- if (environmentNames.length === 0) {
51
- console.warn('⚠️ No environments found in configuration')
52
- }
53
- } catch (error) {
54
- console.error('❌ Failed to load environments:', error instanceof Error ? error.message : 'Unknown error')
55
- process.exit(1)
56
- }
57
-
58
- /**
59
- * Validates and parses a positive integer value from command line input
60
- *
61
- * Used as a custom parser for the --parallel option to ensure only valid
62
- * positive integers are accepted.
63
- *
64
- * @param val - The string value to parse from command line
65
- * @returns The parsed positive integer
66
- * @throws Error if the value is not a valid positive integer
67
- *
68
- * @example
69
- * ```typescript
70
- * parsePositiveInt("4") // Returns 4
71
- * parsePositiveInt("0") // Throws Error
72
- * parsePositiveInt("abc") // Throws Error
73
- * ```
74
- */
75
- function parsePositiveInt(val: string): number {
76
- const n = Number(val)
77
- if (!Number.isInteger(n) || n <= 0) {
78
- throw new Error(`--parallel must be a positive integer, got "${val}"`)
79
- }
80
- return n
81
- }
82
-
83
- /**
84
- * Validates a Cucumber tag expression for proper syntax
85
- *
86
- * Uses the @cucumber/tag-expressions library to validate that the provided
87
- * tag expression follows Cucumber's tag expression syntax rules.
88
- *
89
- * @param val - The tag expression string to validate
90
- * @returns The validated tag expression (unchanged if valid)
91
- * @throws Error if the tag expression is invalid
92
- *
93
- * @example
94
- * ```typescript
95
- * validateCucumberTagExpression('@smoke') // Returns '@smoke'
96
- * validateCucumberTagExpression('@smoke and @regression') // Returns '@smoke and @regression'
97
- * validateCucumberTagExpression('invalid expression') // Throws Error
98
- * ```
99
- */
100
- function validateCucumberTagExpression(val: string): string {
101
- try {
102
- parseTagExpression(val)
103
- return val
104
- } catch (error) {
105
- throw new Error(`Invalid tag expression: ${error instanceof Error ? error.message : 'Unknown error'}`)
106
- }
107
- }
108
-
109
- /**
110
- * Configure the Commander.js CLI program with all available options
111
- *
112
- * Sets up the command name, description, version, and all command line options
113
- * with their respective validation rules and default values.
114
- */
115
- program
116
- .name('cucumber-cli')
117
- .description('A CLI tool for running Cucumber tests with different configurations')
118
- .version('1.0.0')
119
- .addOption(
120
- new Option('-e, --environment <environment>', 'The environment to run the tests on')
121
- .choices(environmentNames)
122
- .makeOptionMandatory(),
123
- )
124
- .addOption(
125
- new Option('-t, --tags <tags>', 'The tags to run the tests on (Cucumber tag expression)')
126
- .argParser(validateCucumberTagExpression)
127
- .makeOptionMandatory(),
128
- )
129
- .option(
130
- '-p, --parallel <parallel>',
131
- 'The number of parallel workers to run the tests on',
132
- parsePositiveInt,
133
- DEFAULT_PARALLEL_WORKERS,
134
- )
135
- .addOption(
136
- new Option('-b, --browser <browser>', 'The browser to run the tests on')
137
- .choices(BROWSER_CHOICES)
138
- .default(DEFAULT_BROWSER),
139
- )
140
- .addOption(
141
- new Option('-h, --headless <headless>', 'The headless mode to run the tests on')
142
- .choices(HEADLESS_CHOICES)
143
- .default(DEFAULT_HEADLESS),
144
- )
145
-
146
- /**
147
- * Starts the CLI and parses command line arguments
148
- *
149
- * This is the main entry point for the CLI tool. It parses command line arguments,
150
- * validates them according to the configured rules, and returns the parsed options.
151
- * The function handles errors gracefully and exits the process with appropriate
152
- * error codes if parsing fails.
153
- *
154
- * @returns The parsed and validated CLI options
155
- * @throws Error if parsing fails or required options are missing
156
- *
157
- * @example
158
- * ```typescript
159
- * const options = startCli();
160
- * console.log(`Running tests on ${options.environment} with ${options.parallel} workers`);
161
- * ```
162
- */
163
- export function startCli(): CliOptions {
164
- try {
165
- program.parse()
166
- const options = program.opts() as CliOptions
167
-
168
- return options
169
- } catch (error) {
170
- if (error instanceof Error) {
171
- console.error('❌ CLI Error:', error.message)
172
- } else {
173
- console.error('❌ CLI Error: Unknown error occurred')
174
- }
175
- process.exit(1)
176
- }
177
- }