agentic-qe 1.9.3 ā 2.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/.claude/agents/qe-api-contract-validator.md +95 -1336
- package/.claude/agents/qe-chaos-engineer.md +152 -1211
- package/.claude/agents/qe-code-complexity.md +144 -707
- package/.claude/agents/qe-coverage-analyzer.md +147 -743
- package/.claude/agents/qe-deployment-readiness.md +143 -1496
- package/.claude/agents/qe-flaky-test-hunter.md +132 -1529
- package/.claude/agents/qe-fleet-commander.md +12 -12
- package/.claude/agents/qe-performance-tester.md +150 -886
- package/.claude/agents/qe-production-intelligence.md +155 -1396
- package/.claude/agents/qe-quality-analyzer.md +6 -6
- package/.claude/agents/qe-quality-gate.md +151 -648
- package/.claude/agents/qe-regression-risk-analyzer.md +132 -1150
- package/.claude/agents/qe-requirements-validator.md +149 -932
- package/.claude/agents/qe-security-scanner.md +157 -797
- package/.claude/agents/qe-test-data-architect.md +96 -1365
- package/.claude/agents/qe-test-executor.md +8 -8
- package/.claude/agents/qe-test-generator.md +145 -1540
- package/.claude/agents/qe-visual-tester.md +153 -1257
- package/.claude/agents/qx-partner.md +235 -0
- package/.claude/agents/subagents/qe-code-reviewer.md +40 -136
- package/.claude/agents/subagents/qe-coverage-gap-analyzer.md +40 -480
- package/.claude/agents/subagents/qe-data-generator.md +41 -125
- package/.claude/agents/subagents/qe-flaky-investigator.md +55 -411
- package/.claude/agents/subagents/qe-integration-tester.md +53 -141
- package/.claude/agents/subagents/qe-performance-validator.md +54 -130
- package/.claude/agents/subagents/qe-security-auditor.md +56 -114
- package/.claude/agents/subagents/qe-test-data-architect-sub.md +57 -548
- package/.claude/agents/subagents/qe-test-implementer.md +58 -551
- package/.claude/agents/subagents/qe-test-refactorer.md +65 -722
- package/.claude/agents/subagents/qe-test-writer.md +63 -726
- package/.claude/skills/skills-manifest.json +632 -0
- package/.claude/skills/testability-scoring/README.md +71 -0
- package/.claude/skills/testability-scoring/SKILL.md +611 -0
- package/.claude/skills/testability-scoring/resources/templates/config.template.js +84 -0
- package/.claude/skills/testability-scoring/resources/templates/testability-scoring.spec.template.js +532 -0
- package/.claude/skills/testability-scoring/scripts/generate-html-report.js +1007 -0
- package/.claude/skills/testability-scoring/scripts/run-assessment.sh +70 -0
- package/CHANGELOG.md +116 -0
- package/README.md +59 -7
- package/config/.env.otel.example +25 -0
- package/config/OTEL-QUICK-REFERENCE.md +137 -0
- package/config/README-OTEL.md +222 -0
- package/config/alerting-rules.yml +518 -0
- package/config/docker-compose.otel.yml +187 -0
- package/config/grafana/dashboards/agentic-qe-overview.json +286 -0
- package/config/grafana/provisioning/dashboards/dashboards.yml +19 -0
- package/config/grafana/provisioning/datasources/datasources.yml +53 -0
- package/config/otel-collector-config.yaml.example +145 -0
- package/config/prometheus.yml.example +106 -0
- package/dist/agents/QXPartnerAgent.d.ts +139 -0
- package/dist/agents/QXPartnerAgent.d.ts.map +1 -0
- package/dist/agents/QXPartnerAgent.js +769 -0
- package/dist/agents/QXPartnerAgent.js.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +82 -2
- package/dist/agents/index.js.map +1 -1
- package/dist/alerting/AlertManager.d.ts +120 -0
- package/dist/alerting/AlertManager.d.ts.map +1 -0
- package/dist/alerting/AlertManager.js +345 -0
- package/dist/alerting/AlertManager.js.map +1 -0
- package/dist/alerting/FeedbackRouter.d.ts +98 -0
- package/dist/alerting/FeedbackRouter.d.ts.map +1 -0
- package/dist/alerting/FeedbackRouter.js +331 -0
- package/dist/alerting/FeedbackRouter.js.map +1 -0
- package/dist/alerting/StrategyApplicator.d.ts +120 -0
- package/dist/alerting/StrategyApplicator.d.ts.map +1 -0
- package/dist/alerting/StrategyApplicator.js +299 -0
- package/dist/alerting/StrategyApplicator.js.map +1 -0
- package/dist/alerting/index.d.ts +68 -0
- package/dist/alerting/index.d.ts.map +1 -0
- package/dist/alerting/index.js +112 -0
- package/dist/alerting/index.js.map +1 -0
- package/dist/alerting/types.d.ts +118 -0
- package/dist/alerting/types.d.ts.map +1 -0
- package/dist/alerting/types.js +11 -0
- package/dist/alerting/types.js.map +1 -0
- package/dist/cli/commands/debug/agent.d.ts.map +1 -1
- package/dist/cli/commands/debug/agent.js +19 -6
- package/dist/cli/commands/debug/agent.js.map +1 -1
- package/dist/cli/commands/debug/health-check.js +20 -7
- package/dist/cli/commands/debug/health-check.js.map +1 -1
- package/dist/cli/commands/init-claude-md-template.d.ts +1 -0
- package/dist/cli/commands/init-claude-md-template.d.ts.map +1 -1
- package/dist/cli/commands/init-claude-md-template.js +4 -3
- package/dist/cli/commands/init-claude-md-template.js.map +1 -1
- package/dist/cli/commands/workflow/cancel.d.ts.map +1 -1
- package/dist/cli/commands/workflow/cancel.js +4 -3
- package/dist/cli/commands/workflow/cancel.js.map +1 -1
- package/dist/cli/commands/workflow/list.d.ts.map +1 -1
- package/dist/cli/commands/workflow/list.js +4 -3
- package/dist/cli/commands/workflow/list.js.map +1 -1
- package/dist/cli/commands/workflow/pause.d.ts.map +1 -1
- package/dist/cli/commands/workflow/pause.js +4 -3
- package/dist/cli/commands/workflow/pause.js.map +1 -1
- package/dist/cli/init/claude-config.d.ts.map +1 -1
- package/dist/cli/init/claude-config.js +13 -13
- package/dist/cli/init/claude-config.js.map +1 -1
- package/dist/cli/init/claude-md.d.ts.map +1 -1
- package/dist/cli/init/claude-md.js +44 -2
- package/dist/cli/init/claude-md.js.map +1 -1
- package/dist/cli/init/database-init.js +1 -1
- package/dist/cli/init/index.d.ts.map +1 -1
- package/dist/cli/init/index.js +13 -6
- package/dist/cli/init/index.js.map +1 -1
- package/dist/cli/init/skills.d.ts.map +1 -1
- package/dist/cli/init/skills.js +2 -1
- package/dist/cli/init/skills.js.map +1 -1
- package/dist/core/memory/AgentDBIntegration.d.ts +24 -6
- package/dist/core/memory/AgentDBIntegration.d.ts.map +1 -1
- package/dist/core/memory/AgentDBIntegration.js +66 -10
- package/dist/core/memory/AgentDBIntegration.js.map +1 -1
- package/dist/core/memory/IPatternStore.d.ts +209 -0
- package/dist/core/memory/IPatternStore.d.ts.map +1 -0
- package/dist/core/memory/IPatternStore.js +15 -0
- package/dist/core/memory/IPatternStore.js.map +1 -0
- package/dist/core/memory/MigrationTools.d.ts +192 -0
- package/dist/core/memory/MigrationTools.d.ts.map +1 -0
- package/dist/core/memory/MigrationTools.js +615 -0
- package/dist/core/memory/MigrationTools.js.map +1 -0
- package/dist/core/memory/NeuralEnhancement.d.ts +154 -0
- package/dist/core/memory/NeuralEnhancement.d.ts.map +1 -0
- package/dist/core/memory/NeuralEnhancement.js +598 -0
- package/dist/core/memory/NeuralEnhancement.js.map +1 -0
- package/dist/core/memory/PatternStoreFactory.d.ts +143 -0
- package/dist/core/memory/PatternStoreFactory.d.ts.map +1 -0
- package/dist/core/memory/PatternStoreFactory.js +370 -0
- package/dist/core/memory/PatternStoreFactory.js.map +1 -0
- package/dist/core/memory/RealAgentDBAdapter.d.ts +1 -0
- package/dist/core/memory/RealAgentDBAdapter.d.ts.map +1 -1
- package/dist/core/memory/RealAgentDBAdapter.js +28 -20
- package/dist/core/memory/RealAgentDBAdapter.js.map +1 -1
- package/dist/core/memory/RuVectorPatternStore.d.ts +198 -0
- package/dist/core/memory/RuVectorPatternStore.d.ts.map +1 -0
- package/dist/core/memory/RuVectorPatternStore.js +605 -0
- package/dist/core/memory/RuVectorPatternStore.js.map +1 -0
- package/dist/core/memory/SelfHealingMonitor.d.ts +186 -0
- package/dist/core/memory/SelfHealingMonitor.d.ts.map +1 -0
- package/dist/core/memory/SelfHealingMonitor.js +451 -0
- package/dist/core/memory/SelfHealingMonitor.js.map +1 -0
- package/dist/core/memory/SwarmMemoryManager.d.ts +62 -0
- package/dist/core/memory/SwarmMemoryManager.d.ts.map +1 -1
- package/dist/core/memory/SwarmMemoryManager.js +97 -0
- package/dist/core/memory/SwarmMemoryManager.js.map +1 -1
- package/dist/core/memory/UnifiedMemoryCoordinator.d.ts +341 -0
- package/dist/core/memory/UnifiedMemoryCoordinator.d.ts.map +1 -0
- package/dist/core/memory/UnifiedMemoryCoordinator.js +986 -0
- package/dist/core/memory/UnifiedMemoryCoordinator.js.map +1 -0
- package/dist/core/memory/index.d.ts +16 -0
- package/dist/core/memory/index.d.ts.map +1 -1
- package/dist/core/memory/index.js +58 -1
- package/dist/core/memory/index.js.map +1 -1
- package/dist/core/optimization/SwarmOptimizer.d.ts +185 -0
- package/dist/core/optimization/SwarmOptimizer.d.ts.map +1 -0
- package/dist/core/optimization/SwarmOptimizer.js +631 -0
- package/dist/core/optimization/SwarmOptimizer.js.map +1 -0
- package/dist/core/optimization/index.d.ts +9 -0
- package/dist/core/optimization/index.d.ts.map +1 -0
- package/dist/core/optimization/index.js +25 -0
- package/dist/core/optimization/index.js.map +1 -0
- package/dist/core/optimization/types.d.ts +53 -0
- package/dist/core/optimization/types.d.ts.map +1 -0
- package/dist/core/optimization/types.js +6 -0
- package/dist/core/optimization/types.js.map +1 -0
- package/dist/core/orchestration/PriorityQueue.d.ts +54 -0
- package/dist/core/orchestration/PriorityQueue.d.ts.map +1 -0
- package/dist/core/orchestration/PriorityQueue.js +122 -0
- package/dist/core/orchestration/PriorityQueue.js.map +1 -0
- package/dist/core/orchestration/WorkflowOrchestrator.d.ts +176 -0
- package/dist/core/orchestration/WorkflowOrchestrator.d.ts.map +1 -0
- package/dist/core/orchestration/WorkflowOrchestrator.js +813 -0
- package/dist/core/orchestration/WorkflowOrchestrator.js.map +1 -0
- package/dist/core/orchestration/index.d.ts +7 -0
- package/dist/core/orchestration/index.d.ts.map +1 -0
- package/dist/core/orchestration/index.js +11 -0
- package/dist/core/orchestration/index.js.map +1 -0
- package/dist/core/orchestration/types.d.ts +96 -0
- package/dist/core/orchestration/types.d.ts.map +1 -0
- package/dist/core/orchestration/types.js +6 -0
- package/dist/core/orchestration/types.js.map +1 -0
- package/dist/core/skills/DynamicSkillLoader.d.ts +96 -0
- package/dist/core/skills/DynamicSkillLoader.d.ts.map +1 -0
- package/dist/core/skills/DynamicSkillLoader.js +353 -0
- package/dist/core/skills/DynamicSkillLoader.js.map +1 -0
- package/dist/core/skills/types.d.ts +118 -0
- package/dist/core/skills/types.d.ts.map +1 -0
- package/dist/core/skills/types.js +7 -0
- package/dist/core/skills/types.js.map +1 -0
- package/dist/core/transport/QUICTransport.d.ts +320 -0
- package/dist/core/transport/QUICTransport.d.ts.map +1 -0
- package/dist/core/transport/QUICTransport.js +711 -0
- package/dist/core/transport/QUICTransport.js.map +1 -0
- package/dist/core/transport/index.d.ts +40 -0
- package/dist/core/transport/index.d.ts.map +1 -0
- package/dist/core/transport/index.js +46 -0
- package/dist/core/transport/index.js.map +1 -0
- package/dist/core/transport/quic-loader.d.ts +123 -0
- package/dist/core/transport/quic-loader.d.ts.map +1 -0
- package/dist/core/transport/quic-loader.js +293 -0
- package/dist/core/transport/quic-loader.js.map +1 -0
- package/dist/core/transport/quic.d.ts +154 -0
- package/dist/core/transport/quic.d.ts.map +1 -0
- package/dist/core/transport/quic.js +214 -0
- package/dist/core/transport/quic.js.map +1 -0
- package/dist/mcp/services/AgentRegistry.d.ts.map +1 -1
- package/dist/mcp/services/AgentRegistry.js +4 -1
- package/dist/mcp/services/AgentRegistry.js.map +1 -1
- package/dist/reasoning/RuVectorReasoningAdapter.d.ts +232 -0
- package/dist/reasoning/RuVectorReasoningAdapter.d.ts.map +1 -0
- package/dist/reasoning/RuVectorReasoningAdapter.js +585 -0
- package/dist/reasoning/RuVectorReasoningAdapter.js.map +1 -0
- package/dist/reasoning/index.d.ts +2 -0
- package/dist/reasoning/index.d.ts.map +1 -1
- package/dist/reasoning/index.js +6 -1
- package/dist/reasoning/index.js.map +1 -1
- package/dist/reporting/ResultAggregator.d.ts +107 -0
- package/dist/reporting/ResultAggregator.d.ts.map +1 -0
- package/dist/reporting/ResultAggregator.js +435 -0
- package/dist/reporting/ResultAggregator.js.map +1 -0
- package/dist/reporting/index.d.ts +48 -0
- package/dist/reporting/index.d.ts.map +1 -0
- package/dist/reporting/index.js +154 -0
- package/dist/reporting/index.js.map +1 -0
- package/dist/reporting/reporters/ControlLoopReporter.d.ts +128 -0
- package/dist/reporting/reporters/ControlLoopReporter.d.ts.map +1 -0
- package/dist/reporting/reporters/ControlLoopReporter.js +417 -0
- package/dist/reporting/reporters/ControlLoopReporter.js.map +1 -0
- package/dist/reporting/reporters/HumanReadableReporter.d.ts +140 -0
- package/dist/reporting/reporters/HumanReadableReporter.d.ts.map +1 -0
- package/dist/reporting/reporters/HumanReadableReporter.js +524 -0
- package/dist/reporting/reporters/HumanReadableReporter.js.map +1 -0
- package/dist/reporting/reporters/JSONReporter.d.ts +193 -0
- package/dist/reporting/reporters/JSONReporter.d.ts.map +1 -0
- package/dist/reporting/reporters/JSONReporter.js +324 -0
- package/dist/reporting/reporters/JSONReporter.js.map +1 -0
- package/dist/reporting/reporters/index.d.ts +14 -0
- package/dist/reporting/reporters/index.d.ts.map +1 -0
- package/dist/reporting/reporters/index.js +19 -0
- package/dist/reporting/reporters/index.js.map +1 -0
- package/dist/reporting/types.d.ts +427 -0
- package/dist/reporting/types.d.ts.map +1 -0
- package/dist/reporting/types.js +12 -0
- package/dist/reporting/types.js.map +1 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/qx.d.ts +397 -0
- package/dist/types/qx.d.ts.map +1 -0
- package/dist/types/qx.js +71 -0
- package/dist/types/qx.js.map +1 -0
- package/dist/visualization/api/RestEndpoints.js +1 -1
- package/dist/visualization/api/RestEndpoints.js.map +1 -1
- package/dist/visualization/api/WebSocketServer.d.ts +44 -0
- package/dist/visualization/api/WebSocketServer.d.ts.map +1 -1
- package/dist/visualization/api/WebSocketServer.js +144 -23
- package/dist/visualization/api/WebSocketServer.js.map +1 -1
- package/dist/visualization/core/DataTransformer.d.ts +10 -0
- package/dist/visualization/core/DataTransformer.d.ts.map +1 -1
- package/dist/visualization/core/DataTransformer.js +60 -5
- package/dist/visualization/core/DataTransformer.js.map +1 -1
- package/dist/visualization/emit-event.d.ts +75 -0
- package/dist/visualization/emit-event.d.ts.map +1 -0
- package/dist/visualization/emit-event.js +213 -0
- package/dist/visualization/emit-event.js.map +1 -0
- package/dist/visualization/index.d.ts +1 -0
- package/dist/visualization/index.d.ts.map +1 -1
- package/dist/visualization/index.js +7 -1
- package/dist/visualization/index.js.map +1 -1
- package/docs/reference/skills.md +63 -1
- package/package.json +12 -4
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Testability Scorer Configuration Template
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
// Application under test
|
|
5
|
+
baseURL: 'https://www.saucedemo.com',
|
|
6
|
+
|
|
7
|
+
// Scoring weights (must sum to 100)
|
|
8
|
+
weights: {
|
|
9
|
+
observability: 15, // Console logs, state visibility, monitoring
|
|
10
|
+
controllability: 15, // API access, state manipulation, test data injection
|
|
11
|
+
algorithmicSimplicity: 10, // Clear input-output relationships, low complexity
|
|
12
|
+
algorithmicTransparency: 10, // Understandable logic, clear data flow
|
|
13
|
+
explainability: 10, // Documentation, comments, error messages
|
|
14
|
+
similarity: 5, // Standard patterns, familiar architecture
|
|
15
|
+
algorithmicStability: 10, // API versioning, backward compatibility
|
|
16
|
+
unbugginess: 10, // Low defect rate, test reliability
|
|
17
|
+
smallness: 10, // Manageable size, modular design
|
|
18
|
+
decomposability: 5 // Component isolation, test unit granularity
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// Grading scale
|
|
22
|
+
grades: {
|
|
23
|
+
A: 90, // Excellent
|
|
24
|
+
B: 80, // Good
|
|
25
|
+
C: 70, // Acceptable
|
|
26
|
+
D: 60, // Below average
|
|
27
|
+
F: 0 // Poor
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
// Report settings
|
|
31
|
+
reports: {
|
|
32
|
+
format: ['html', 'json', 'text'], // Output formats
|
|
33
|
+
directory: 'tests/reports', // Report location
|
|
34
|
+
autoOpen: true, // Open HTML report automatically
|
|
35
|
+
includeAI: true, // AI-powered recommendations
|
|
36
|
+
includeCharts: true, // Visual charts
|
|
37
|
+
includeHistory: true // Historical comparison
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// User types for comparative analysis (optional)
|
|
41
|
+
userTypes: [
|
|
42
|
+
{ username: 'standard_user', password: 'secret_sauce', role: 'standard' },
|
|
43
|
+
{ username: 'locked_out_user', password: 'secret_sauce', role: 'locked' },
|
|
44
|
+
{ username: 'problem_user', password: 'secret_sauce', role: 'problem' },
|
|
45
|
+
{ username: 'performance_glitch_user', password: 'secret_sauce', role: 'performance' },
|
|
46
|
+
{ username: 'error_user', password: 'secret_sauce', role: 'error' },
|
|
47
|
+
{ username: 'visual_user', password: 'secret_sauce', role: 'visual' }
|
|
48
|
+
],
|
|
49
|
+
|
|
50
|
+
// Browser configuration
|
|
51
|
+
browsers: ['chromium', 'firefox', 'webkit'],
|
|
52
|
+
|
|
53
|
+
// Timeouts
|
|
54
|
+
timeouts: {
|
|
55
|
+
test: 30000, // Per test timeout
|
|
56
|
+
navigation: 10000, // Page navigation
|
|
57
|
+
action: 5000 // UI action timeout
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// Thresholds for pass/fail
|
|
61
|
+
thresholds: {
|
|
62
|
+
overall: 70, // Minimum overall score
|
|
63
|
+
critical: { // Critical principles must meet these minimums
|
|
64
|
+
observability: 60,
|
|
65
|
+
controllability: 60,
|
|
66
|
+
unbugginess: 70
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// AI recommendations settings
|
|
71
|
+
ai: {
|
|
72
|
+
enabled: true,
|
|
73
|
+
provider: 'local', // 'local' or 'api'
|
|
74
|
+
prioritize: 'impact', // 'impact', 'effort', or 'score'
|
|
75
|
+
maxRecommendations: 10
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// Historical tracking
|
|
79
|
+
history: {
|
|
80
|
+
enabled: true,
|
|
81
|
+
directory: '.testability-history',
|
|
82
|
+
retention: 90 // days
|
|
83
|
+
}
|
|
84
|
+
};
|
package/.claude/skills/testability-scoring/resources/templates/testability-scoring.spec.template.js
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
const { test, expect } = require('@playwright/test');
|
|
2
|
+
const config = require('./config');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
let testabilityScores = {
|
|
7
|
+
timestamp: new Date().toISOString(),
|
|
8
|
+
overall: 0,
|
|
9
|
+
grade: 'F',
|
|
10
|
+
principles: {},
|
|
11
|
+
recommendations: [],
|
|
12
|
+
metadata: {
|
|
13
|
+
url: config.baseURL,
|
|
14
|
+
browser: 'chromium',
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
assessor: 'testability-scorer-skill'
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Initialize all principles with default values to ensure we always have data
|
|
21
|
+
function initializeDefaultScores() {
|
|
22
|
+
const defaultScore = { score: 50, grade: 'F', weight: 0 };
|
|
23
|
+
testabilityScores.principles = {
|
|
24
|
+
observability: { ...defaultScore, weight: config.weights.observability },
|
|
25
|
+
controllability: { ...defaultScore, weight: config.weights.controllability },
|
|
26
|
+
algorithmicSimplicity: { ...defaultScore, weight: config.weights.algorithmicSimplicity },
|
|
27
|
+
algorithmicTransparency: { ...defaultScore, weight: config.weights.algorithmicTransparency },
|
|
28
|
+
explainability: { ...defaultScore, weight: config.weights.explainability },
|
|
29
|
+
similarity: { ...defaultScore, weight: config.weights.similarity },
|
|
30
|
+
algorithmicStability: { ...defaultScore, weight: config.weights.algorithmicStability },
|
|
31
|
+
unbugginess: { ...defaultScore, weight: config.weights.unbugginess },
|
|
32
|
+
smallness: { ...defaultScore, weight: config.weights.smallness },
|
|
33
|
+
decomposability: { ...defaultScore, weight: config.weights.decomposability }
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Robust page navigation helper
|
|
38
|
+
async function navigateToPage(page) {
|
|
39
|
+
console.log(`[NAV] Starting navigation to ${config.baseURL}`);
|
|
40
|
+
page.setDefaultTimeout(45000);
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
console.log('[NAV] Attempting page.goto with domcontentloaded...');
|
|
44
|
+
await page.goto(config.baseURL, {
|
|
45
|
+
timeout: 45000,
|
|
46
|
+
waitUntil: 'domcontentloaded'
|
|
47
|
+
});
|
|
48
|
+
console.log('[NAV] Page loaded (domcontentloaded)');
|
|
49
|
+
|
|
50
|
+
// Try to wait for network idle but don't fail if it times out
|
|
51
|
+
console.log('[NAV] Waiting for networkidle (15s timeout)...');
|
|
52
|
+
await page.waitForLoadState('networkidle', { timeout: 15000 })
|
|
53
|
+
.then(() => console.log('[NAV] Network is idle'))
|
|
54
|
+
.catch(() => console.log('[NAV] Network not idle after 15s, continuing...'));
|
|
55
|
+
|
|
56
|
+
return true;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.log(`[NAV] Navigation failed: ${error.message}`);
|
|
59
|
+
// Try one more time with even more lenient settings
|
|
60
|
+
try {
|
|
61
|
+
console.log('[NAV] Retrying with commit waitUntil...');
|
|
62
|
+
await page.goto(config.baseURL, {
|
|
63
|
+
timeout: 45000,
|
|
64
|
+
waitUntil: 'commit'
|
|
65
|
+
});
|
|
66
|
+
console.log('[NAV] Page committed');
|
|
67
|
+
return true;
|
|
68
|
+
} catch (retryError) {
|
|
69
|
+
console.error(`[NAV] Final navigation failed: ${retryError.message}`);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
test.describe.configure({ mode: 'serial', timeout: 60000 });
|
|
76
|
+
|
|
77
|
+
test.describe('Comprehensive Testability Analysis - Sauce Demo Shopify', () => {
|
|
78
|
+
|
|
79
|
+
test.beforeAll(() => {
|
|
80
|
+
console.log('Starting testability assessment...');
|
|
81
|
+
initializeDefaultScores();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('1. Observability Assessment', async ({ page }) => {
|
|
85
|
+
try {
|
|
86
|
+
const logs = [];
|
|
87
|
+
const errors = [];
|
|
88
|
+
const networkRequests = [];
|
|
89
|
+
|
|
90
|
+
page.on('console', msg => logs.push(msg));
|
|
91
|
+
page.on('pageerror', err => errors.push(err));
|
|
92
|
+
page.on('request', request => networkRequests.push(request));
|
|
93
|
+
|
|
94
|
+
const loaded = await navigateToPage(page);
|
|
95
|
+
if (!loaded) {
|
|
96
|
+
throw new Error('Failed to load page');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Check console logging
|
|
100
|
+
const hasConsoleLogs = logs.length > 0;
|
|
101
|
+
|
|
102
|
+
// Check network visibility
|
|
103
|
+
const hasNetworkRequests = networkRequests.length > 0;
|
|
104
|
+
|
|
105
|
+
// Check state inspection
|
|
106
|
+
const stateVisible = await page.evaluate(() => {
|
|
107
|
+
return typeof window !== 'undefined' &&
|
|
108
|
+
(typeof window.Shopify !== 'undefined' ||
|
|
109
|
+
typeof window.console !== 'undefined');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Calculate score
|
|
113
|
+
let score = 0;
|
|
114
|
+
if (hasConsoleLogs) score += 25;
|
|
115
|
+
if (hasNetworkRequests) score += 30;
|
|
116
|
+
if (stateVisible) score += 27;
|
|
117
|
+
score += 10; // Base score for page loading
|
|
118
|
+
|
|
119
|
+
testabilityScores.principles.observability = {
|
|
120
|
+
score: Math.min(score, 100),
|
|
121
|
+
grade: getLetterGrade(score),
|
|
122
|
+
weight: config.weights.observability
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
if (score < 70) {
|
|
126
|
+
testabilityScores.recommendations.push({
|
|
127
|
+
principle: 'Observability',
|
|
128
|
+
severity: 'medium',
|
|
129
|
+
recommendation: 'Implement detailed event logging for user actions, cart operations, and payment processing.',
|
|
130
|
+
impact: 15,
|
|
131
|
+
effort: 'Low (4-6 hours)'
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('Observability assessment failed:', error.message);
|
|
136
|
+
testabilityScores.principles.observability = { score: 50, grade: 'F', weight: config.weights.observability };
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('2. Controllability Assessment', async ({ page }) => {
|
|
141
|
+
try {
|
|
142
|
+
const loaded = await navigateToPage(page);
|
|
143
|
+
if (!loaded) throw new Error('Failed to load page');
|
|
144
|
+
|
|
145
|
+
// Check direct API access
|
|
146
|
+
const hasAPI = await page.evaluate(() => {
|
|
147
|
+
return typeof window.Shopify !== 'undefined' &&
|
|
148
|
+
typeof window.Shopify.checkout !== 'undefined';
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Check state manipulation
|
|
152
|
+
const canManipulateCart = await page.evaluate(() => {
|
|
153
|
+
return typeof window.Shopify !== 'undefined';
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Check test data injection capabilities
|
|
157
|
+
const hasTestMode = await page.evaluate(() => {
|
|
158
|
+
return typeof window.testAPI !== 'undefined' ||
|
|
159
|
+
typeof window.Shopify !== 'undefined';
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
let score = 0;
|
|
163
|
+
if (hasAPI) score += 25;
|
|
164
|
+
if (canManipulateCart) score += 20;
|
|
165
|
+
if (hasTestMode) score += 10;
|
|
166
|
+
|
|
167
|
+
// E-commerce sites typically have limited controllability for security
|
|
168
|
+
score = Math.min(score + 20, 65); // Cap at 65 for production e-commerce
|
|
169
|
+
|
|
170
|
+
testabilityScores.principles.controllability = {
|
|
171
|
+
score,
|
|
172
|
+
grade: getLetterGrade(score),
|
|
173
|
+
weight: config.weights.controllability
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
if (score < 70) {
|
|
177
|
+
testabilityScores.recommendations.push({
|
|
178
|
+
principle: 'Controllability',
|
|
179
|
+
severity: 'critical',
|
|
180
|
+
recommendation: 'Add test data injection endpoints to allow programmatic product catalog and cart management during automated testing.',
|
|
181
|
+
impact: 38,
|
|
182
|
+
effort: 'Medium (8-12 hours)'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error('Controllability assessment failed:', error.message);
|
|
187
|
+
testabilityScores.principles.controllability = { score: 50, grade: 'F', weight: config.weights.controllability };
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('3. Algorithmic Simplicity Assessment', async ({ page }) => {
|
|
192
|
+
try {
|
|
193
|
+
const loaded = await navigateToPage(page);
|
|
194
|
+
if (!loaded) throw new Error('Failed to load page');
|
|
195
|
+
|
|
196
|
+
// Measure complexity through interaction patterns
|
|
197
|
+
const interactions = [];
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
// Test product browsing
|
|
201
|
+
const products = await page.locator('[data-product], .product, .product-item').count();
|
|
202
|
+
interactions.push({ action: 'browse', steps: products > 0 ? 2 : 5 });
|
|
203
|
+
|
|
204
|
+
// Test cart interaction
|
|
205
|
+
const cartExists = await page.locator('[data-cart], .cart, #cart').count() > 0;
|
|
206
|
+
interactions.push({ action: 'cart', steps: cartExists ? 2 : 4 });
|
|
207
|
+
|
|
208
|
+
// Test checkout flow
|
|
209
|
+
const checkoutExists = await page.locator('[href*="checkout"], .checkout-button').count() > 0;
|
|
210
|
+
interactions.push({ action: 'checkout', steps: checkoutExists ? 3 : 6 });
|
|
211
|
+
} catch (e) {
|
|
212
|
+
interactions.push({ action: 'default', steps: 5 });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const avgComplexity = interactions.reduce((sum, i) => sum + i.steps, 0) / interactions.length;
|
|
216
|
+
|
|
217
|
+
// Shopify is well-structured, so base score is high
|
|
218
|
+
const score = Math.max(70, Math.min(100 - (avgComplexity * 5), 95));
|
|
219
|
+
|
|
220
|
+
testabilityScores.principles.algorithmicSimplicity = {
|
|
221
|
+
score: Math.round(score),
|
|
222
|
+
grade: getLetterGrade(score),
|
|
223
|
+
weight: config.weights.algorithmicSimplicity
|
|
224
|
+
};
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error('Algorithmic Simplicity assessment failed:', error.message);
|
|
227
|
+
testabilityScores.principles.algorithmicSimplicity = { score: 50, grade: 'F', weight: config.weights.algorithmicSimplicity };
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('4. Algorithmic Transparency Assessment', async ({ page }) => {
|
|
232
|
+
try {
|
|
233
|
+
const loaded = await navigateToPage(page);
|
|
234
|
+
if (!loaded) throw new Error('Failed to load page');
|
|
235
|
+
|
|
236
|
+
// Check code readability indicators
|
|
237
|
+
const hasReadableClasses = await page.evaluate(() => {
|
|
238
|
+
const elements = document.querySelectorAll('[class]');
|
|
239
|
+
let readableCount = 0;
|
|
240
|
+
elements.forEach(el => {
|
|
241
|
+
const classes = el.className.toString();
|
|
242
|
+
if (classes.includes('product') || classes.includes('cart') ||
|
|
243
|
+
classes.includes('checkout') || classes.includes('price')) {
|
|
244
|
+
readableCount++;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return readableCount > 10;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Check data attributes
|
|
251
|
+
const hasDataAttributes = await page.evaluate(() => {
|
|
252
|
+
return document.querySelectorAll('[data-product], [data-cart], [data-price]').length > 0;
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
let score = 60; // Base score for Shopify structure
|
|
256
|
+
if (hasReadableClasses) score += 10;
|
|
257
|
+
if (hasDataAttributes) score += 10;
|
|
258
|
+
|
|
259
|
+
testabilityScores.principles.algorithmicTransparency = {
|
|
260
|
+
score: Math.min(score, 100),
|
|
261
|
+
grade: getLetterGrade(score),
|
|
262
|
+
weight: config.weights.algorithmicTransparency
|
|
263
|
+
};
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.error('Algorithmic Transparency assessment failed:', error.message);
|
|
266
|
+
testabilityScores.principles.algorithmicTransparency = { score: 50, grade: 'F', weight: config.weights.algorithmicTransparency };
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test('5. Explainability Assessment', async ({ page }) => {
|
|
271
|
+
try {
|
|
272
|
+
const loaded = await navigateToPage(page);
|
|
273
|
+
if (!loaded) throw new Error('Failed to load page');
|
|
274
|
+
|
|
275
|
+
// Check for help text and documentation
|
|
276
|
+
const hasHelpText = await page.locator('[aria-label], [title], .help-text').count() > 0;
|
|
277
|
+
|
|
278
|
+
// Check for clear error messages
|
|
279
|
+
const hasErrorHandling = await page.evaluate(() => {
|
|
280
|
+
return document.querySelectorAll('[role="alert"], .error, .message').length >= 0;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Check for tooltips and guidance
|
|
284
|
+
const hasGuidance = await page.locator('[data-tooltip], .tooltip').count() > 0;
|
|
285
|
+
|
|
286
|
+
let score = 50; // Base score
|
|
287
|
+
if (hasHelpText) score += 15;
|
|
288
|
+
if (hasErrorHandling) score += 10;
|
|
289
|
+
if (hasGuidance) score += 7;
|
|
290
|
+
|
|
291
|
+
testabilityScores.principles.explainability = {
|
|
292
|
+
score: Math.min(score, 100),
|
|
293
|
+
grade: getLetterGrade(score),
|
|
294
|
+
weight: config.weights.explainability
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
if (score < 70) {
|
|
298
|
+
testabilityScores.recommendations.push({
|
|
299
|
+
principle: 'Explainability',
|
|
300
|
+
severity: 'medium',
|
|
301
|
+
recommendation: 'Document API contracts, add OpenAPI specifications, and improve error message clarity for checkout process.',
|
|
302
|
+
impact: 25,
|
|
303
|
+
effort: 'Medium (6-8 hours)'
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
} catch (error) {
|
|
307
|
+
console.error('Explainability assessment failed:', error.message);
|
|
308
|
+
testabilityScores.principles.explainability = { score: 50, grade: 'F', weight: config.weights.explainability };
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test('6. Similarity to Known Technology Assessment', async ({ page }) => {
|
|
313
|
+
try {
|
|
314
|
+
const loaded = await navigateToPage(page);
|
|
315
|
+
if (!loaded) throw new Error('Failed to load page');
|
|
316
|
+
|
|
317
|
+
// Shopify is a well-known platform
|
|
318
|
+
const usesShopify = await page.evaluate(() => {
|
|
319
|
+
return typeof window.Shopify !== 'undefined' ||
|
|
320
|
+
document.documentElement.innerHTML.includes('Shopify');
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Check for standard frameworks
|
|
324
|
+
const usesStandardFrameworks = await page.evaluate(() => {
|
|
325
|
+
return typeof window.jQuery !== 'undefined' ||
|
|
326
|
+
document.querySelector('[data-react-root]') !== null ||
|
|
327
|
+
typeof window.Vue !== 'undefined';
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
let score = 85; // High base score for Shopify
|
|
331
|
+
if (usesShopify) score += 10;
|
|
332
|
+
if (usesStandardFrameworks) score += 5;
|
|
333
|
+
|
|
334
|
+
testabilityScores.principles.similarity = {
|
|
335
|
+
score: Math.round(score),
|
|
336
|
+
grade: getLetterGrade(score),
|
|
337
|
+
weight: config.weights.similarity
|
|
338
|
+
};
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error('Similarity assessment failed:', error.message);
|
|
341
|
+
testabilityScores.principles.similarity = { score: 50, grade: 'F', weight: config.weights.similarity };
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test('7. Algorithmic Stability Assessment', async ({ page }) => {
|
|
346
|
+
try {
|
|
347
|
+
const loaded = await navigateToPage(page);
|
|
348
|
+
if (!loaded) throw new Error('Failed to load page');
|
|
349
|
+
|
|
350
|
+
// Check for versioning
|
|
351
|
+
const hasVersioning = await page.evaluate(() => {
|
|
352
|
+
return typeof window.Shopify !== 'undefined';
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Check page consistency
|
|
356
|
+
const pageLoadsConsistently = await page.evaluate(() => {
|
|
357
|
+
return document.readyState === 'complete';
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
let score = 60; // Base score
|
|
361
|
+
if (hasVersioning) score += 15;
|
|
362
|
+
if (pageLoadsConsistently) score += 10;
|
|
363
|
+
|
|
364
|
+
testabilityScores.principles.algorithmicStability = {
|
|
365
|
+
score: Math.min(score, 100),
|
|
366
|
+
grade: getLetterGrade(score),
|
|
367
|
+
weight: config.weights.algorithmicStability
|
|
368
|
+
};
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error('Algorithmic Stability assessment failed:', error.message);
|
|
371
|
+
testabilityScores.principles.algorithmicStability = { score: 50, grade: 'F', weight: config.weights.algorithmicStability };
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test('8. Unbugginess Assessment', async ({ page }) => {
|
|
376
|
+
try {
|
|
377
|
+
const errors = [];
|
|
378
|
+
const warnings = [];
|
|
379
|
+
|
|
380
|
+
page.on('console', msg => {
|
|
381
|
+
if (msg.type() === 'error') errors.push(msg);
|
|
382
|
+
if (msg.type() === 'warning') warnings.push(msg);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
page.on('pageerror', err => errors.push(err));
|
|
386
|
+
|
|
387
|
+
const loaded = await navigateToPage(page);
|
|
388
|
+
if (!loaded) throw new Error('Failed to load page');
|
|
389
|
+
|
|
390
|
+
// Score based on errors
|
|
391
|
+
let score = 95; // Start high
|
|
392
|
+
score -= errors.length * 5;
|
|
393
|
+
score -= warnings.length * 2;
|
|
394
|
+
score = Math.max(score, 0);
|
|
395
|
+
|
|
396
|
+
testabilityScores.principles.unbugginess = {
|
|
397
|
+
score: Math.min(score, 100),
|
|
398
|
+
grade: getLetterGrade(score),
|
|
399
|
+
weight: config.weights.unbugginess
|
|
400
|
+
};
|
|
401
|
+
} catch (error) {
|
|
402
|
+
console.error('Unbugginess assessment failed:', error.message);
|
|
403
|
+
testabilityScores.principles.unbugginess = { score: 50, grade: 'F', weight: config.weights.unbugginess };
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test('9. Smallness Assessment', async ({ page }) => {
|
|
408
|
+
try {
|
|
409
|
+
const loaded = await navigateToPage(page);
|
|
410
|
+
if (!loaded) throw new Error('Failed to load page');
|
|
411
|
+
|
|
412
|
+
// Measure page size indicators
|
|
413
|
+
const elementCount = await page.evaluate(() => document.querySelectorAll('*').length);
|
|
414
|
+
const scriptCount = await page.evaluate(() => document.querySelectorAll('script').length);
|
|
415
|
+
const styleCount = await page.evaluate(() => document.querySelectorAll('style, link[rel="stylesheet"]').length);
|
|
416
|
+
|
|
417
|
+
// Smaller is better
|
|
418
|
+
let score = 100;
|
|
419
|
+
if (elementCount > 1000) score -= 10;
|
|
420
|
+
if (elementCount > 2000) score -= 10;
|
|
421
|
+
if (scriptCount > 20) score -= 5;
|
|
422
|
+
if (styleCount > 10) score -= 5;
|
|
423
|
+
|
|
424
|
+
testabilityScores.principles.smallness = {
|
|
425
|
+
score: Math.min(score, 100),
|
|
426
|
+
grade: getLetterGrade(score),
|
|
427
|
+
weight: config.weights.smallness
|
|
428
|
+
};
|
|
429
|
+
} catch (error) {
|
|
430
|
+
console.error('Smallness assessment failed:', error.message);
|
|
431
|
+
testabilityScores.principles.smallness = { score: 50, grade: 'F', weight: config.weights.smallness };
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
test('10. Decomposability Assessment', async ({ page }) => {
|
|
436
|
+
try {
|
|
437
|
+
const loaded = await navigateToPage(page);
|
|
438
|
+
if (!loaded) throw new Error('Failed to load page');
|
|
439
|
+
|
|
440
|
+
// Check for modular components
|
|
441
|
+
const hasModularStructure = await page.evaluate(() => {
|
|
442
|
+
const hasComponents = document.querySelectorAll('[data-component], [data-module], .component, .module').length > 0;
|
|
443
|
+
const hasSections = document.querySelectorAll('section, [role="region"]').length > 0;
|
|
444
|
+
return hasComponents || hasSections;
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Check for isolated features
|
|
448
|
+
const hasIsolatedFeatures = await page.evaluate(() => {
|
|
449
|
+
const hasCart = document.querySelector('[data-cart], .cart') !== null;
|
|
450
|
+
const hasProduct = document.querySelector('[data-product], .product') !== null;
|
|
451
|
+
return hasCart && hasProduct;
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
let score = 50; // Base score
|
|
455
|
+
if (hasModularStructure) score += 20;
|
|
456
|
+
if (hasIsolatedFeatures) score += 15;
|
|
457
|
+
|
|
458
|
+
testabilityScores.principles.decomposability = {
|
|
459
|
+
score: Math.min(score, 100),
|
|
460
|
+
grade: getLetterGrade(score),
|
|
461
|
+
weight: config.weights.decomposability
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
if (score < 70) {
|
|
465
|
+
testabilityScores.recommendations.push({
|
|
466
|
+
principle: 'Decomposability',
|
|
467
|
+
severity: 'high',
|
|
468
|
+
recommendation: 'Extract product catalog, shopping cart, and checkout into separate microservices for better isolation and testability.',
|
|
469
|
+
impact: 30,
|
|
470
|
+
effort: 'High (16-24 hours)'
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
} catch (error) {
|
|
474
|
+
console.error('Decomposability assessment failed:', error.message);
|
|
475
|
+
testabilityScores.principles.decomposability = { score: 50, grade: 'F', weight: config.weights.decomposability };
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
test.afterAll('Calculate Overall Score & Generate Report', async () => {
|
|
480
|
+
// Calculate weighted average
|
|
481
|
+
const principles = testabilityScores.principles;
|
|
482
|
+
const weights = config.weights;
|
|
483
|
+
|
|
484
|
+
let totalScore = 0;
|
|
485
|
+
Object.keys(principles).forEach(key => {
|
|
486
|
+
const weight = weights[key] / 100;
|
|
487
|
+
totalScore += principles[key].score * weight;
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
testabilityScores.overall = Math.round(totalScore);
|
|
491
|
+
testabilityScores.grade = getLetterGrade(testabilityScores.overall);
|
|
492
|
+
testabilityScores.metadata.duration = Date.now() - new Date(testabilityScores.timestamp).getTime();
|
|
493
|
+
|
|
494
|
+
// Sort recommendations by impact
|
|
495
|
+
testabilityScores.recommendations.sort((a, b) => b.impact - a.impact);
|
|
496
|
+
|
|
497
|
+
// Save JSON report
|
|
498
|
+
const timestamp = Date.now();
|
|
499
|
+
const jsonPath = path.join(config.reports.directory, `testability-results-${timestamp}.json`);
|
|
500
|
+
const htmlPath = path.join(config.reports.directory, `testability-report-${timestamp}.html`);
|
|
501
|
+
|
|
502
|
+
fs.writeFileSync(jsonPath, JSON.stringify(testabilityScores, null, 2));
|
|
503
|
+
fs.writeFileSync(path.join(config.reports.directory, 'latest.json'), JSON.stringify(testabilityScores, null, 2));
|
|
504
|
+
|
|
505
|
+
console.log(`\nā
Assessment Complete!`);
|
|
506
|
+
console.log(`\nš Overall Testability Score: ${testabilityScores.overall}/100 (${testabilityScores.grade})`);
|
|
507
|
+
console.log(`\nš JSON Report saved: ${jsonPath}`);
|
|
508
|
+
console.log(`\nšÆ Generating HTML report with Chrome auto-launch...`);
|
|
509
|
+
|
|
510
|
+
// Generate HTML report using the enhanced script
|
|
511
|
+
const { exec } = require('child_process');
|
|
512
|
+
exec(`node .claude/skills/testability-scorer/scripts/generate-html-report.js "${jsonPath}" "${htmlPath}"`,
|
|
513
|
+
(error, stdout, stderr) => {
|
|
514
|
+
if (error) {
|
|
515
|
+
console.error(`Error generating HTML: ${error.message}`);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (stderr) {
|
|
519
|
+
console.error(`stderr: ${stderr}`);
|
|
520
|
+
}
|
|
521
|
+
console.log(stdout);
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
function getLetterGrade(score) {
|
|
527
|
+
if (score >= 90) return 'A';
|
|
528
|
+
if (score >= 80) return 'B';
|
|
529
|
+
if (score >= 70) return 'C';
|
|
530
|
+
if (score >= 60) return 'D';
|
|
531
|
+
return 'F';
|
|
532
|
+
}
|