@toolstackhq/create-qa-patterns 1.0.14 → 1.0.15

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 (123) hide show
  1. package/README.md +5 -0
  2. package/index.js +252 -1076
  3. package/lib/args.js +139 -0
  4. package/lib/constants.js +115 -0
  5. package/lib/interactive.js +131 -0
  6. package/lib/local-env.js +65 -0
  7. package/lib/metadata.js +329 -0
  8. package/lib/output.js +326 -0
  9. package/lib/prereqs.js +72 -0
  10. package/lib/scaffold.js +120 -0
  11. package/lib/templates.js +40 -0
  12. package/package.json +5 -3
  13. package/templates/cypress-template/.env.example +2 -2
  14. package/templates/cypress-template/.github/workflows/cypress-tests.yml +2 -2
  15. package/templates/cypress-template/README.md +10 -6
  16. package/templates/cypress-template/allurerc.mjs +1 -1
  17. package/templates/cypress-template/config/environments.ts +13 -11
  18. package/templates/cypress-template/config/runtime-config.ts +17 -12
  19. package/templates/cypress-template/config/secret-manager.ts +1 -1
  20. package/templates/cypress-template/config/test-env.ts +3 -3
  21. package/templates/cypress-template/cypress/e2e/ui-journey.cy.ts +12 -10
  22. package/templates/cypress-template/cypress/support/app-config.ts +5 -5
  23. package/templates/cypress-template/cypress/support/commands.ts +7 -7
  24. package/templates/cypress-template/cypress/support/data/data-factory.ts +6 -4
  25. package/templates/cypress-template/cypress/support/data/id-generator.ts +1 -1
  26. package/templates/cypress-template/cypress/support/data/seeded-faker.ts +2 -2
  27. package/templates/cypress-template/cypress/support/e2e.ts +2 -2
  28. package/templates/cypress-template/cypress/support/pages/login-page.ts +4 -4
  29. package/templates/cypress-template/cypress/support/pages/people-page.ts +10 -10
  30. package/templates/cypress-template/cypress.config.ts +9 -9
  31. package/templates/cypress-template/demo-apps/ui-demo-app/public/styles.css +1 -1
  32. package/templates/cypress-template/demo-apps/ui-demo-app/src/server.js +44 -41
  33. package/templates/cypress-template/demo-apps/ui-demo-app/src/store.js +31 -3
  34. package/templates/cypress-template/demo-apps/ui-demo-app/src/templates.js +5 -5
  35. package/templates/cypress-template/eslint.config.mjs +53 -45
  36. package/templates/cypress-template/package.json +6 -5
  37. package/templates/cypress-template/scripts/ensure-local-env.mjs +36 -0
  38. package/templates/cypress-template/scripts/generate-allure-report.mjs +16 -10
  39. package/templates/cypress-template/scripts/run-cypress.mjs +33 -24
  40. package/templates/cypress-template/scripts/run-tests.sh +1 -0
  41. package/templates/cypress-template/tsconfig.json +7 -1
  42. package/templates/playwright-template/.env.example +6 -6
  43. package/templates/playwright-template/.github/workflows/playwright-tests.yml +14 -5
  44. package/templates/playwright-template/README.md +6 -5
  45. package/templates/playwright-template/allurerc.mjs +1 -1
  46. package/templates/playwright-template/components/flash-message.ts +2 -2
  47. package/templates/playwright-template/config/environments.ts +16 -14
  48. package/templates/playwright-template/config/runtime-config.ts +17 -12
  49. package/templates/playwright-template/config/secret-manager.ts +1 -1
  50. package/templates/playwright-template/config/test-env.ts +3 -3
  51. package/templates/playwright-template/data/factories/data-factory.ts +6 -4
  52. package/templates/playwright-template/data/generators/id-generator.ts +1 -1
  53. package/templates/playwright-template/data/generators/seeded-faker.ts +2 -2
  54. package/templates/playwright-template/demo-apps/api-demo-server/src/server.js +9 -9
  55. package/templates/playwright-template/demo-apps/api-demo-server/src/store.js +1 -1
  56. package/templates/playwright-template/demo-apps/ui-demo-app/public/styles.css +1 -1
  57. package/templates/playwright-template/demo-apps/ui-demo-app/src/server.js +44 -41
  58. package/templates/playwright-template/demo-apps/ui-demo-app/src/store.js +31 -3
  59. package/templates/playwright-template/demo-apps/ui-demo-app/src/templates.js +5 -5
  60. package/templates/playwright-template/eslint.config.mjs +40 -40
  61. package/templates/playwright-template/fixtures/test-fixtures.ts +27 -12
  62. package/templates/playwright-template/lint/architecture-plugin.cjs +36 -31
  63. package/templates/playwright-template/package.json +7 -6
  64. package/templates/playwright-template/pages/base-page.ts +4 -4
  65. package/templates/playwright-template/pages/login-page.ts +9 -9
  66. package/templates/playwright-template/pages/people-page.ts +21 -17
  67. package/templates/playwright-template/playwright.config.ts +22 -19
  68. package/templates/playwright-template/reporters/structured-reporter.ts +11 -8
  69. package/templates/playwright-template/scripts/ensure-local-env.mjs +37 -0
  70. package/templates/playwright-template/scripts/generate-allure-report.mjs +16 -10
  71. package/templates/playwright-template/scripts/run-tests.sh +1 -0
  72. package/templates/playwright-template/tests/api-people.spec.ts +8 -6
  73. package/templates/playwright-template/tests/ui-journey.spec.ts +13 -8
  74. package/templates/playwright-template/tsconfig.json +3 -11
  75. package/templates/playwright-template/utils/logger.ts +12 -8
  76. package/templates/playwright-template/utils/test-step.ts +5 -5
  77. package/templates/wdio-template/.env.example +14 -0
  78. package/templates/wdio-template/.github/workflows/wdio-tests.yml +46 -0
  79. package/templates/wdio-template/README.md +241 -0
  80. package/templates/wdio-template/allurerc.mjs +10 -0
  81. package/templates/wdio-template/components/README.md +5 -0
  82. package/templates/wdio-template/components/flash-message.ts +16 -0
  83. package/templates/wdio-template/config/README.md +5 -0
  84. package/templates/wdio-template/config/environments.ts +40 -0
  85. package/templates/wdio-template/config/runtime-config.ts +53 -0
  86. package/templates/wdio-template/config/secret-manager.ts +29 -0
  87. package/templates/wdio-template/config/test-env.ts +9 -0
  88. package/templates/wdio-template/data/README.md +9 -0
  89. package/templates/wdio-template/data/factories/README.md +6 -0
  90. package/templates/wdio-template/data/factories/data-factory.ts +36 -0
  91. package/templates/wdio-template/data/generators/README.md +5 -0
  92. package/templates/wdio-template/data/generators/id-generator.ts +18 -0
  93. package/templates/wdio-template/data/generators/seeded-faker.ts +14 -0
  94. package/templates/wdio-template/demo-apps/ui-demo-app/public/styles.css +120 -0
  95. package/templates/wdio-template/demo-apps/ui-demo-app/src/server.js +152 -0
  96. package/templates/wdio-template/demo-apps/ui-demo-app/src/store.js +71 -0
  97. package/templates/wdio-template/demo-apps/ui-demo-app/src/templates.js +121 -0
  98. package/templates/wdio-template/eslint.config.mjs +86 -0
  99. package/templates/wdio-template/lint/architecture-plugin.cjs +123 -0
  100. package/templates/wdio-template/package-lock.json +11058 -0
  101. package/templates/wdio-template/package.json +44 -0
  102. package/templates/wdio-template/pages/README.md +6 -0
  103. package/templates/wdio-template/pages/base-page.ts +15 -0
  104. package/templates/wdio-template/pages/login-page.ts +27 -0
  105. package/templates/wdio-template/pages/people-page.ts +54 -0
  106. package/templates/wdio-template/reporters/README.md +5 -0
  107. package/templates/wdio-template/reporters/structured-reporter.ts +78 -0
  108. package/templates/wdio-template/scripts/README.md +5 -0
  109. package/templates/wdio-template/scripts/ensure-local-env.mjs +36 -0
  110. package/templates/wdio-template/scripts/generate-allure-report.mjs +72 -0
  111. package/templates/wdio-template/scripts/run-tests.sh +7 -0
  112. package/templates/wdio-template/scripts/run-wdio.mjs +114 -0
  113. package/templates/wdio-template/tests/README.md +7 -0
  114. package/templates/wdio-template/tests/ui-journey.spec.ts +52 -0
  115. package/templates/wdio-template/tsconfig.json +22 -0
  116. package/templates/wdio-template/utils/README.md +5 -0
  117. package/templates/wdio-template/utils/logger.ts +60 -0
  118. package/templates/wdio-template/utils/test-step.ts +20 -0
  119. package/templates/wdio-template/wdio.conf.ts +58 -0
  120. package/tests/args.test.js +58 -0
  121. package/tests/local-env.test.js +70 -0
  122. package/tests/metadata.test.js +147 -0
  123. package/tests/templates.test.js +44 -0
@@ -0,0 +1,58 @@
1
+ // Central WebdriverIO configuration for specs, retries, reporters, and runtime env values.
2
+ import type { Capabilities, Options, Reporters } from '@wdio/types';
3
+
4
+ import { loadRuntimeConfig } from './config/runtime-config';
5
+ import StructuredReporter from './reporters/structured-reporter';
6
+
7
+ const runtimeConfig = loadRuntimeConfig();
8
+ const structuredReporter =
9
+ StructuredReporter as unknown as Reporters.ReporterClass;
10
+
11
+ export const config: Options.Testrunner &
12
+ Capabilities.WithRequestedTestrunnerCapabilities = {
13
+ runner: 'local',
14
+ specs: ['./tests/**/*.spec.ts'],
15
+ maxInstances: 1,
16
+ baseUrl: runtimeConfig.uiBaseUrl,
17
+ framework: 'mocha',
18
+ tsConfigPath: './tsconfig.json',
19
+ mochaOpts: {
20
+ ui: 'bdd',
21
+ timeout: 60_000
22
+ },
23
+ logLevel: 'error',
24
+ waitforTimeout: 10_000,
25
+ connectionRetryTimeout: 120_000,
26
+ connectionRetryCount: 1,
27
+ reporters: [
28
+ 'spec',
29
+ [
30
+ 'allure',
31
+ {
32
+ outputDir: 'allure-results',
33
+ disableWebdriverStepsReporting: true,
34
+ disableWebdriverScreenshotsReporting: false
35
+ }
36
+ ],
37
+ [structuredReporter, { outputFile: 'reports/logs/wdio-events.jsonl' }]
38
+ ],
39
+ capabilities: [
40
+ {
41
+ browserName: 'chrome',
42
+ 'goog:chromeOptions': {
43
+ args: ['--headless=new', '--disable-dev-shm-usage', '--no-sandbox']
44
+ }
45
+ }
46
+ ],
47
+ afterTest: async function (
48
+ _test: unknown,
49
+ _context: unknown,
50
+ result: { passed: boolean; error?: unknown }
51
+ ) {
52
+ if (!result.passed) {
53
+ await browser.saveScreenshot(
54
+ `test-results/${Date.now()}-${result.error ? 'failed' : 'result'}.png`
55
+ );
56
+ }
57
+ }
58
+ };
@@ -0,0 +1,58 @@
1
+ const test = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+
4
+ const { DEFAULT_TEMPLATE, TEMPLATE_CATALOG } = require('../lib/constants');
5
+ const { parseCliOptions, resolveNonInteractiveArgs } = require('../lib/args');
6
+ const { createTemplateAliases, resolveTemplate } = require('../lib/templates');
7
+
8
+ const templateAliases = createTemplateAliases(TEMPLATE_CATALOG);
9
+ const supportedTemplateIds = TEMPLATE_CATALOG.map((template) => template.id);
10
+
11
+ test('parseCliOptions resolves --template aliases', () => {
12
+ const options = parseCliOptions(['--template', 'pw', 'demo'], {
13
+ resolveTemplate: (value) => resolveTemplate(templateAliases, value),
14
+ supportedTemplateIds
15
+ });
16
+
17
+ assert.equal(options.templateName, 'playwright-template');
18
+ assert.deepEqual(options.positionalArgs, ['demo']);
19
+ });
20
+
21
+ test('resolveNonInteractiveArgs defaults to the default template for a directory name', () => {
22
+ const result = resolveNonInteractiveArgs(['demo-project'], {
23
+ defaultTemplate: DEFAULT_TEMPLATE,
24
+ resolveTemplate: (value) => resolveTemplate(templateAliases, value),
25
+ supportedTemplateIds
26
+ });
27
+
28
+ assert.equal(result.templateName, DEFAULT_TEMPLATE);
29
+ assert.equal(result.generatedInCurrentDirectory, false);
30
+ assert.match(result.targetDirectory, /demo-project$/);
31
+ });
32
+
33
+ test('resolveNonInteractiveArgs accepts explicit template and target directory', () => {
34
+ const result = resolveNonInteractiveArgs(
35
+ ['cypress-template', 'demo-project'],
36
+ {
37
+ defaultTemplate: DEFAULT_TEMPLATE,
38
+ resolveTemplate: (value) => resolveTemplate(templateAliases, value),
39
+ supportedTemplateIds
40
+ }
41
+ );
42
+
43
+ assert.equal(result.templateName, 'cypress-template');
44
+ assert.equal(result.generatedInCurrentDirectory, false);
45
+ assert.match(result.targetDirectory, /demo-project$/);
46
+ });
47
+
48
+ test('resolveNonInteractiveArgs accepts a WebdriverIO alias and target directory', () => {
49
+ const result = resolveNonInteractiveArgs(['wdio', 'demo-project'], {
50
+ defaultTemplate: DEFAULT_TEMPLATE,
51
+ resolveTemplate: (value) => resolveTemplate(templateAliases, value),
52
+ supportedTemplateIds
53
+ });
54
+
55
+ assert.equal(result.templateName, 'wdio-template');
56
+ assert.equal(result.generatedInCurrentDirectory, false);
57
+ assert.match(result.targetDirectory, /demo-project$/);
58
+ });
@@ -0,0 +1,70 @@
1
+ const test = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+
7
+ const {
8
+ createLocalCredentials,
9
+ renderLocalEnv,
10
+ writeGeneratedLocalEnv
11
+ } = require('../lib/local-env');
12
+
13
+ test('createLocalCredentials generates non-empty username and password', () => {
14
+ const credentials = createLocalCredentials('/tmp/my-project');
15
+
16
+ assert.match(credentials.username, /^my-project-/);
17
+ assert.ok(credentials.password.length > 10);
18
+ });
19
+
20
+ test('renderLocalEnv includes api base url only for playwright', () => {
21
+ const credentials = {
22
+ username: 'local-user',
23
+ password: 'local-password'
24
+ };
25
+
26
+ const playwrightEnv = renderLocalEnv('playwright-template', credentials);
27
+ const cypressEnv = renderLocalEnv('cypress-template', credentials);
28
+ const wdioEnv = renderLocalEnv('wdio-template', credentials);
29
+
30
+ assert.match(playwrightEnv, /DEV_API_BASE_URL=http:\/\/127.0.0.1:3001/);
31
+ assert.doesNotMatch(cypressEnv, /DEV_API_BASE_URL/);
32
+ assert.doesNotMatch(wdioEnv, /DEV_API_BASE_URL/);
33
+ assert.match(playwrightEnv, /DEV_APP_USERNAME=local-user/);
34
+ assert.match(cypressEnv, /UI_DEMO_PASSWORD=local-password/);
35
+ assert.match(wdioEnv, /UI_DEMO_PASSWORD=local-password/);
36
+ });
37
+
38
+ test('writeGeneratedLocalEnv creates .env only once', () => {
39
+ const targetDirectory = fs.mkdtempSync(
40
+ path.join(os.tmpdir(), 'qa-patterns-env-')
41
+ );
42
+ const credentials = {
43
+ username: 'first-user',
44
+ password: 'first-password'
45
+ };
46
+
47
+ const firstWrite = writeGeneratedLocalEnv(
48
+ targetDirectory,
49
+ 'cypress-template',
50
+ credentials
51
+ );
52
+ const secondWrite = writeGeneratedLocalEnv(
53
+ targetDirectory,
54
+ 'cypress-template',
55
+ {
56
+ username: 'second-user',
57
+ password: 'second-password'
58
+ }
59
+ );
60
+
61
+ const envContents = fs.readFileSync(
62
+ path.join(targetDirectory, '.env'),
63
+ 'utf8'
64
+ );
65
+
66
+ assert.equal(firstWrite.created, true);
67
+ assert.equal(secondWrite.created, false);
68
+ assert.match(envContents, /first-user/);
69
+ assert.doesNotMatch(envContents, /second-user/);
70
+ });
@@ -0,0 +1,147 @@
1
+ const test = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+
7
+ const {
8
+ analyzeUpgrade,
9
+ applySafeUpdates,
10
+ readProjectMetadata,
11
+ renderTemplateFile,
12
+ writeProjectMetadata
13
+ } = require('../lib/metadata');
14
+ const {
15
+ DEFAULT_GITIGNORE,
16
+ MANAGED_FILE_PATTERNS,
17
+ METADATA_FILENAME
18
+ } = require('../lib/constants');
19
+ const { getTemplateDirectory, toPackageName } = require('../lib/templates');
20
+
21
+ const metadataOptions = {
22
+ cliPackageVersion: '1.0.14',
23
+ defaultGitignore: DEFAULT_GITIGNORE,
24
+ getTemplateDirectory: (templateId) =>
25
+ getTemplateDirectory(path.resolve(__dirname, '..'), templateId),
26
+ managedPatterns: MANAGED_FILE_PATTERNS,
27
+ metadataFilename: METADATA_FILENAME,
28
+ toPackageName
29
+ };
30
+
31
+ const template = {
32
+ id: 'playwright-template',
33
+ defaultPackageName: 'playwright-template'
34
+ };
35
+
36
+ function prepareScaffoldBaseline(targetDirectory) {
37
+ fs.cpSync(
38
+ metadataOptions.getTemplateDirectory(template.id),
39
+ targetDirectory,
40
+ { recursive: true }
41
+ );
42
+
43
+ for (const relativePath of ['package.json', 'package-lock.json']) {
44
+ const absolutePath = path.join(targetDirectory, relativePath);
45
+ fs.writeFileSync(
46
+ absolutePath,
47
+ renderTemplateFile(
48
+ template,
49
+ relativePath,
50
+ targetDirectory,
51
+ metadataOptions
52
+ ),
53
+ 'utf8'
54
+ );
55
+ }
56
+ }
57
+
58
+ test('analyzeUpgrade reports up-to-date state for a fresh scaffold baseline', () => {
59
+ const targetDirectory = fs.mkdtempSync(
60
+ path.join(os.tmpdir(), 'qa-patterns-meta-')
61
+ );
62
+ prepareScaffoldBaseline(targetDirectory);
63
+ writeProjectMetadata(template, targetDirectory, undefined, metadataOptions);
64
+
65
+ const metadata = readProjectMetadata(targetDirectory, METADATA_FILENAME);
66
+ const results = analyzeUpgrade(
67
+ template,
68
+ targetDirectory,
69
+ metadata,
70
+ metadataOptions
71
+ );
72
+
73
+ assert.equal(
74
+ results.every((entry) => entry.status === 'up-to-date'),
75
+ true
76
+ );
77
+ });
78
+
79
+ test('analyzeUpgrade reports conflicts for user-edited managed files', () => {
80
+ const targetDirectory = fs.mkdtempSync(
81
+ path.join(os.tmpdir(), 'qa-patterns-meta-')
82
+ );
83
+ prepareScaffoldBaseline(targetDirectory);
84
+ writeProjectMetadata(template, targetDirectory, undefined, metadataOptions);
85
+
86
+ const packageJsonPath = path.join(targetDirectory, 'package.json');
87
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
88
+ packageJson.name = `${packageJson.name}-custom`;
89
+ fs.writeFileSync(
90
+ packageJsonPath,
91
+ `${JSON.stringify(packageJson, null, 2)}\n`,
92
+ 'utf8'
93
+ );
94
+
95
+ const metadata = readProjectMetadata(targetDirectory, METADATA_FILENAME);
96
+ const results = analyzeUpgrade(
97
+ template,
98
+ targetDirectory,
99
+ metadata,
100
+ metadataOptions
101
+ );
102
+ const packageResult = results.find(
103
+ (entry) => entry.relativePath === 'package.json'
104
+ );
105
+
106
+ assert.equal(packageResult.status, 'conflict');
107
+ });
108
+
109
+ test('applySafeUpdates adopts a missing managed baseline entry', () => {
110
+ const targetDirectory = fs.mkdtempSync(
111
+ path.join(os.tmpdir(), 'qa-patterns-meta-')
112
+ );
113
+ prepareScaffoldBaseline(targetDirectory);
114
+ writeProjectMetadata(template, targetDirectory, undefined, metadataOptions);
115
+
116
+ const metadataPath = path.join(targetDirectory, METADATA_FILENAME);
117
+ const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
118
+ delete metadata.managedFiles['.github/workflows/playwright-tests.yml'];
119
+ fs.writeFileSync(
120
+ metadataPath,
121
+ `${JSON.stringify(metadata, null, 2)}\n`,
122
+ 'utf8'
123
+ );
124
+
125
+ const nextMetadata = readProjectMetadata(targetDirectory, METADATA_FILENAME);
126
+ const results = analyzeUpgrade(
127
+ template,
128
+ targetDirectory,
129
+ nextMetadata,
130
+ metadataOptions
131
+ );
132
+ const outcome = applySafeUpdates(
133
+ targetDirectory,
134
+ nextMetadata,
135
+ results,
136
+ metadataOptions
137
+ );
138
+
139
+ assert.equal(outcome.appliedCount >= 1, true);
140
+ const updatedMetadata = readProjectMetadata(
141
+ targetDirectory,
142
+ METADATA_FILENAME
143
+ );
144
+ assert.ok(
145
+ updatedMetadata.managedFiles['.github/workflows/playwright-tests.yml']
146
+ );
147
+ });
@@ -0,0 +1,44 @@
1
+ const test = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+
4
+ const {
5
+ createTemplateAliases,
6
+ resolveTemplate,
7
+ toPackageName
8
+ } = require('../lib/templates');
9
+
10
+ test('toPackageName normalizes directory names for generated packages', () => {
11
+ assert.equal(
12
+ toPackageName('/tmp/My QA Project', {
13
+ defaultPackageName: 'fallback-template'
14
+ }),
15
+ 'my-qa-project'
16
+ );
17
+ assert.equal(
18
+ toPackageName('/tmp/---', { defaultPackageName: 'fallback-template' }),
19
+ 'fallback-template'
20
+ );
21
+ });
22
+
23
+ test('createTemplateAliases resolves template ids and aliases', () => {
24
+ const aliases = createTemplateAliases([
25
+ {
26
+ id: 'playwright-template',
27
+ aliases: ['playwright', 'pw']
28
+ },
29
+ {
30
+ id: 'wdio-template',
31
+ aliases: ['webdriverio', 'wdio']
32
+ }
33
+ ]);
34
+
35
+ assert.equal(
36
+ resolveTemplate(aliases, 'playwright-template'),
37
+ 'playwright-template'
38
+ );
39
+ assert.equal(resolveTemplate(aliases, 'playwright'), 'playwright-template');
40
+ assert.equal(resolveTemplate(aliases, 'pw'), 'playwright-template');
41
+ assert.equal(resolveTemplate(aliases, 'wdio-template'), 'wdio-template');
42
+ assert.equal(resolveTemplate(aliases, 'webdriverio'), 'wdio-template');
43
+ assert.equal(resolveTemplate(aliases, 'wdio'), 'wdio-template');
44
+ });