@wundr.io/cli 1.0.0 → 1.0.2-dev.20260530174250.ef0ec927
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 +696 -280
- package/bin/wundr.js +13 -5
- package/package.json +30 -9
- package/src/ai/ai-service.ts +6 -4
- package/src/ai/claude-client.ts +6 -2
- package/src/ai/conversation-manager.ts +12 -5
- package/src/cli.ts +42 -13
- package/src/commands/ai.ts +340 -64
- package/src/commands/alignment.ts +1212 -0
- package/src/commands/analyze-optimized.ts +371 -33
- package/src/commands/analyze.ts +8 -6
- package/src/commands/batch.ts +166 -26
- package/src/commands/chat.ts +20 -10
- package/src/commands/claude-init.ts +31 -27
- package/src/commands/claude-setup.ts +761 -81
- package/src/commands/computer-setup.ts +524 -12
- package/src/commands/create-command.ts +3 -3
- package/src/commands/create.ts +9 -6
- package/src/commands/dashboard.ts +11 -6
- package/src/commands/govern.ts +11 -6
- package/src/commands/governance.ts +1005 -0
- package/src/commands/guardian.ts +887 -0
- package/src/commands/init.ts +104 -11
- package/src/commands/orchestrator.ts +789 -0
- package/src/commands/performance-optimizer.ts +15 -10
- package/src/commands/plugins.ts +8 -5
- package/src/commands/project-update.ts +1156 -0
- package/src/commands/rag.ts +1011 -0
- package/src/commands/session.ts +631 -0
- package/src/commands/setup.ts +42 -344
- package/src/commands/test-init.ts +3 -2
- package/src/commands/test.ts +3 -2
- package/src/commands/watch.ts +21 -11
- package/src/commands/worktree.ts +1057 -0
- package/src/context/context-manager.ts +5 -2
- package/src/context/session-manager.ts +18 -7
- package/src/framework/command-interface.ts +520 -0
- package/src/framework/command-registry.ts +942 -0
- package/src/framework/completion-exporter.ts +383 -0
- package/src/framework/debug-logger.ts +519 -0
- package/src/framework/error-handler.ts +867 -0
- package/src/framework/help-generator.ts +540 -0
- package/src/framework/index.ts +169 -0
- package/src/framework/interactive-repl.ts +703 -0
- package/src/framework/output-formatter.ts +834 -0
- package/src/framework/progress-manager.ts +539 -0
- package/src/index.ts +3 -2
- package/src/interactive/interactive-mode.ts +14 -7
- package/src/lib/conflict-resolution.ts +818 -0
- package/src/lib/merge-strategy.ts +550 -0
- package/src/lib/safety-mechanisms.ts +451 -0
- package/src/lib/state-detection.ts +1030 -0
- package/src/nlp/command-mapper.ts +8 -3
- package/src/nlp/command-parser.ts +5 -2
- package/src/nlp/intent-parser.ts +23 -9
- package/src/plugins/plugin-manager.ts +50 -24
- package/src/tests/computer-setup-integration.test.ts +470 -0
- package/src/types/index.ts +1 -1
- package/src/types/modules.d.ts +425 -1
- package/src/utils/backup-rollback-manager.ts +366 -0
- package/src/utils/claude-config-installer.ts +823 -0
- package/src/utils/config-manager.ts +9 -6
- package/src/utils/error-handler.ts +3 -1
- package/src/utils/logger.ts +35 -12
- package/templates/batch/ci-cd.yaml +7 -7
- package/test-suites/api/health.spec.ts +20 -23
- package/test-suites/helpers/test-config.ts +14 -13
- package/test-suites/ui/accessibility.spec.ts +27 -22
- package/test-suites/ui/smoke.spec.ts +26 -21
- package/dist/ai/ai-service.d.ts +0 -152
- package/dist/ai/ai-service.d.ts.map +0 -1
- package/dist/ai/ai-service.js +0 -430
- package/dist/ai/ai-service.js.map +0 -1
- package/dist/ai/claude-client.d.ts +0 -130
- package/dist/ai/claude-client.d.ts.map +0 -1
- package/dist/ai/claude-client.js +0 -339
- package/dist/ai/claude-client.js.map +0 -1
- package/dist/ai/conversation-manager.d.ts +0 -164
- package/dist/ai/conversation-manager.d.ts.map +0 -1
- package/dist/ai/conversation-manager.js +0 -612
- package/dist/ai/conversation-manager.js.map +0 -1
- package/dist/ai/index.d.ts +0 -5
- package/dist/ai/index.d.ts.map +0 -1
- package/dist/ai/index.js +0 -8
- package/dist/ai/index.js.map +0 -1
- package/dist/cli.d.ts +0 -36
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -173
- package/dist/cli.js.map +0 -1
- package/dist/commands/ai.d.ts +0 -89
- package/dist/commands/ai.d.ts.map +0 -1
- package/dist/commands/ai.js +0 -735
- package/dist/commands/ai.js.map +0 -1
- package/dist/commands/analyze-optimized.d.ts +0 -14
- package/dist/commands/analyze-optimized.d.ts.map +0 -1
- package/dist/commands/analyze-optimized.js +0 -437
- package/dist/commands/analyze-optimized.js.map +0 -1
- package/dist/commands/analyze.d.ts +0 -65
- package/dist/commands/analyze.d.ts.map +0 -1
- package/dist/commands/analyze.js +0 -435
- package/dist/commands/analyze.js.map +0 -1
- package/dist/commands/batch.d.ts +0 -71
- package/dist/commands/batch.d.ts.map +0 -1
- package/dist/commands/batch.js +0 -738
- package/dist/commands/batch.js.map +0 -1
- package/dist/commands/chat.d.ts +0 -71
- package/dist/commands/chat.d.ts.map +0 -1
- package/dist/commands/chat.js +0 -674
- package/dist/commands/chat.js.map +0 -1
- package/dist/commands/claude-init.d.ts +0 -28
- package/dist/commands/claude-init.d.ts.map +0 -1
- package/dist/commands/claude-init.js +0 -587
- package/dist/commands/claude-init.js.map +0 -1
- package/dist/commands/claude-setup.d.ts +0 -32
- package/dist/commands/claude-setup.d.ts.map +0 -1
- package/dist/commands/claude-setup.js +0 -570
- package/dist/commands/claude-setup.js.map +0 -1
- package/dist/commands/computer-setup-commands.d.ts +0 -39
- package/dist/commands/computer-setup-commands.d.ts.map +0 -1
- package/dist/commands/computer-setup-commands.js +0 -563
- package/dist/commands/computer-setup-commands.js.map +0 -1
- package/dist/commands/computer-setup.d.ts +0 -7
- package/dist/commands/computer-setup.d.ts.map +0 -1
- package/dist/commands/computer-setup.js +0 -481
- package/dist/commands/computer-setup.js.map +0 -1
- package/dist/commands/create-command.d.ts +0 -7
- package/dist/commands/create-command.d.ts.map +0 -1
- package/dist/commands/create-command.js +0 -158
- package/dist/commands/create-command.js.map +0 -1
- package/dist/commands/create.d.ts +0 -74
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js +0 -556
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/dashboard.d.ts +0 -91
- package/dist/commands/dashboard.d.ts.map +0 -1
- package/dist/commands/dashboard.js +0 -537
- package/dist/commands/dashboard.js.map +0 -1
- package/dist/commands/govern.d.ts +0 -70
- package/dist/commands/govern.d.ts.map +0 -1
- package/dist/commands/govern.js +0 -480
- package/dist/commands/govern.js.map +0 -1
- package/dist/commands/init.d.ts +0 -55
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -584
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/performance-optimizer.d.ts +0 -30
- package/dist/commands/performance-optimizer.d.ts.map +0 -1
- package/dist/commands/performance-optimizer.js +0 -649
- package/dist/commands/performance-optimizer.js.map +0 -1
- package/dist/commands/plugins.d.ts +0 -87
- package/dist/commands/plugins.d.ts.map +0 -1
- package/dist/commands/plugins.js +0 -685
- package/dist/commands/plugins.js.map +0 -1
- package/dist/commands/setup.d.ts +0 -29
- package/dist/commands/setup.d.ts.map +0 -1
- package/dist/commands/setup.js +0 -399
- package/dist/commands/setup.js.map +0 -1
- package/dist/commands/test-init.d.ts +0 -9
- package/dist/commands/test-init.d.ts.map +0 -1
- package/dist/commands/test-init.js +0 -222
- package/dist/commands/test-init.js.map +0 -1
- package/dist/commands/test.d.ts +0 -25
- package/dist/commands/test.d.ts.map +0 -1
- package/dist/commands/test.js +0 -217
- package/dist/commands/test.js.map +0 -1
- package/dist/commands/watch.d.ts +0 -76
- package/dist/commands/watch.d.ts.map +0 -1
- package/dist/commands/watch.js +0 -610
- package/dist/commands/watch.js.map +0 -1
- package/dist/context/context-manager.d.ts +0 -155
- package/dist/context/context-manager.d.ts.map +0 -1
- package/dist/context/context-manager.js +0 -383
- package/dist/context/context-manager.js.map +0 -1
- package/dist/context/index.d.ts +0 -3
- package/dist/context/index.d.ts.map +0 -1
- package/dist/context/index.js +0 -6
- package/dist/context/index.js.map +0 -1
- package/dist/context/session-manager.d.ts +0 -207
- package/dist/context/session-manager.d.ts.map +0 -1
- package/dist/context/session-manager.js +0 -682
- package/dist/context/session-manager.js.map +0 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -51
- package/dist/index.js.map +0 -1
- package/dist/interactive/interactive-mode.d.ts +0 -76
- package/dist/interactive/interactive-mode.d.ts.map +0 -1
- package/dist/interactive/interactive-mode.js +0 -730
- package/dist/interactive/interactive-mode.js.map +0 -1
- package/dist/nlp/command-mapper.d.ts +0 -174
- package/dist/nlp/command-mapper.d.ts.map +0 -1
- package/dist/nlp/command-mapper.js +0 -623
- package/dist/nlp/command-mapper.js.map +0 -1
- package/dist/nlp/command-parser.d.ts +0 -106
- package/dist/nlp/command-parser.d.ts.map +0 -1
- package/dist/nlp/command-parser.js +0 -416
- package/dist/nlp/command-parser.js.map +0 -1
- package/dist/nlp/index.d.ts +0 -5
- package/dist/nlp/index.d.ts.map +0 -1
- package/dist/nlp/index.js +0 -8
- package/dist/nlp/index.js.map +0 -1
- package/dist/nlp/intent-classifier.d.ts +0 -59
- package/dist/nlp/intent-classifier.d.ts.map +0 -1
- package/dist/nlp/intent-classifier.js +0 -384
- package/dist/nlp/intent-classifier.js.map +0 -1
- package/dist/nlp/intent-parser.d.ts +0 -152
- package/dist/nlp/intent-parser.d.ts.map +0 -1
- package/dist/nlp/intent-parser.js +0 -739
- package/dist/nlp/intent-parser.js.map +0 -1
- package/dist/plugins/plugin-manager.d.ts +0 -120
- package/dist/plugins/plugin-manager.d.ts.map +0 -1
- package/dist/plugins/plugin-manager.js +0 -595
- package/dist/plugins/plugin-manager.js.map +0 -1
- package/dist/types/index.d.ts +0 -224
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +0 -1
- package/dist/utils/config-manager.d.ts +0 -73
- package/dist/utils/config-manager.d.ts.map +0 -1
- package/dist/utils/config-manager.js +0 -339
- package/dist/utils/config-manager.js.map +0 -1
- package/dist/utils/error-handler.d.ts +0 -46
- package/dist/utils/error-handler.d.ts.map +0 -1
- package/dist/utils/error-handler.js +0 -169
- package/dist/utils/error-handler.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -25
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -94
- package/dist/utils/logger.js.map +0 -1
- package/src/commands/computer-setup-commands.ts +0 -709
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
1
|
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
import fs from 'fs-extra';
|
|
4
5
|
import { z } from 'zod';
|
|
5
|
-
|
|
6
|
-
import { logger } from './logger';
|
|
6
|
+
|
|
7
7
|
import { errorHandler } from './error-handler';
|
|
8
|
+
import { logger } from './logger';
|
|
9
|
+
|
|
10
|
+
import type { WundrConfig } from '../types';
|
|
8
11
|
|
|
9
12
|
// Zod schema for configuration validation
|
|
10
13
|
const WundrConfigSchema = z.object({
|
|
@@ -213,7 +216,7 @@ export class ConfigManager {
|
|
|
213
216
|
throw new Error(`Invalid path: empty key at position ${i}`);
|
|
214
217
|
}
|
|
215
218
|
if (!current || typeof current !== 'object') {
|
|
216
|
-
throw new Error(
|
|
219
|
+
throw new Error('Invalid path: cannot set property on non-object');
|
|
217
220
|
}
|
|
218
221
|
if (!current[key] || typeof current[key] !== 'object') {
|
|
219
222
|
current[key] = {};
|
|
@@ -226,7 +229,7 @@ export class ConfigManager {
|
|
|
226
229
|
throw new Error('Invalid path: empty final key');
|
|
227
230
|
}
|
|
228
231
|
if (!finalKey || !current || typeof current !== 'object') {
|
|
229
|
-
throw new Error(
|
|
232
|
+
throw new Error('Invalid path: cannot set property');
|
|
230
233
|
}
|
|
231
234
|
current[finalKey] = value;
|
|
232
235
|
}
|
package/src/utils/logger.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import type { Logger } from '../types';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Enhanced logger with multiple levels and colored output
|
|
@@ -17,7 +18,9 @@ class WundrLogger implements Logger {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
private shouldLog(level: string): boolean {
|
|
20
|
-
if (this.silent)
|
|
21
|
+
if (this.silent) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
21
24
|
|
|
22
25
|
const levels = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
23
26
|
return levels[level as keyof typeof levels] >= levels[this.level];
|
|
@@ -36,54 +39,74 @@ class WundrLogger implements Logger {
|
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
debug(message: string, ...args: any[]): void {
|
|
39
|
-
if (!this.shouldLog('debug'))
|
|
42
|
+
if (!this.shouldLog('debug')) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
40
45
|
console.log(chalk.gray(this.formatMessage('debug', message, ...args)));
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
info(message: string, ...args: any[]): void {
|
|
44
|
-
if (!this.shouldLog('info'))
|
|
49
|
+
if (!this.shouldLog('info')) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
45
52
|
console.log(chalk.blue(this.formatMessage('info', message, ...args)));
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
warn(message: string, ...args: any[]): void {
|
|
49
|
-
if (!this.shouldLog('warn'))
|
|
56
|
+
if (!this.shouldLog('warn')) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
50
59
|
console.warn(chalk.yellow(this.formatMessage('warn', message, ...args)));
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
error(message: string, ...args: any[]): void {
|
|
54
|
-
if (!this.shouldLog('error'))
|
|
63
|
+
if (!this.shouldLog('error')) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
55
66
|
console.error(chalk.red(this.formatMessage('error', message, ...args)));
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
success(message: string, ...args: any[]): void {
|
|
59
|
-
if (!this.shouldLog('info'))
|
|
70
|
+
if (!this.shouldLog('info')) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
60
73
|
console.log(chalk.green(this.formatMessage('success', message, ...args)));
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
// Utility methods for structured logging
|
|
64
77
|
table(data: any[]): void {
|
|
65
|
-
if (this.silent)
|
|
78
|
+
if (this.silent) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
66
81
|
console.table(data);
|
|
67
82
|
}
|
|
68
83
|
|
|
69
84
|
json(data: any): void {
|
|
70
|
-
if (this.silent)
|
|
85
|
+
if (this.silent) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
71
88
|
console.log(JSON.stringify(data, null, 2));
|
|
72
89
|
}
|
|
73
90
|
|
|
74
91
|
group(label: string): void {
|
|
75
|
-
if (this.silent)
|
|
92
|
+
if (this.silent) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
76
95
|
console.group(chalk.cyan(label));
|
|
77
96
|
}
|
|
78
97
|
|
|
79
98
|
groupEnd(): void {
|
|
80
|
-
if (this.silent)
|
|
99
|
+
if (this.silent) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
81
102
|
console.groupEnd();
|
|
82
103
|
}
|
|
83
104
|
|
|
84
105
|
// Progress logging
|
|
85
106
|
progress(current: number, total: number, message?: string): void {
|
|
86
|
-
if (this.silent)
|
|
107
|
+
if (this.silent) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
87
110
|
const percentage = Math.round((current / total) * 100);
|
|
88
111
|
const bar = '█'.repeat(Math.round(percentage / 2));
|
|
89
112
|
const empty = '░'.repeat(50 - Math.round(percentage / 2));
|
|
@@ -7,7 +7,7 @@ description: Continuous Integration and Deployment pipeline
|
|
|
7
7
|
# Global settings
|
|
8
8
|
parallel: false
|
|
9
9
|
continueOnError: false
|
|
10
|
-
timeout: 1800000
|
|
10
|
+
timeout: 1800000 # 30 minutes
|
|
11
11
|
|
|
12
12
|
# Environment variables
|
|
13
13
|
variables:
|
|
@@ -23,7 +23,7 @@ commands:
|
|
|
23
23
|
# Install dependencies
|
|
24
24
|
- command: "npm ci"
|
|
25
25
|
retry: 2
|
|
26
|
-
timeout: 300000
|
|
26
|
+
timeout: 300000 # 5 minutes
|
|
27
27
|
|
|
28
28
|
# Code quality checks
|
|
29
29
|
- command: "npm run lint"
|
|
@@ -34,11 +34,11 @@ commands:
|
|
|
34
34
|
|
|
35
35
|
# Testing
|
|
36
36
|
- command: "npm run test"
|
|
37
|
-
timeout: 600000
|
|
37
|
+
timeout: 600000 # 10 minutes
|
|
38
38
|
|
|
39
39
|
- command: "npm run test:e2e"
|
|
40
40
|
condition: "e2e-tests-exist"
|
|
41
|
-
timeout: 900000
|
|
41
|
+
timeout: 900000 # 15 minutes
|
|
42
42
|
|
|
43
43
|
# Security audit
|
|
44
44
|
- command: "npm audit --audit-level high"
|
|
@@ -46,7 +46,7 @@ commands:
|
|
|
46
46
|
|
|
47
47
|
# Build
|
|
48
48
|
- command: "npm run build"
|
|
49
|
-
timeout: 300000
|
|
49
|
+
timeout: 300000 # 5 minutes
|
|
50
50
|
|
|
51
51
|
# Package
|
|
52
52
|
- command: "npm pack"
|
|
@@ -55,8 +55,8 @@ commands:
|
|
|
55
55
|
# Deploy (conditional)
|
|
56
56
|
- command: "npm run deploy"
|
|
57
57
|
condition: "production"
|
|
58
|
-
timeout: 600000
|
|
58
|
+
timeout: 600000 # 10 minutes
|
|
59
59
|
|
|
60
60
|
# Cleanup
|
|
61
61
|
- command: "echo 'CI/CD pipeline completed'"
|
|
62
|
-
condition: "always"
|
|
62
|
+
condition: "always"
|
|
@@ -15,11 +15,11 @@ test.describe('API Health Checks', () => {
|
|
|
15
15
|
'/status',
|
|
16
16
|
'/api/status',
|
|
17
17
|
'/_health',
|
|
18
|
-
'/ping'
|
|
18
|
+
'/ping',
|
|
19
19
|
];
|
|
20
20
|
|
|
21
21
|
let healthyEndpoint = null;
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
for (const endpoint of healthEndpoints) {
|
|
24
24
|
try {
|
|
25
25
|
const response = await request.get(`${baseURL}${endpoint}`);
|
|
@@ -38,23 +38,18 @@ test.describe('API Health Checks', () => {
|
|
|
38
38
|
|
|
39
39
|
test('API returns proper content types', async ({ request, baseURL }) => {
|
|
40
40
|
// Try to find an API endpoint
|
|
41
|
-
const apiEndpoints = [
|
|
42
|
-
'/api',
|
|
43
|
-
'/api/v1',
|
|
44
|
-
'/api/v2',
|
|
45
|
-
'/graphql'
|
|
46
|
-
];
|
|
41
|
+
const apiEndpoints = ['/api', '/api/v1', '/api/v2', '/graphql'];
|
|
47
42
|
|
|
48
43
|
for (const endpoint of apiEndpoints) {
|
|
49
44
|
try {
|
|
50
45
|
const response = await request.get(`${baseURL}${endpoint}`);
|
|
51
46
|
if (response.ok() || response.status() === 404) {
|
|
52
47
|
const contentType = response.headers()['content-type'];
|
|
53
|
-
|
|
48
|
+
|
|
54
49
|
// Should return JSON or HTML
|
|
55
50
|
expect(
|
|
56
51
|
contentType?.includes('application/json') ||
|
|
57
|
-
|
|
52
|
+
contentType?.includes('text/html')
|
|
58
53
|
).toBeTruthy();
|
|
59
54
|
}
|
|
60
55
|
} catch {
|
|
@@ -65,11 +60,13 @@ test.describe('API Health Checks', () => {
|
|
|
65
60
|
|
|
66
61
|
test('API handles errors gracefully', async ({ request, baseURL }) => {
|
|
67
62
|
// Test 404 handling
|
|
68
|
-
const response = await request.get(
|
|
69
|
-
|
|
63
|
+
const response = await request.get(
|
|
64
|
+
`${baseURL}/api/nonexistent-endpoint-${Date.now()}`
|
|
65
|
+
);
|
|
66
|
+
|
|
70
67
|
// Should return appropriate status code
|
|
71
68
|
expect([404, 400, 401, 403]).toContain(response.status());
|
|
72
|
-
|
|
69
|
+
|
|
73
70
|
// Should not expose sensitive information
|
|
74
71
|
const body = await response.text();
|
|
75
72
|
expect(body).not.toContain('stack');
|
|
@@ -78,12 +75,12 @@ test.describe('API Health Checks', () => {
|
|
|
78
75
|
|
|
79
76
|
test('API responds within acceptable time', async ({ request, baseURL }) => {
|
|
80
77
|
const startTime = Date.now();
|
|
81
|
-
|
|
78
|
+
|
|
82
79
|
// Make a simple request
|
|
83
80
|
await request.get(`${baseURL}/`);
|
|
84
|
-
|
|
81
|
+
|
|
85
82
|
const responseTime = Date.now() - startTime;
|
|
86
|
-
|
|
83
|
+
|
|
87
84
|
// Should respond within 5 seconds
|
|
88
85
|
expect(responseTime).toBeLessThan(5000);
|
|
89
86
|
});
|
|
@@ -92,18 +89,18 @@ test.describe('API Health Checks', () => {
|
|
|
92
89
|
try {
|
|
93
90
|
const response = await request.get(`${baseURL}/api`, {
|
|
94
91
|
headers: {
|
|
95
|
-
|
|
96
|
-
}
|
|
92
|
+
Origin: 'https://example.com',
|
|
93
|
+
},
|
|
97
94
|
});
|
|
98
95
|
|
|
99
96
|
const headers = response.headers();
|
|
100
|
-
|
|
97
|
+
|
|
101
98
|
// Check for CORS headers if API exists
|
|
102
99
|
if (response.status() !== 404) {
|
|
103
|
-
const hasCorsHeaders =
|
|
100
|
+
const hasCorsHeaders =
|
|
104
101
|
headers['access-control-allow-origin'] !== undefined ||
|
|
105
102
|
headers['access-control-allow-methods'] !== undefined;
|
|
106
|
-
|
|
103
|
+
|
|
107
104
|
// Log CORS configuration
|
|
108
105
|
console.log('CORS configured:', hasCorsHeaders);
|
|
109
106
|
}
|
|
@@ -119,7 +116,7 @@ test.describe('API Health Checks', () => {
|
|
|
119
116
|
for (const method of methods) {
|
|
120
117
|
try {
|
|
121
118
|
const response = await request.fetch(`${baseURL}/api`, {
|
|
122
|
-
method
|
|
119
|
+
method,
|
|
123
120
|
});
|
|
124
121
|
results[method] = response.status();
|
|
125
122
|
} catch {
|
|
@@ -131,4 +128,4 @@ test.describe('API Health Checks', () => {
|
|
|
131
128
|
expect(results['GET']).toBeGreaterThan(0);
|
|
132
129
|
expect(results['OPTIONS']).toBeGreaterThan(0);
|
|
133
130
|
});
|
|
134
|
-
});
|
|
131
|
+
});
|
|
@@ -12,7 +12,7 @@ export interface TestConfig {
|
|
|
12
12
|
screenshot: 'off' | 'on' | 'only-on-failure';
|
|
13
13
|
video: 'off' | 'on' | 'retain-on-failure';
|
|
14
14
|
trace: 'off' | 'on' | 'on-first-retry';
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
// Custom selectors for specific apps
|
|
17
17
|
selectors?: {
|
|
18
18
|
navigation?: string;
|
|
@@ -21,14 +21,14 @@ export interface TestConfig {
|
|
|
21
21
|
searchInput?: string;
|
|
22
22
|
loginButton?: string;
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
// API configuration
|
|
26
26
|
api?: {
|
|
27
27
|
baseURL?: string;
|
|
28
28
|
headers?: Record<string, string>;
|
|
29
29
|
timeout?: number;
|
|
30
30
|
};
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
// Test data
|
|
33
33
|
testData?: {
|
|
34
34
|
validUser?: {
|
|
@@ -47,18 +47,19 @@ export const defaultConfig: TestConfig = {
|
|
|
47
47
|
screenshot: 'only-on-failure',
|
|
48
48
|
video: 'retain-on-failure',
|
|
49
49
|
trace: 'on-first-retry',
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
selectors: {
|
|
52
52
|
navigation: 'nav, [role="navigation"], header',
|
|
53
53
|
mainContent: 'main, [role="main"], #content',
|
|
54
54
|
footer: 'footer, [role="contentinfo"]',
|
|
55
55
|
searchInput: 'input[type="search"], input[placeholder*="search" i]',
|
|
56
|
-
loginButton:
|
|
56
|
+
loginButton:
|
|
57
|
+
'button[type="submit"], button:has-text("Login"), button:has-text("Sign in")',
|
|
57
58
|
},
|
|
58
|
-
|
|
59
|
+
|
|
59
60
|
api: {
|
|
60
|
-
timeout: 10000
|
|
61
|
-
}
|
|
61
|
+
timeout: 10000,
|
|
62
|
+
},
|
|
62
63
|
};
|
|
63
64
|
|
|
64
65
|
/**
|
|
@@ -70,15 +71,15 @@ export function loadConfig(customConfig?: Partial<TestConfig>): TestConfig {
|
|
|
70
71
|
...customConfig,
|
|
71
72
|
selectors: {
|
|
72
73
|
...defaultConfig.selectors,
|
|
73
|
-
...customConfig?.selectors
|
|
74
|
+
...customConfig?.selectors,
|
|
74
75
|
},
|
|
75
76
|
api: {
|
|
76
77
|
...defaultConfig.api,
|
|
77
|
-
...customConfig?.api
|
|
78
|
+
...customConfig?.api,
|
|
78
79
|
},
|
|
79
80
|
testData: {
|
|
80
81
|
...defaultConfig.testData,
|
|
81
|
-
...customConfig?.testData
|
|
82
|
-
}
|
|
82
|
+
...customConfig?.testData,
|
|
83
|
+
},
|
|
83
84
|
};
|
|
84
|
-
}
|
|
85
|
+
}
|
|
@@ -8,23 +8,23 @@ import AxeBuilder from '@axe-core/playwright';
|
|
|
8
8
|
test.describe('Accessibility Tests', () => {
|
|
9
9
|
test('homepage meets WCAG standards', async ({ page }) => {
|
|
10
10
|
await page.goto('/');
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
const accessibilityScanResults = await new AxeBuilder({ page })
|
|
13
13
|
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
|
14
14
|
.analyze();
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
expect(accessibilityScanResults.violations).toEqual([]);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
test('all images have alt text', async ({ page }) => {
|
|
20
20
|
await page.goto('/');
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
const images = await page.locator('img').all();
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
for (const img of images) {
|
|
25
25
|
const alt = await img.getAttribute('alt');
|
|
26
26
|
const role = await img.getAttribute('role');
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
// Images should have alt text or be marked as decorative
|
|
29
29
|
expect(alt !== null || role === 'presentation').toBeTruthy();
|
|
30
30
|
}
|
|
@@ -32,18 +32,19 @@ test.describe('Accessibility Tests', () => {
|
|
|
32
32
|
|
|
33
33
|
test('forms have proper labels', async ({ page }) => {
|
|
34
34
|
await page.goto('/');
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
const inputs = await page.locator('input, select, textarea').all();
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
for (const input of inputs) {
|
|
39
39
|
const id = await input.getAttribute('id');
|
|
40
40
|
const ariaLabel = await input.getAttribute('aria-label');
|
|
41
41
|
const ariaLabelledBy = await input.getAttribute('aria-labelledby');
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
if (id) {
|
|
44
44
|
// Check for associated label
|
|
45
45
|
const label = await page.locator(`label[for="${id}"]`).count();
|
|
46
|
-
const hasLabel =
|
|
46
|
+
const hasLabel =
|
|
47
|
+
label > 0 || ariaLabel !== null || ariaLabelledBy !== null;
|
|
47
48
|
expect(hasLabel).toBeTruthy();
|
|
48
49
|
}
|
|
49
50
|
}
|
|
@@ -51,47 +52,51 @@ test.describe('Accessibility Tests', () => {
|
|
|
51
52
|
|
|
52
53
|
test('focus is visible and logical', async ({ page }) => {
|
|
53
54
|
await page.goto('/');
|
|
54
|
-
|
|
55
|
+
|
|
55
56
|
// Tab through interactive elements
|
|
56
57
|
await page.keyboard.press('Tab');
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
// Check if focused element has visible outline
|
|
59
|
-
const focusedElement = await page.evaluateHandle(
|
|
60
|
-
|
|
60
|
+
const focusedElement = await page.evaluateHandle(
|
|
61
|
+
() => document.activeElement
|
|
62
|
+
);
|
|
63
|
+
const hasOutline = await focusedElement.evaluate(el => {
|
|
61
64
|
if (!el) return false;
|
|
62
65
|
const styles = window.getComputedStyle(el as Element);
|
|
63
66
|
return styles.outlineStyle !== 'none' || styles.boxShadow !== 'none';
|
|
64
67
|
});
|
|
65
|
-
|
|
68
|
+
|
|
66
69
|
expect(hasOutline).toBeTruthy();
|
|
67
70
|
});
|
|
68
71
|
|
|
69
72
|
test('color contrast meets standards', async ({ page }) => {
|
|
70
73
|
await page.goto('/');
|
|
71
|
-
|
|
74
|
+
|
|
72
75
|
const accessibilityScanResults = await new AxeBuilder({ page })
|
|
73
76
|
.withTags(['color-contrast'])
|
|
74
77
|
.analyze();
|
|
75
|
-
|
|
78
|
+
|
|
76
79
|
expect(accessibilityScanResults.violations).toEqual([]);
|
|
77
80
|
});
|
|
78
81
|
|
|
79
82
|
test('page has proper heading structure', async ({ page }) => {
|
|
80
83
|
await page.goto('/');
|
|
81
|
-
|
|
84
|
+
|
|
82
85
|
// Check for h1
|
|
83
86
|
const h1Count = await page.locator('h1').count();
|
|
84
87
|
expect(h1Count).toBeGreaterThan(0);
|
|
85
|
-
|
|
88
|
+
|
|
86
89
|
// Check heading hierarchy
|
|
87
90
|
const headings = await page.evaluate(() => {
|
|
88
|
-
const headingElements = document.querySelectorAll(
|
|
91
|
+
const headingElements = document.querySelectorAll(
|
|
92
|
+
'h1, h2, h3, h4, h5, h6'
|
|
93
|
+
);
|
|
89
94
|
return Array.from(headingElements).map(h => ({
|
|
90
95
|
level: parseInt(h.tagName[1]),
|
|
91
|
-
text: h.textContent
|
|
96
|
+
text: h.textContent,
|
|
92
97
|
}));
|
|
93
98
|
});
|
|
94
|
-
|
|
99
|
+
|
|
95
100
|
// Verify no skipped heading levels
|
|
96
101
|
let previousLevel = 0;
|
|
97
102
|
for (const heading of headings) {
|
|
@@ -99,4 +104,4 @@ test.describe('Accessibility Tests', () => {
|
|
|
99
104
|
previousLevel = heading.level;
|
|
100
105
|
}
|
|
101
106
|
});
|
|
102
|
-
});
|
|
107
|
+
});
|
|
@@ -8,8 +8,8 @@ import { test, expect } from '@playwright/test';
|
|
|
8
8
|
test.describe('Portable Smoke Tests', () => {
|
|
9
9
|
test('homepage loads without errors', async ({ page }) => {
|
|
10
10
|
const jsErrors: string[] = [];
|
|
11
|
-
|
|
12
|
-
page.on('pageerror',
|
|
11
|
+
|
|
12
|
+
page.on('pageerror', error => {
|
|
13
13
|
jsErrors.push(error.message);
|
|
14
14
|
});
|
|
15
15
|
|
|
@@ -18,31 +18,33 @@ test.describe('Portable Smoke Tests', () => {
|
|
|
18
18
|
|
|
19
19
|
// Basic structure should be present
|
|
20
20
|
await expect(page.locator('body')).toBeVisible();
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
// Should not have critical JavaScript errors
|
|
23
|
-
const criticalErrors = jsErrors.filter(
|
|
24
|
-
error
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const criticalErrors = jsErrors.filter(
|
|
24
|
+
error =>
|
|
25
|
+
error.includes('Cannot read') ||
|
|
26
|
+
error.includes('undefined is not') ||
|
|
27
|
+
error.includes('Uncaught')
|
|
27
28
|
);
|
|
28
|
-
|
|
29
|
+
|
|
29
30
|
expect(criticalErrors.length).toBe(0);
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
test('navigation elements are present', async ({ page }) => {
|
|
33
34
|
await page.goto('/');
|
|
34
|
-
|
|
35
|
+
|
|
35
36
|
// Check for common navigation patterns
|
|
36
|
-
const hasNavigation =
|
|
37
|
+
const hasNavigation =
|
|
38
|
+
(await page.locator('nav, [role="navigation"], header').count()) > 0;
|
|
37
39
|
expect(hasNavigation).toBeTruthy();
|
|
38
40
|
});
|
|
39
41
|
|
|
40
42
|
test('interactive elements are clickable', async ({ page }) => {
|
|
41
43
|
await page.goto('/');
|
|
42
|
-
|
|
44
|
+
|
|
43
45
|
// Find clickable elements
|
|
44
46
|
const buttons = await page.locator('button:visible, a:visible').all();
|
|
45
|
-
|
|
47
|
+
|
|
46
48
|
if (buttons.length > 0) {
|
|
47
49
|
// Test first button/link
|
|
48
50
|
const firstElement = buttons[0];
|
|
@@ -52,10 +54,10 @@ test.describe('Portable Smoke Tests', () => {
|
|
|
52
54
|
|
|
53
55
|
test('forms accept input', async ({ page }) => {
|
|
54
56
|
await page.goto('/');
|
|
55
|
-
|
|
57
|
+
|
|
56
58
|
// Look for form inputs
|
|
57
59
|
const inputs = await page.locator('input:visible, textarea:visible').all();
|
|
58
|
-
|
|
60
|
+
|
|
59
61
|
if (inputs.length > 0) {
|
|
60
62
|
const firstInput = inputs[0];
|
|
61
63
|
await firstInput.fill('test');
|
|
@@ -66,27 +68,30 @@ test.describe('Portable Smoke Tests', () => {
|
|
|
66
68
|
|
|
67
69
|
test('responsive layout works', async ({ page }) => {
|
|
68
70
|
await page.goto('/');
|
|
69
|
-
|
|
71
|
+
|
|
70
72
|
// Test mobile viewport
|
|
71
73
|
await page.setViewportSize({ width: 375, height: 667 });
|
|
72
|
-
|
|
74
|
+
|
|
73
75
|
// Should not have horizontal scroll
|
|
74
76
|
const hasHorizontalScroll = await page.evaluate(() => {
|
|
75
|
-
return
|
|
77
|
+
return (
|
|
78
|
+
document.documentElement.scrollWidth >
|
|
79
|
+
document.documentElement.clientWidth
|
|
80
|
+
);
|
|
76
81
|
});
|
|
77
|
-
|
|
82
|
+
|
|
78
83
|
expect(hasHorizontalScroll).toBeFalsy();
|
|
79
84
|
});
|
|
80
85
|
|
|
81
86
|
test('page has proper metadata', async ({ page }) => {
|
|
82
87
|
await page.goto('/');
|
|
83
|
-
|
|
88
|
+
|
|
84
89
|
// Check for title
|
|
85
90
|
const title = await page.title();
|
|
86
91
|
expect(title).toBeTruthy();
|
|
87
|
-
|
|
92
|
+
|
|
88
93
|
// Check for language attribute
|
|
89
94
|
const lang = await page.locator('html').getAttribute('lang');
|
|
90
95
|
expect(lang).toBeTruthy();
|
|
91
96
|
});
|
|
92
|
-
});
|
|
97
|
+
});
|