@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.
- package/README.md +551 -0
- package/bin/wundr.js +39 -0
- package/dist/ai/ai-service.d.ts +152 -0
- package/dist/ai/ai-service.d.ts.map +1 -0
- package/dist/ai/ai-service.js +430 -0
- package/dist/ai/ai-service.js.map +1 -0
- package/dist/ai/claude-client.d.ts +130 -0
- package/dist/ai/claude-client.d.ts.map +1 -0
- package/dist/ai/claude-client.js +339 -0
- package/dist/ai/claude-client.js.map +1 -0
- package/dist/ai/conversation-manager.d.ts +164 -0
- package/dist/ai/conversation-manager.d.ts.map +1 -0
- package/dist/ai/conversation-manager.js +612 -0
- package/dist/ai/conversation-manager.js.map +1 -0
- package/dist/ai/index.d.ts +5 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +8 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/cli.d.ts +36 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +173 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ai.d.ts +89 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +735 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/analyze-optimized.d.ts +14 -0
- package/dist/commands/analyze-optimized.d.ts.map +1 -0
- package/dist/commands/analyze-optimized.js +437 -0
- package/dist/commands/analyze-optimized.js.map +1 -0
- package/dist/commands/analyze.d.ts +65 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +435 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/batch.d.ts +71 -0
- package/dist/commands/batch.d.ts.map +1 -0
- package/dist/commands/batch.js +738 -0
- package/dist/commands/batch.js.map +1 -0
- package/dist/commands/chat.d.ts +71 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +674 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/claude-init.d.ts +28 -0
- package/dist/commands/claude-init.d.ts.map +1 -0
- package/dist/commands/claude-init.js +587 -0
- package/dist/commands/claude-init.js.map +1 -0
- package/dist/commands/claude-setup.d.ts +32 -0
- package/dist/commands/claude-setup.d.ts.map +1 -0
- package/dist/commands/claude-setup.js +570 -0
- package/dist/commands/claude-setup.js.map +1 -0
- package/dist/commands/computer-setup-commands.d.ts +39 -0
- package/dist/commands/computer-setup-commands.d.ts.map +1 -0
- package/dist/commands/computer-setup-commands.js +563 -0
- package/dist/commands/computer-setup-commands.js.map +1 -0
- package/dist/commands/computer-setup.d.ts +7 -0
- package/dist/commands/computer-setup.d.ts.map +1 -0
- package/dist/commands/computer-setup.js +481 -0
- package/dist/commands/computer-setup.js.map +1 -0
- package/dist/commands/create-command.d.ts +7 -0
- package/dist/commands/create-command.d.ts.map +1 -0
- package/dist/commands/create-command.js +158 -0
- package/dist/commands/create-command.js.map +1 -0
- package/dist/commands/create.d.ts +74 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +556 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dashboard.d.ts +91 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +537 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/govern.d.ts +70 -0
- package/dist/commands/govern.d.ts.map +1 -0
- package/dist/commands/govern.js +480 -0
- package/dist/commands/govern.js.map +1 -0
- package/dist/commands/init.d.ts +55 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +584 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/performance-optimizer.d.ts +30 -0
- package/dist/commands/performance-optimizer.d.ts.map +1 -0
- package/dist/commands/performance-optimizer.js +649 -0
- package/dist/commands/performance-optimizer.js.map +1 -0
- package/dist/commands/plugins.d.ts +87 -0
- package/dist/commands/plugins.d.ts.map +1 -0
- package/dist/commands/plugins.js +685 -0
- package/dist/commands/plugins.js.map +1 -0
- package/dist/commands/setup.d.ts +29 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +399 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/test-init.d.ts +9 -0
- package/dist/commands/test-init.d.ts.map +1 -0
- package/dist/commands/test-init.js +222 -0
- package/dist/commands/test-init.js.map +1 -0
- package/dist/commands/test.d.ts +25 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +217 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/watch.d.ts +76 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +610 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/context/context-manager.d.ts +155 -0
- package/dist/context/context-manager.d.ts.map +1 -0
- package/dist/context/context-manager.js +383 -0
- package/dist/context/context-manager.js.map +1 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +6 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/session-manager.d.ts +207 -0
- package/dist/context/session-manager.d.ts.map +1 -0
- package/dist/context/session-manager.js +682 -0
- package/dist/context/session-manager.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive/interactive-mode.d.ts +76 -0
- package/dist/interactive/interactive-mode.d.ts.map +1 -0
- package/dist/interactive/interactive-mode.js +730 -0
- package/dist/interactive/interactive-mode.js.map +1 -0
- package/dist/nlp/command-mapper.d.ts +174 -0
- package/dist/nlp/command-mapper.d.ts.map +1 -0
- package/dist/nlp/command-mapper.js +623 -0
- package/dist/nlp/command-mapper.js.map +1 -0
- package/dist/nlp/command-parser.d.ts +106 -0
- package/dist/nlp/command-parser.d.ts.map +1 -0
- package/dist/nlp/command-parser.js +416 -0
- package/dist/nlp/command-parser.js.map +1 -0
- package/dist/nlp/index.d.ts +5 -0
- package/dist/nlp/index.d.ts.map +1 -0
- package/dist/nlp/index.js +8 -0
- package/dist/nlp/index.js.map +1 -0
- package/dist/nlp/intent-classifier.d.ts +59 -0
- package/dist/nlp/intent-classifier.d.ts.map +1 -0
- package/dist/nlp/intent-classifier.js +384 -0
- package/dist/nlp/intent-classifier.js.map +1 -0
- package/dist/nlp/intent-parser.d.ts +152 -0
- package/dist/nlp/intent-parser.d.ts.map +1 -0
- package/dist/nlp/intent-parser.js +739 -0
- package/dist/nlp/intent-parser.js.map +1 -0
- package/dist/plugins/plugin-manager.d.ts +120 -0
- package/dist/plugins/plugin-manager.d.ts.map +1 -0
- package/dist/plugins/plugin-manager.js +595 -0
- package/dist/plugins/plugin-manager.js.map +1 -0
- package/dist/types/index.d.ts +224 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/config-manager.d.ts +73 -0
- package/dist/utils/config-manager.d.ts.map +1 -0
- package/dist/utils/config-manager.js +339 -0
- package/dist/utils/config-manager.js.map +1 -0
- package/dist/utils/error-handler.d.ts +46 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +169 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/logger.d.ts +25 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +94 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +119 -0
- package/src/ai/ai-service.ts +595 -0
- package/src/ai/claude-client.ts +490 -0
- package/src/ai/conversation-manager.ts +907 -0
- package/src/ai/index.ts +8 -0
- package/src/cli.ts +202 -0
- package/src/commands/ai.ts +995 -0
- package/src/commands/analyze-optimized.ts +641 -0
- package/src/commands/analyze.ts +576 -0
- package/src/commands/batch.ts +935 -0
- package/src/commands/chat.ts +876 -0
- package/src/commands/claude-init.ts +715 -0
- package/src/commands/claude-setup.ts +697 -0
- package/src/commands/computer-setup-commands.ts +709 -0
- package/src/commands/computer-setup.ts +565 -0
- package/src/commands/create-command.ts +175 -0
- package/src/commands/create.ts +727 -0
- package/src/commands/dashboard.ts +691 -0
- package/src/commands/govern.ts +635 -0
- package/src/commands/init.ts +677 -0
- package/src/commands/performance-optimizer.ts +864 -0
- package/src/commands/plugins.ts +848 -0
- package/src/commands/setup.ts +508 -0
- package/src/commands/test-init.ts +242 -0
- package/src/commands/test.ts +264 -0
- package/src/commands/watch.ts +755 -0
- package/src/context/context-manager.ts +546 -0
- package/src/context/index.ts +9 -0
- package/src/context/session-manager.ts +1019 -0
- package/src/index.ts +64 -0
- package/src/interactive/interactive-mode.ts +830 -0
- package/src/nlp/command-mapper.ts +885 -0
- package/src/nlp/command-parser.ts +564 -0
- package/src/nlp/index.ts +4 -0
- package/src/nlp/intent-classifier.ts +458 -0
- package/src/nlp/intent-parser.ts +1101 -0
- package/src/plugins/plugin-manager.ts +744 -0
- package/src/types/index.ts +252 -0
- package/src/types/modules.d.ts +56 -0
- package/src/utils/config-manager.ts +391 -0
- package/src/utils/error-handler.ts +192 -0
- package/src/utils/logger.ts +104 -0
- package/templates/batch/ci-cd.yaml +62 -0
- package/templates/component/{{fileName}}.test.tsx +17 -0
- package/templates/component/{{fileName}}.tsx +21 -0
- package/templates/service/{{fileName}}.ts +98 -0
- package/templates/wundr-test.config.js +0 -0
- package/test-suites/api/health.spec.ts +134 -0
- package/test-suites/helpers/test-config.ts +84 -0
- package/test-suites/ui/accessibility.spec.ts +102 -0
- 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
|
+
}
|