@wundr.io/cli 1.0.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 (213) hide show
  1. package/README.md +551 -0
  2. package/bin/wundr.js +39 -0
  3. package/dist/ai/ai-service.d.ts +152 -0
  4. package/dist/ai/ai-service.d.ts.map +1 -0
  5. package/dist/ai/ai-service.js +430 -0
  6. package/dist/ai/ai-service.js.map +1 -0
  7. package/dist/ai/claude-client.d.ts +130 -0
  8. package/dist/ai/claude-client.d.ts.map +1 -0
  9. package/dist/ai/claude-client.js +339 -0
  10. package/dist/ai/claude-client.js.map +1 -0
  11. package/dist/ai/conversation-manager.d.ts +164 -0
  12. package/dist/ai/conversation-manager.d.ts.map +1 -0
  13. package/dist/ai/conversation-manager.js +612 -0
  14. package/dist/ai/conversation-manager.js.map +1 -0
  15. package/dist/ai/index.d.ts +5 -0
  16. package/dist/ai/index.d.ts.map +1 -0
  17. package/dist/ai/index.js +8 -0
  18. package/dist/ai/index.js.map +1 -0
  19. package/dist/cli.d.ts +36 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +173 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/commands/ai.d.ts +89 -0
  24. package/dist/commands/ai.d.ts.map +1 -0
  25. package/dist/commands/ai.js +735 -0
  26. package/dist/commands/ai.js.map +1 -0
  27. package/dist/commands/analyze-optimized.d.ts +14 -0
  28. package/dist/commands/analyze-optimized.d.ts.map +1 -0
  29. package/dist/commands/analyze-optimized.js +437 -0
  30. package/dist/commands/analyze-optimized.js.map +1 -0
  31. package/dist/commands/analyze.d.ts +65 -0
  32. package/dist/commands/analyze.d.ts.map +1 -0
  33. package/dist/commands/analyze.js +435 -0
  34. package/dist/commands/analyze.js.map +1 -0
  35. package/dist/commands/batch.d.ts +71 -0
  36. package/dist/commands/batch.d.ts.map +1 -0
  37. package/dist/commands/batch.js +738 -0
  38. package/dist/commands/batch.js.map +1 -0
  39. package/dist/commands/chat.d.ts +71 -0
  40. package/dist/commands/chat.d.ts.map +1 -0
  41. package/dist/commands/chat.js +674 -0
  42. package/dist/commands/chat.js.map +1 -0
  43. package/dist/commands/claude-init.d.ts +28 -0
  44. package/dist/commands/claude-init.d.ts.map +1 -0
  45. package/dist/commands/claude-init.js +587 -0
  46. package/dist/commands/claude-init.js.map +1 -0
  47. package/dist/commands/claude-setup.d.ts +32 -0
  48. package/dist/commands/claude-setup.d.ts.map +1 -0
  49. package/dist/commands/claude-setup.js +570 -0
  50. package/dist/commands/claude-setup.js.map +1 -0
  51. package/dist/commands/computer-setup-commands.d.ts +39 -0
  52. package/dist/commands/computer-setup-commands.d.ts.map +1 -0
  53. package/dist/commands/computer-setup-commands.js +563 -0
  54. package/dist/commands/computer-setup-commands.js.map +1 -0
  55. package/dist/commands/computer-setup.d.ts +7 -0
  56. package/dist/commands/computer-setup.d.ts.map +1 -0
  57. package/dist/commands/computer-setup.js +481 -0
  58. package/dist/commands/computer-setup.js.map +1 -0
  59. package/dist/commands/create-command.d.ts +7 -0
  60. package/dist/commands/create-command.d.ts.map +1 -0
  61. package/dist/commands/create-command.js +158 -0
  62. package/dist/commands/create-command.js.map +1 -0
  63. package/dist/commands/create.d.ts +74 -0
  64. package/dist/commands/create.d.ts.map +1 -0
  65. package/dist/commands/create.js +556 -0
  66. package/dist/commands/create.js.map +1 -0
  67. package/dist/commands/dashboard.d.ts +91 -0
  68. package/dist/commands/dashboard.d.ts.map +1 -0
  69. package/dist/commands/dashboard.js +537 -0
  70. package/dist/commands/dashboard.js.map +1 -0
  71. package/dist/commands/govern.d.ts +70 -0
  72. package/dist/commands/govern.d.ts.map +1 -0
  73. package/dist/commands/govern.js +480 -0
  74. package/dist/commands/govern.js.map +1 -0
  75. package/dist/commands/init.d.ts +55 -0
  76. package/dist/commands/init.d.ts.map +1 -0
  77. package/dist/commands/init.js +584 -0
  78. package/dist/commands/init.js.map +1 -0
  79. package/dist/commands/performance-optimizer.d.ts +30 -0
  80. package/dist/commands/performance-optimizer.d.ts.map +1 -0
  81. package/dist/commands/performance-optimizer.js +649 -0
  82. package/dist/commands/performance-optimizer.js.map +1 -0
  83. package/dist/commands/plugins.d.ts +87 -0
  84. package/dist/commands/plugins.d.ts.map +1 -0
  85. package/dist/commands/plugins.js +685 -0
  86. package/dist/commands/plugins.js.map +1 -0
  87. package/dist/commands/setup.d.ts +29 -0
  88. package/dist/commands/setup.d.ts.map +1 -0
  89. package/dist/commands/setup.js +399 -0
  90. package/dist/commands/setup.js.map +1 -0
  91. package/dist/commands/test-init.d.ts +9 -0
  92. package/dist/commands/test-init.d.ts.map +1 -0
  93. package/dist/commands/test-init.js +222 -0
  94. package/dist/commands/test-init.js.map +1 -0
  95. package/dist/commands/test.d.ts +25 -0
  96. package/dist/commands/test.d.ts.map +1 -0
  97. package/dist/commands/test.js +217 -0
  98. package/dist/commands/test.js.map +1 -0
  99. package/dist/commands/watch.d.ts +76 -0
  100. package/dist/commands/watch.d.ts.map +1 -0
  101. package/dist/commands/watch.js +610 -0
  102. package/dist/commands/watch.js.map +1 -0
  103. package/dist/context/context-manager.d.ts +155 -0
  104. package/dist/context/context-manager.d.ts.map +1 -0
  105. package/dist/context/context-manager.js +383 -0
  106. package/dist/context/context-manager.js.map +1 -0
  107. package/dist/context/index.d.ts +3 -0
  108. package/dist/context/index.d.ts.map +1 -0
  109. package/dist/context/index.js +6 -0
  110. package/dist/context/index.js.map +1 -0
  111. package/dist/context/session-manager.d.ts +207 -0
  112. package/dist/context/session-manager.d.ts.map +1 -0
  113. package/dist/context/session-manager.js +682 -0
  114. package/dist/context/session-manager.js.map +1 -0
  115. package/dist/index.d.ts +8 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +51 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/interactive/interactive-mode.d.ts +76 -0
  120. package/dist/interactive/interactive-mode.d.ts.map +1 -0
  121. package/dist/interactive/interactive-mode.js +730 -0
  122. package/dist/interactive/interactive-mode.js.map +1 -0
  123. package/dist/nlp/command-mapper.d.ts +174 -0
  124. package/dist/nlp/command-mapper.d.ts.map +1 -0
  125. package/dist/nlp/command-mapper.js +623 -0
  126. package/dist/nlp/command-mapper.js.map +1 -0
  127. package/dist/nlp/command-parser.d.ts +106 -0
  128. package/dist/nlp/command-parser.d.ts.map +1 -0
  129. package/dist/nlp/command-parser.js +416 -0
  130. package/dist/nlp/command-parser.js.map +1 -0
  131. package/dist/nlp/index.d.ts +5 -0
  132. package/dist/nlp/index.d.ts.map +1 -0
  133. package/dist/nlp/index.js +8 -0
  134. package/dist/nlp/index.js.map +1 -0
  135. package/dist/nlp/intent-classifier.d.ts +59 -0
  136. package/dist/nlp/intent-classifier.d.ts.map +1 -0
  137. package/dist/nlp/intent-classifier.js +384 -0
  138. package/dist/nlp/intent-classifier.js.map +1 -0
  139. package/dist/nlp/intent-parser.d.ts +152 -0
  140. package/dist/nlp/intent-parser.d.ts.map +1 -0
  141. package/dist/nlp/intent-parser.js +739 -0
  142. package/dist/nlp/intent-parser.js.map +1 -0
  143. package/dist/plugins/plugin-manager.d.ts +120 -0
  144. package/dist/plugins/plugin-manager.d.ts.map +1 -0
  145. package/dist/plugins/plugin-manager.js +595 -0
  146. package/dist/plugins/plugin-manager.js.map +1 -0
  147. package/dist/types/index.d.ts +224 -0
  148. package/dist/types/index.d.ts.map +1 -0
  149. package/dist/types/index.js +3 -0
  150. package/dist/types/index.js.map +1 -0
  151. package/dist/utils/config-manager.d.ts +73 -0
  152. package/dist/utils/config-manager.d.ts.map +1 -0
  153. package/dist/utils/config-manager.js +339 -0
  154. package/dist/utils/config-manager.js.map +1 -0
  155. package/dist/utils/error-handler.d.ts +46 -0
  156. package/dist/utils/error-handler.d.ts.map +1 -0
  157. package/dist/utils/error-handler.js +169 -0
  158. package/dist/utils/error-handler.js.map +1 -0
  159. package/dist/utils/logger.d.ts +25 -0
  160. package/dist/utils/logger.d.ts.map +1 -0
  161. package/dist/utils/logger.js +94 -0
  162. package/dist/utils/logger.js.map +1 -0
  163. package/package.json +119 -0
  164. package/src/ai/ai-service.ts +595 -0
  165. package/src/ai/claude-client.ts +490 -0
  166. package/src/ai/conversation-manager.ts +907 -0
  167. package/src/ai/index.ts +8 -0
  168. package/src/cli.ts +202 -0
  169. package/src/commands/ai.ts +995 -0
  170. package/src/commands/analyze-optimized.ts +641 -0
  171. package/src/commands/analyze.ts +576 -0
  172. package/src/commands/batch.ts +935 -0
  173. package/src/commands/chat.ts +876 -0
  174. package/src/commands/claude-init.ts +715 -0
  175. package/src/commands/claude-setup.ts +697 -0
  176. package/src/commands/computer-setup-commands.ts +709 -0
  177. package/src/commands/computer-setup.ts +565 -0
  178. package/src/commands/create-command.ts +175 -0
  179. package/src/commands/create.ts +727 -0
  180. package/src/commands/dashboard.ts +691 -0
  181. package/src/commands/govern.ts +635 -0
  182. package/src/commands/init.ts +677 -0
  183. package/src/commands/performance-optimizer.ts +864 -0
  184. package/src/commands/plugins.ts +848 -0
  185. package/src/commands/setup.ts +508 -0
  186. package/src/commands/test-init.ts +242 -0
  187. package/src/commands/test.ts +264 -0
  188. package/src/commands/watch.ts +755 -0
  189. package/src/context/context-manager.ts +546 -0
  190. package/src/context/index.ts +9 -0
  191. package/src/context/session-manager.ts +1019 -0
  192. package/src/index.ts +64 -0
  193. package/src/interactive/interactive-mode.ts +830 -0
  194. package/src/nlp/command-mapper.ts +885 -0
  195. package/src/nlp/command-parser.ts +564 -0
  196. package/src/nlp/index.ts +4 -0
  197. package/src/nlp/intent-classifier.ts +458 -0
  198. package/src/nlp/intent-parser.ts +1101 -0
  199. package/src/plugins/plugin-manager.ts +744 -0
  200. package/src/types/index.ts +252 -0
  201. package/src/types/modules.d.ts +56 -0
  202. package/src/utils/config-manager.ts +391 -0
  203. package/src/utils/error-handler.ts +192 -0
  204. package/src/utils/logger.ts +104 -0
  205. package/templates/batch/ci-cd.yaml +62 -0
  206. package/templates/component/{{fileName}}.test.tsx +17 -0
  207. package/templates/component/{{fileName}}.tsx +21 -0
  208. package/templates/service/{{fileName}}.ts +98 -0
  209. package/templates/wundr-test.config.js +0 -0
  210. package/test-suites/api/health.spec.ts +134 -0
  211. package/test-suites/helpers/test-config.ts +84 -0
  212. package/test-suites/ui/accessibility.spec.ts +102 -0
  213. package/test-suites/ui/smoke.spec.ts +92 -0
@@ -0,0 +1,242 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import fs from 'fs-extra';
4
+ import path from 'path';
5
+ import inquirer from 'inquirer';
6
+
7
+ export class TestInitCommand {
8
+ async run(): Promise<void> {
9
+ console.log(chalk.blue('\n๐Ÿงช Initializing Wundr Test Configuration\n'));
10
+
11
+ const answers = await inquirer.prompt([
12
+ {
13
+ type: 'input',
14
+ name: 'baseURL',
15
+ message: "What is your application's base URL?",
16
+ default: 'http://localhost:3000',
17
+ },
18
+ {
19
+ type: 'input',
20
+ name: 'apiURL',
21
+ message: 'What is your API base URL?',
22
+ default: 'http://localhost:3000/api',
23
+ },
24
+ {
25
+ type: 'checkbox',
26
+ name: 'testSuites',
27
+ message: 'Which test suites would you like to enable?',
28
+ choices: [
29
+ { name: 'Smoke Tests', value: 'smoke', checked: true },
30
+ {
31
+ name: 'Accessibility Tests',
32
+ value: 'accessibility',
33
+ checked: true,
34
+ },
35
+ { name: 'API Tests', value: 'api', checked: true },
36
+ { name: 'Performance Tests', value: 'performance', checked: false },
37
+ { name: 'Security Tests', value: 'security', checked: false },
38
+ ],
39
+ },
40
+ {
41
+ type: 'checkbox',
42
+ name: 'browsers',
43
+ message: 'Which browsers should we test?',
44
+ choices: [
45
+ { name: 'Chromium', value: 'chromium', checked: true },
46
+ { name: 'Firefox', value: 'firefox', checked: false },
47
+ { name: 'Safari (WebKit)', value: 'webkit', checked: false },
48
+ { name: 'Mobile', value: 'mobile', checked: true },
49
+ ],
50
+ },
51
+ {
52
+ type: 'confirm',
53
+ name: 'headless',
54
+ message: 'Run tests in headless mode?',
55
+ default: true,
56
+ },
57
+ {
58
+ type: 'confirm',
59
+ name: 'ci',
60
+ message: 'Would you like to add CI/CD configuration?',
61
+ default: true,
62
+ },
63
+ ]);
64
+
65
+ // Generate configuration
66
+ const config = this.generateConfig(answers);
67
+
68
+ // Write configuration file
69
+ const configPath = path.join(process.cwd(), 'wundr-test.config.js');
70
+ await fs.writeFile(configPath, config);
71
+
72
+ console.log(
73
+ chalk.green('\nโœ… Test configuration created: wundr-test.config.js')
74
+ );
75
+
76
+ // Add package.json scripts
77
+ await this.updatePackageJson();
78
+
79
+ // Generate CI configuration if requested
80
+ if (answers.ci) {
81
+ await this.generateCIConfig(answers);
82
+ }
83
+
84
+ // Show next steps
85
+ console.log(chalk.cyan('\n๐Ÿ“‹ Next Steps:'));
86
+ console.log(' 1. Install Playwright: npm install -D @playwright/test');
87
+ console.log(' 2. Run tests: npm run test:wundr');
88
+ console.log(' 3. View report: npm run test:report');
89
+ console.log(' 4. Customize wundr-test.config.js as needed');
90
+ }
91
+
92
+ private generateConfig(answers: any): string {
93
+ const testSuites = answers.testSuites.reduce((acc: any, suite: string) => {
94
+ acc[suite] = true;
95
+ return acc;
96
+ }, {});
97
+
98
+ const projects = answers.browsers
99
+ .map((browser: string) => {
100
+ if (browser === 'mobile') {
101
+ return ` {
102
+ name: 'mobile',
103
+ use: {
104
+ browserName: 'chromium',
105
+ viewport: { width: 375, height: 667 },
106
+ userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)'
107
+ }
108
+ }`;
109
+ }
110
+ return ` {
111
+ name: '${browser}',
112
+ use: {
113
+ browserName: '${browser}',
114
+ viewport: { width: 1280, height: 720 }
115
+ }
116
+ }`;
117
+ })
118
+ .join(',\n');
119
+
120
+ return `/**
121
+ * Wundr Test Configuration
122
+ * Generated on ${new Date().toISOString()}
123
+ */
124
+
125
+ module.exports = {
126
+ // Base URL of your application
127
+ baseURL: '${answers.baseURL}',
128
+
129
+ // Test execution settings
130
+ timeout: 30000,
131
+ retries: 2,
132
+ workers: 4,
133
+
134
+ // Browser settings
135
+ headless: ${answers.headless},
136
+ slowMo: 0,
137
+
138
+ // Screenshot and video settings
139
+ screenshot: 'only-on-failure',
140
+ video: 'retain-on-failure',
141
+ trace: 'on-first-retry',
142
+
143
+ // API configuration
144
+ api: {
145
+ baseURL: '${answers.apiURL}',
146
+ headers: {
147
+ 'Content-Type': 'application/json'
148
+ },
149
+ timeout: 10000
150
+ },
151
+
152
+ // Enabled test suites
153
+ testSuites: ${JSON.stringify(testSuites, null, 4)},
154
+
155
+ // Projects/browsers to test
156
+ projects: [
157
+ ${projects}
158
+ ]
159
+ };`;
160
+ }
161
+
162
+ private async updatePackageJson(): Promise<void> {
163
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
164
+
165
+ if (await fs.pathExists(packageJsonPath)) {
166
+ const packageJson = await fs.readJson(packageJsonPath);
167
+
168
+ packageJson.scripts = packageJson.scripts || {};
169
+ packageJson.scripts['test:wundr'] = 'wundr test';
170
+ packageJson.scripts['test:wundr:ui'] = 'wundr test --type ui';
171
+ packageJson.scripts['test:wundr:api'] = 'wundr test --type api';
172
+ packageJson.scripts['test:wundr:headed'] = 'wundr test --headed';
173
+ packageJson.scripts['test:report'] = 'wundr test --report';
174
+
175
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
176
+
177
+ console.log(chalk.green('โœ… Added test scripts to package.json'));
178
+ }
179
+ }
180
+
181
+ private async generateCIConfig(answers: any): Promise<void> {
182
+ const githubWorkflow = `name: Wundr Tests
183
+
184
+ on:
185
+ push:
186
+ branches: [ main, master ]
187
+ pull_request:
188
+ branches: [ main, master ]
189
+
190
+ jobs:
191
+ test:
192
+ runs-on: ubuntu-latest
193
+
194
+ steps:
195
+ - uses: actions/checkout@v3
196
+
197
+ - name: Setup Node.js
198
+ uses: actions/setup-node@v3
199
+ with:
200
+ node-version: '18'
201
+
202
+ - name: Install dependencies
203
+ run: npm ci
204
+
205
+ - name: Install Playwright
206
+ run: npx playwright install --with-deps
207
+
208
+ - name: Run Wundr tests
209
+ run: npm run test:wundr
210
+ env:
211
+ TEST_BASE_URL: '${answers.baseURL}'
212
+
213
+ - name: Upload test results
214
+ if: always()
215
+ uses: actions/upload-artifact@v3
216
+ with:
217
+ name: test-results
218
+ path: test-results/
219
+ retention-days: 30`;
220
+
221
+ const githubDir = path.join(process.cwd(), '.github', 'workflows');
222
+ await fs.ensureDir(githubDir);
223
+ await fs.writeFile(path.join(githubDir, 'wundr-tests.yml'), githubWorkflow);
224
+
225
+ console.log(
226
+ chalk.green(
227
+ 'โœ… Created GitHub Actions workflow: .github/workflows/wundr-tests.yml'
228
+ )
229
+ );
230
+ }
231
+ }
232
+
233
+ export function createTestInitCommand(): Command {
234
+ const command = new Command('test-init')
235
+ .description('Initialize Wundr test configuration for your project')
236
+ .action(async () => {
237
+ const initCommand = new TestInitCommand();
238
+ await initCommand.run();
239
+ });
240
+
241
+ return command;
242
+ }
@@ -0,0 +1,264 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { spawn } from 'child_process';
4
+ import path from 'path';
5
+ import fs from 'fs-extra';
6
+ import ora from 'ora';
7
+
8
+ export interface TestOptions {
9
+ type?: 'ui' | 'api' | 'unit' | 'all';
10
+ browser?: 'chromium' | 'firefox' | 'webkit';
11
+ headed?: boolean;
12
+ baseUrl?: string;
13
+ config?: string;
14
+ report?: boolean;
15
+ watch?: boolean;
16
+ }
17
+
18
+ export class TestCommand {
19
+ private testConfigPath: string;
20
+ private testsPath: string;
21
+
22
+ constructor() {
23
+ // Tests are bundled with the CLI package
24
+ this.testsPath = path.join(__dirname, '../../test-suites');
25
+ this.testConfigPath = path.join(process.cwd(), 'wundr-test.config.js');
26
+ }
27
+
28
+ async run(options: TestOptions): Promise<void> {
29
+ const spinner = ora('Preparing test environment...').start();
30
+
31
+ try {
32
+ // Check if tests are available
33
+ if (!(await fs.pathExists(this.testsPath))) {
34
+ spinner.fail('Test suites not found. Please reinstall @wundr/cli');
35
+ return;
36
+ }
37
+
38
+ // Load or create config
39
+ const config = await this.loadConfig(options);
40
+
41
+ // Setup test environment
42
+ await this.setupTestEnvironment(config);
43
+
44
+ spinner.succeed('Test environment ready');
45
+
46
+ // Run tests based on type
47
+ switch (options.type) {
48
+ case 'ui':
49
+ await this.runUITests(config, options);
50
+ break;
51
+ case 'api':
52
+ await this.runAPITests(config, options);
53
+ break;
54
+ case 'unit':
55
+ await this.runUnitTests(config, options);
56
+ break;
57
+ default:
58
+ await this.runAllTests(config, options);
59
+ }
60
+ } catch (error) {
61
+ spinner.fail('Test execution failed');
62
+ console.error(chalk.red('Error:'), error);
63
+ process.exit(1);
64
+ }
65
+ }
66
+
67
+ private async loadConfig(options: TestOptions): Promise<any> {
68
+ let config = {
69
+ baseUrl:
70
+ options.baseUrl || process.env.TEST_BASE_URL || 'http://localhost:3000',
71
+ testDir: this.testsPath,
72
+ outputDir: path.join(process.cwd(), 'test-results'),
73
+ timeout: 30000,
74
+ retries: 2,
75
+ workers: 4,
76
+ use: {
77
+ headless: !options.headed,
78
+ screenshot: 'only-on-failure',
79
+ video: 'retain-on-failure',
80
+ trace: 'on-first-retry',
81
+ },
82
+ };
83
+
84
+ // Check for custom config
85
+ if (options.config) {
86
+ const customConfigPath = path.resolve(options.config);
87
+ if (await fs.pathExists(customConfigPath)) {
88
+ const customConfig = require(customConfigPath);
89
+ config = { ...config, ...customConfig };
90
+ }
91
+ } else if (await fs.pathExists(this.testConfigPath)) {
92
+ const projectConfig = require(this.testConfigPath);
93
+ config = { ...config, ...projectConfig };
94
+ }
95
+
96
+ return config;
97
+ }
98
+
99
+ private async setupTestEnvironment(config: any): Promise<void> {
100
+ // Create output directory
101
+ await fs.ensureDir(config.outputDir);
102
+
103
+ // Copy test files to temp location if needed
104
+ const tempTestDir = path.join(config.outputDir, '.test-suites');
105
+ await fs.copy(this.testsPath, tempTestDir, { overwrite: true });
106
+
107
+ // Generate Playwright config
108
+ const playwrightConfig = `
109
+ import { defineConfig, devices } from '@playwright/test';
110
+
111
+ export default defineConfig({
112
+ testDir: '${tempTestDir}',
113
+ outputDir: '${config.outputDir}',
114
+ timeout: ${config.timeout},
115
+ retries: ${config.retries},
116
+ workers: ${config.workers},
117
+ reporter: [
118
+ ['html', { outputFolder: '${path.join(config.outputDir, 'html-report')}' }],
119
+ ['json', { outputFile: '${path.join(config.outputDir, 'results.json')}' }],
120
+ ['list']
121
+ ],
122
+ use: {
123
+ baseURL: '${config.baseUrl}',
124
+ ...${JSON.stringify(config.use, null, 2)}
125
+ },
126
+ projects: [
127
+ { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
128
+ { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
129
+ { name: 'webkit', use: { ...devices['Desktop Safari'] } }
130
+ ]
131
+ });
132
+ `;
133
+
134
+ await fs.writeFile(
135
+ path.join(config.outputDir, 'playwright.config.ts'),
136
+ playwrightConfig
137
+ );
138
+ }
139
+
140
+ private async runUITests(config: any, options: TestOptions): Promise<void> {
141
+ console.log(chalk.blue('\n๐Ÿงช Running UI Tests...'));
142
+
143
+ const args = [
144
+ 'playwright',
145
+ 'test',
146
+ '--config',
147
+ path.join(config.outputDir, 'playwright.config.ts'),
148
+ ];
149
+
150
+ if (options.browser) {
151
+ args.push('--project', options.browser);
152
+ }
153
+
154
+ if (options.watch) {
155
+ args.push('--ui');
156
+ }
157
+
158
+ await this.runCommand('npx', args);
159
+
160
+ if (options.report) {
161
+ console.log(chalk.green('\n๐Ÿ“Š Opening test report...'));
162
+ await this.runCommand('npx', [
163
+ 'playwright',
164
+ 'show-report',
165
+ path.join(config.outputDir, 'html-report'),
166
+ ]);
167
+ }
168
+ }
169
+
170
+ private async runAPITests(config: any, options: TestOptions): Promise<void> {
171
+ console.log(chalk.blue('\n๐Ÿ”Œ Running API Tests...'));
172
+
173
+ const testFiles = await fs.readdir(path.join(config.testDir, 'api'));
174
+ const apiTests = testFiles.filter(f => f.endsWith('.spec.ts'));
175
+
176
+ for (const testFile of apiTests) {
177
+ console.log(chalk.gray(` Running ${testFile}...`));
178
+ await this.runCommand('npx', [
179
+ 'playwright',
180
+ 'test',
181
+ path.join(config.testDir, 'api', testFile),
182
+ '--config',
183
+ path.join(config.outputDir, 'playwright.config.ts'),
184
+ ]);
185
+ }
186
+ }
187
+
188
+ private async runUnitTests(config: any, options: TestOptions): Promise<void> {
189
+ console.log(chalk.blue('\n๐Ÿ”ฌ Running Unit Tests...'));
190
+
191
+ // Check for Jest or Vitest
192
+ const packageJson = await fs.readJson(
193
+ path.join(process.cwd(), 'package.json')
194
+ );
195
+
196
+ if (packageJson.scripts?.test) {
197
+ await this.runCommand('npm', ['run', 'test']);
198
+ } else {
199
+ console.log(chalk.yellow('No unit tests configured in package.json'));
200
+ }
201
+ }
202
+
203
+ private async runAllTests(config: any, options: TestOptions): Promise<void> {
204
+ console.log(chalk.blue('\n๐Ÿš€ Running All Tests...'));
205
+
206
+ await this.runUnitTests(config, options);
207
+ await this.runAPITests(config, options);
208
+ await this.runUITests(config, options);
209
+ }
210
+
211
+ private runCommand(command: string, args: string[]): Promise<void> {
212
+ return new Promise((resolve, reject) => {
213
+ const child = spawn(command, args, {
214
+ stdio: 'inherit',
215
+ shell: true,
216
+ });
217
+
218
+ child.on('close', code => {
219
+ if (code === 0) {
220
+ resolve();
221
+ } else {
222
+ reject(new Error(`Command failed with exit code ${code}`));
223
+ }
224
+ });
225
+
226
+ child.on('error', reject);
227
+ });
228
+ }
229
+ }
230
+
231
+ export function createTestCommand(): Command {
232
+ const command = new Command('test')
233
+ .description('Run tests against your application')
234
+ .option(
235
+ '-t, --type <type>',
236
+ 'Type of tests to run (ui, api, unit, all)',
237
+ 'all'
238
+ )
239
+ .option(
240
+ '-b, --browser <browser>',
241
+ 'Browser to use (chromium, firefox, webkit)'
242
+ )
243
+ .option('--headed', 'Run tests in headed mode')
244
+ .option('--base-url <url>', 'Base URL for the application')
245
+ .option('-c, --config <path>', 'Path to custom test configuration')
246
+ .option('-r, --report', 'Open HTML report after tests')
247
+ .option('-w, --watch', 'Run tests in watch mode')
248
+ .action(async options => {
249
+ const testCommand = new TestCommand();
250
+ await testCommand.run(options);
251
+ });
252
+
253
+ // Add init subcommand
254
+ command
255
+ .command('init')
256
+ .description('Initialize test configuration for your project')
257
+ .action(async () => {
258
+ const { TestInitCommand } = await import('./test-init');
259
+ const initCommand = new TestInitCommand();
260
+ await initCommand.run();
261
+ });
262
+
263
+ return command;
264
+ }