create-qa-architect 5.0.0 โ 5.0.6
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/.github/RELEASE_CHECKLIST.md +2 -4
- package/.github/workflows/daily-deploy-check.yml +136 -0
- package/.github/workflows/nightly-gitleaks-verification.yml +1 -1
- package/.github/workflows/quality.yml +3 -0
- package/.github/workflows/release.yml +12 -10
- package/.github/workflows/weekly-audit.yml +173 -0
- package/LICENSE +66 -0
- package/README.md +44 -30
- package/config/defaults.js +22 -1
- package/config/quality-config.schema.json +1 -1
- package/create-saas-monetization.js +75 -27
- package/docs/ARCHITECTURE.md +53 -0
- package/docs/DEPLOYMENT.md +62 -0
- package/docs/PREFLIGHT_REPORT.md +108 -0
- package/docs/SLA_GATES.md +28 -0
- package/docs/TESTING.md +61 -0
- package/docs/security/SOC2_STARTER.md +29 -0
- package/lib/config-validator.js +8 -2
- package/lib/dependency-monitoring-basic.js +73 -26
- package/lib/dependency-monitoring-premium.js +21 -19
- package/lib/github-api.js +249 -0
- package/lib/interactive/questions.js +4 -0
- package/lib/license-validator.js +1 -1
- package/lib/licensing.js +11 -10
- package/lib/package-utils.js +224 -8
- package/lib/project-maturity.js +1 -1
- package/lib/setup-enhancements.js +33 -0
- package/lib/template-loader.js +2 -0
- package/lib/ui-helpers.js +2 -1
- package/lib/validation/base-validator.js +5 -1
- package/lib/validation/cache-manager.js +1 -0
- package/lib/validation/config-security.js +5 -4
- package/lib/validation/validation-factory.js +1 -1
- package/lib/yaml-utils.js +15 -10
- package/package.json +18 -13
- package/scripts/check-docs.sh +63 -0
- package/scripts/smart-test-strategy.sh +98 -0
- package/scripts/test-e2e-package.sh +283 -0
- package/scripts/validate-command-patterns.js +112 -0
- package/setup.js +161 -44
- package/templates/QUALITY_TROUBLESHOOTING.md +403 -0
- package/templates/ci/circleci-config.yml +35 -0
- package/templates/ci/gitlab-ci.yml +47 -0
- package/templates/integration-tests/api-service.test.js +244 -0
- package/templates/integration-tests/frontend-app.test.js +267 -0
- package/templates/scripts/smart-test-strategy.sh +109 -0
- package/templates/test-stubs/e2e.smoke.test.js +12 -0
- package/templates/test-stubs/unit.test.js +7 -0
- package/legal/README.md +0 -106
- package/legal/copyright.md +0 -76
- package/legal/disclaimer.md +0 -146
- package/legal/privacy-policy.html +0 -324
- package/legal/privacy-policy.md +0 -196
- package/legal/terms-of-service.md +0 -224
- package/marketing/beta-user-email-campaign.md +0 -372
- package/marketing/landing-page.html +0 -721
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Service Integration Test Template
|
|
3
|
+
* Real database and HTTP integration testing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'
|
|
7
|
+
import request from 'supertest'
|
|
8
|
+
|
|
9
|
+
// Example: Adjust imports based on your API framework
|
|
10
|
+
// import app from '../src/app.js'
|
|
11
|
+
// import { setupTestDatabase, cleanupTestDatabase } from './helpers/database'
|
|
12
|
+
|
|
13
|
+
describe('API Service Integration Tests', () => {
|
|
14
|
+
let testApp
|
|
15
|
+
let _testDb
|
|
16
|
+
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
// Setup test database with real connection
|
|
19
|
+
// testDb = await setupTestDatabase()
|
|
20
|
+
// testApp = createApp(testDb)
|
|
21
|
+
|
|
22
|
+
console.log('๐ง API Integration Test Setup:')
|
|
23
|
+
console.log(' 1. Initialize test database (PostgreSQL/MySQL/SQLite)')
|
|
24
|
+
console.log(' 2. Run migrations and seed test data')
|
|
25
|
+
console.log(' 3. Configure test environment variables')
|
|
26
|
+
console.log(' 4. Start test server on random port')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
afterAll(async () => {
|
|
30
|
+
// Cleanup test database
|
|
31
|
+
// await cleanupTestDatabase(testDb)
|
|
32
|
+
console.log('๐งน Cleaned up test database and connections')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
beforeEach(async () => {
|
|
36
|
+
// Reset test data before each test
|
|
37
|
+
// await testDb.truncate(['users', 'sessions'])
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('Authentication Flow', () => {
|
|
41
|
+
it('should complete full authentication cycle', async () => {
|
|
42
|
+
// Register user
|
|
43
|
+
const registerResponse = await request(testApp)
|
|
44
|
+
.post('/api/auth/register')
|
|
45
|
+
.send({
|
|
46
|
+
email: 'test@example.com',
|
|
47
|
+
password: 'SecurePassword123!',
|
|
48
|
+
name: 'Test User',
|
|
49
|
+
})
|
|
50
|
+
.expect(201)
|
|
51
|
+
|
|
52
|
+
expect(registerResponse.body).toMatchObject({
|
|
53
|
+
user: { email: 'test@example.com' },
|
|
54
|
+
token: expect.any(String),
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// Login with credentials
|
|
58
|
+
const loginResponse = await request(testApp)
|
|
59
|
+
.post('/api/auth/login')
|
|
60
|
+
.send({
|
|
61
|
+
email: 'test@example.com',
|
|
62
|
+
password: 'SecurePassword123!',
|
|
63
|
+
})
|
|
64
|
+
.expect(200)
|
|
65
|
+
|
|
66
|
+
const { token } = loginResponse.body
|
|
67
|
+
|
|
68
|
+
// Access protected resource
|
|
69
|
+
const protectedResponse = await request(testApp)
|
|
70
|
+
.get('/api/user/profile')
|
|
71
|
+
.set('Authorization', `Bearer ${token}`)
|
|
72
|
+
.expect(200)
|
|
73
|
+
|
|
74
|
+
expect(protectedResponse.body.email).toBe('test@example.com')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should handle invalid credentials properly', async () => {
|
|
78
|
+
await request(testApp)
|
|
79
|
+
.post('/api/auth/login')
|
|
80
|
+
.send({
|
|
81
|
+
email: 'nonexistent@example.com',
|
|
82
|
+
password: 'wrongpassword',
|
|
83
|
+
})
|
|
84
|
+
.expect(401)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('should enforce rate limiting on login attempts', async () => {
|
|
88
|
+
const credentials = {
|
|
89
|
+
email: 'test@example.com',
|
|
90
|
+
password: 'wrongpassword',
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Make multiple failed attempts
|
|
94
|
+
for (let i = 0; i < 5; i++) {
|
|
95
|
+
await request(testApp)
|
|
96
|
+
.post('/api/auth/login')
|
|
97
|
+
.send(credentials)
|
|
98
|
+
.expect(401)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 6th attempt should be rate limited
|
|
102
|
+
await request(testApp)
|
|
103
|
+
.post('/api/auth/login')
|
|
104
|
+
.send(credentials)
|
|
105
|
+
.expect(429)
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('Database Operations', () => {
|
|
110
|
+
it('should handle concurrent user creation', async () => {
|
|
111
|
+
const users = [
|
|
112
|
+
{ email: 'user1@test.com', password: 'Pass123!', name: 'User 1' },
|
|
113
|
+
{ email: 'user2@test.com', password: 'Pass123!', name: 'User 2' },
|
|
114
|
+
{ email: 'user3@test.com', password: 'Pass123!', name: 'User 3' },
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
// Create users concurrently
|
|
118
|
+
const promises = users.map(user =>
|
|
119
|
+
request(testApp).post('/api/auth/register').send(user)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const responses = await Promise.all(promises)
|
|
123
|
+
|
|
124
|
+
// All should succeed
|
|
125
|
+
responses.forEach(response => {
|
|
126
|
+
expect(response.status).toBe(201)
|
|
127
|
+
expect(response.body.user).toBeDefined()
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// Verify database consistency
|
|
131
|
+
const allUsers = await request(testApp)
|
|
132
|
+
.get('/api/admin/users')
|
|
133
|
+
.set('Authorization', 'Bearer admin_token')
|
|
134
|
+
.expect(200)
|
|
135
|
+
|
|
136
|
+
expect(allUsers.body.users).toHaveLength(3)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should maintain data integrity during transactions', async () => {
|
|
140
|
+
// Test database transactions and rollbacks
|
|
141
|
+
// Example: Transfer operation that must be atomic
|
|
142
|
+
const response = await request(testApp)
|
|
143
|
+
.post('/api/transfer')
|
|
144
|
+
.send({
|
|
145
|
+
fromAccount: 'account1',
|
|
146
|
+
toAccount: 'account2',
|
|
147
|
+
amount: 100.0,
|
|
148
|
+
})
|
|
149
|
+
.expect(200)
|
|
150
|
+
|
|
151
|
+
expect(response.body.transaction).toMatchObject({
|
|
152
|
+
status: 'completed',
|
|
153
|
+
fromAccount: 'account1',
|
|
154
|
+
toAccount: 'account2',
|
|
155
|
+
amount: 100.0,
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
describe('Error Handling', () => {
|
|
161
|
+
it('should handle database connection errors gracefully', async () => {
|
|
162
|
+
// Simulate database disconnection
|
|
163
|
+
// await testDb.disconnect()
|
|
164
|
+
|
|
165
|
+
const response = await request(testApp).get('/api/users').expect(503)
|
|
166
|
+
|
|
167
|
+
expect(response.body).toMatchObject({
|
|
168
|
+
error: 'Service temporarily unavailable',
|
|
169
|
+
code: 'DATABASE_UNAVAILABLE',
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
// Reconnect for cleanup
|
|
173
|
+
// await testDb.reconnect()
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('should validate request payloads properly', async () => {
|
|
177
|
+
await request(testApp)
|
|
178
|
+
.post('/api/auth/register')
|
|
179
|
+
.send({
|
|
180
|
+
// Missing required fields
|
|
181
|
+
email: 'invalid-email',
|
|
182
|
+
})
|
|
183
|
+
.expect(400)
|
|
184
|
+
.expect(res => {
|
|
185
|
+
expect(res.body.errors).toContain('Valid email required')
|
|
186
|
+
expect(res.body.errors).toContain('Password is required')
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe('Performance', () => {
|
|
192
|
+
it('should handle reasonable load', async () => {
|
|
193
|
+
const startTime = Date.now()
|
|
194
|
+
|
|
195
|
+
// Make 50 concurrent requests
|
|
196
|
+
const promises = Array.from({ length: 50 }, () =>
|
|
197
|
+
request(testApp).get('/api/health')
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
const responses = await Promise.all(promises)
|
|
201
|
+
const endTime = Date.now()
|
|
202
|
+
|
|
203
|
+
// All should succeed
|
|
204
|
+
responses.forEach(response => {
|
|
205
|
+
expect(response.status).toBe(200)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
// Should complete within reasonable time (adjust threshold)
|
|
209
|
+
expect(endTime - startTime).toBeLessThan(5000)
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
// Example test helpers (create in tests/helpers/)
|
|
215
|
+
/*
|
|
216
|
+
// tests/helpers/database.js
|
|
217
|
+
export async function setupTestDatabase() {
|
|
218
|
+
// Create test database connection
|
|
219
|
+
// Run migrations
|
|
220
|
+
// Seed test data
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export async function cleanupTestDatabase(db) {
|
|
224
|
+
// Clean up test data
|
|
225
|
+
// Close connections
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// tests/helpers/auth.js
|
|
229
|
+
export async function createTestUser(app, userData) {
|
|
230
|
+
const response = await request(app)
|
|
231
|
+
.post('/api/auth/register')
|
|
232
|
+
.send(userData)
|
|
233
|
+
|
|
234
|
+
return response.body.user
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export async function loginTestUser(app, credentials) {
|
|
238
|
+
const response = await request(app)
|
|
239
|
+
.post('/api/auth/login')
|
|
240
|
+
.send(credentials)
|
|
241
|
+
|
|
242
|
+
return response.body.token
|
|
243
|
+
}
|
|
244
|
+
*/
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontend Application Integration Test Template
|
|
3
|
+
* Real browser testing with user interactions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
7
|
+
import { test } from '@playwright/test'
|
|
8
|
+
|
|
9
|
+
// Example: Adjust imports based on your frontend framework
|
|
10
|
+
// React Testing Library example
|
|
11
|
+
// import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
12
|
+
// import userEvent from '@testing-library/user-event'
|
|
13
|
+
// import { BrowserRouter } from 'react-router-dom'
|
|
14
|
+
|
|
15
|
+
describe('Frontend Application Integration Tests', () => {
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
console.log('๐ง Frontend Integration Test Setup:')
|
|
18
|
+
console.log(' 1. Start development server')
|
|
19
|
+
console.log(' 2. Setup test database/mock APIs')
|
|
20
|
+
console.log(' 3. Configure test environment')
|
|
21
|
+
console.log(' 4. Launch browser for E2E tests')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
afterAll(async () => {
|
|
25
|
+
console.log('๐งน Cleaned up test environment')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('User Authentication Flow', () => {
|
|
29
|
+
it('should complete full authentication journey', async () => {
|
|
30
|
+
// Example using React Testing Library
|
|
31
|
+
/*
|
|
32
|
+
const user = userEvent.setup()
|
|
33
|
+
|
|
34
|
+
render(
|
|
35
|
+
<BrowserRouter>
|
|
36
|
+
<App />
|
|
37
|
+
</BrowserRouter>
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
// Navigate to login
|
|
41
|
+
const loginButton = screen.getByRole('button', { name: /sign in/i })
|
|
42
|
+
await user.click(loginButton)
|
|
43
|
+
|
|
44
|
+
// Fill login form
|
|
45
|
+
const emailInput = screen.getByLabelText(/email/i)
|
|
46
|
+
const passwordInput = screen.getByLabelText(/password/i)
|
|
47
|
+
|
|
48
|
+
await user.type(emailInput, 'test@example.com')
|
|
49
|
+
await user.type(passwordInput, 'SecurePassword123!')
|
|
50
|
+
|
|
51
|
+
const submitButton = screen.getByRole('button', { name: /log in/i })
|
|
52
|
+
await user.click(submitButton)
|
|
53
|
+
|
|
54
|
+
// Verify successful login
|
|
55
|
+
await waitFor(() => {
|
|
56
|
+
expect(screen.getByText(/welcome back/i)).toBeInTheDocument()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Verify navigation to dashboard
|
|
60
|
+
expect(screen.getByRole('heading', { name: /dashboard/i })).toBeInTheDocument()
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
expect(true).toBe(true) // Replace with actual test
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should handle login validation errors', async () => {
|
|
67
|
+
// Test form validation, API errors, network issues
|
|
68
|
+
expect(true).toBe(true) // Replace with actual test
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should persist authentication across page refreshes', async () => {
|
|
72
|
+
// Test token persistence, automatic login restoration
|
|
73
|
+
expect(true).toBe(true) // Replace with actual test
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('Component Integration', () => {
|
|
78
|
+
it('should handle complex form interactions', async () => {
|
|
79
|
+
// Example: Multi-step form with validation
|
|
80
|
+
/*
|
|
81
|
+
const user = userEvent.setup()
|
|
82
|
+
|
|
83
|
+
render(<UserRegistrationForm />)
|
|
84
|
+
|
|
85
|
+
// Step 1: Basic information
|
|
86
|
+
await user.type(screen.getByLabelText(/first name/i), 'John')
|
|
87
|
+
await user.type(screen.getByLabelText(/last name/i), 'Doe')
|
|
88
|
+
await user.type(screen.getByLabelText(/email/i), 'john@example.com')
|
|
89
|
+
|
|
90
|
+
await user.click(screen.getByRole('button', { name: /next/i }))
|
|
91
|
+
|
|
92
|
+
// Step 2: Additional details
|
|
93
|
+
await user.selectOptions(screen.getByLabelText(/country/i), 'US')
|
|
94
|
+
await user.type(screen.getByLabelText(/phone/i), '+1234567890')
|
|
95
|
+
|
|
96
|
+
await user.click(screen.getByRole('button', { name: /submit/i }))
|
|
97
|
+
|
|
98
|
+
// Verify submission
|
|
99
|
+
await waitFor(() => {
|
|
100
|
+
expect(screen.getByText(/registration successful/i)).toBeInTheDocument()
|
|
101
|
+
})
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
expect(true).toBe(true) // Replace with actual test
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('should handle API loading states properly', async () => {
|
|
108
|
+
// Test loading spinners, skeleton UI, error states
|
|
109
|
+
expect(true).toBe(true) // Replace with actual test
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should update UI based on real-time data', async () => {
|
|
113
|
+
// Test WebSocket connections, polling, state updates
|
|
114
|
+
expect(true).toBe(true) // Replace with actual test
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
describe('Navigation and Routing', () => {
|
|
119
|
+
it('should handle complex navigation flows', async () => {
|
|
120
|
+
// Test protected routes, redirects, deep linking
|
|
121
|
+
expect(true).toBe(true) // Replace with actual test
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should preserve state during navigation', async () => {
|
|
125
|
+
// Test form data persistence, scroll position, etc.
|
|
126
|
+
expect(true).toBe(true) // Replace with actual test
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
describe('Accessibility Integration', () => {
|
|
131
|
+
it('should support keyboard navigation', async () => {
|
|
132
|
+
// Test tab order, keyboard shortcuts, focus management
|
|
133
|
+
expect(true).toBe(true) // Replace with actual test
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should work with screen readers', async () => {
|
|
137
|
+
// Test aria labels, semantic HTML, announcements
|
|
138
|
+
expect(true).toBe(true) // Replace with actual test
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should handle color contrast and visual requirements', async () => {
|
|
142
|
+
// Test color contrast ratios, font sizes, visual indicators
|
|
143
|
+
expect(true).toBe(true) // Replace with actual test
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
// Playwright E2E Tests (real browser testing)
|
|
149
|
+
test.describe('Frontend E2E Tests', () => {
|
|
150
|
+
test.beforeEach(async ({ page }) => {
|
|
151
|
+
// Navigate to app
|
|
152
|
+
await page.goto('http://localhost:3000')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('should complete user signup and onboarding', async ({ page }) => {
|
|
156
|
+
// Click signup button
|
|
157
|
+
await page.click('text=Sign Up')
|
|
158
|
+
|
|
159
|
+
// Fill registration form
|
|
160
|
+
await page.fill('[data-testid=email-input]', 'test@example.com')
|
|
161
|
+
await page.fill('[data-testid=password-input]', 'SecurePassword123!')
|
|
162
|
+
await page.fill(
|
|
163
|
+
'[data-testid=confirm-password-input]',
|
|
164
|
+
'SecurePassword123!'
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
// Submit form
|
|
168
|
+
await page.click('[data-testid=signup-button]')
|
|
169
|
+
|
|
170
|
+
// Wait for redirect to onboarding
|
|
171
|
+
await page.waitForURL('**/onboarding')
|
|
172
|
+
|
|
173
|
+
// Complete onboarding steps
|
|
174
|
+
await page.fill('[data-testid=first-name]', 'Test')
|
|
175
|
+
await page.fill('[data-testid=last-name]', 'User')
|
|
176
|
+
await page.click('[data-testid=continue-button]')
|
|
177
|
+
|
|
178
|
+
// Verify dashboard access
|
|
179
|
+
await page.waitForURL('**/dashboard')
|
|
180
|
+
await expect(page.locator('h1')).toContainText('Welcome, Test!')
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('should handle responsive design across devices', async ({ page }) => {
|
|
184
|
+
// Test mobile viewport
|
|
185
|
+
await page.setViewportSize({ width: 375, height: 667 })
|
|
186
|
+
|
|
187
|
+
// Mobile navigation should be collapsed
|
|
188
|
+
await expect(page.locator('[data-testid=mobile-menu-button]')).toBeVisible()
|
|
189
|
+
await expect(page.locator('[data-testid=desktop-nav]')).not.toBeVisible()
|
|
190
|
+
|
|
191
|
+
// Test tablet viewport
|
|
192
|
+
await page.setViewportSize({ width: 768, height: 1024 })
|
|
193
|
+
|
|
194
|
+
// Test desktop viewport
|
|
195
|
+
await page.setViewportSize({ width: 1920, height: 1080 })
|
|
196
|
+
|
|
197
|
+
// Desktop navigation should be visible
|
|
198
|
+
await expect(page.locator('[data-testid=desktop-nav]')).toBeVisible()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('should handle network failures gracefully', async ({ page }) => {
|
|
202
|
+
// Simulate offline mode
|
|
203
|
+
await page.context().setOffline(true)
|
|
204
|
+
|
|
205
|
+
// Try to navigate or perform action
|
|
206
|
+
await page.click('[data-testid=load-data-button]')
|
|
207
|
+
|
|
208
|
+
// Should show offline message
|
|
209
|
+
await expect(page.locator('[data-testid=offline-banner]')).toContainText(
|
|
210
|
+
'You are offline'
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
// Restore connection
|
|
214
|
+
await page.context().setOffline(false)
|
|
215
|
+
|
|
216
|
+
// Should automatically retry and succeed
|
|
217
|
+
await expect(page.locator('[data-testid=data-loaded]')).toBeVisible()
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
test('should maintain performance under load', async ({ page }) => {
|
|
221
|
+
// Navigate to data-heavy page
|
|
222
|
+
await page.goto('http://localhost:3000/dashboard/analytics')
|
|
223
|
+
|
|
224
|
+
// Measure page load performance
|
|
225
|
+
const navigationPromise = page.waitForLoadState('networkidle')
|
|
226
|
+
await navigationPromise
|
|
227
|
+
|
|
228
|
+
// Check that page loaded within reasonable time
|
|
229
|
+
const metrics = await page.evaluate(() =>
|
|
230
|
+
JSON.stringify(window.performance.timing)
|
|
231
|
+
)
|
|
232
|
+
const timing = JSON.parse(metrics)
|
|
233
|
+
|
|
234
|
+
const loadTime = timing.loadEventEnd - timing.navigationStart
|
|
235
|
+
expect(loadTime).toBeLessThan(3000) // 3 seconds max
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// Example test helpers (create in tests/helpers/)
|
|
240
|
+
/*
|
|
241
|
+
// tests/helpers/render.js
|
|
242
|
+
export function renderWithProviders(ui, options) {
|
|
243
|
+
// Wrap with providers (Router, Theme, Auth, etc.)
|
|
244
|
+
return render(
|
|
245
|
+
<BrowserRouter>
|
|
246
|
+
<ThemeProvider>
|
|
247
|
+
<AuthProvider>
|
|
248
|
+
{ui}
|
|
249
|
+
</AuthProvider>
|
|
250
|
+
</ThemeProvider>
|
|
251
|
+
</BrowserRouter>,
|
|
252
|
+
options
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// tests/helpers/api.js
|
|
257
|
+
export function mockApiCalls() {
|
|
258
|
+
// Setup MSW or similar mocking
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// tests/helpers/accessibility.js
|
|
262
|
+
export async function checkAccessibility(container) {
|
|
263
|
+
const { axe } = await import('axe-core')
|
|
264
|
+
const results = await axe(container)
|
|
265
|
+
expect(results.violations).toHaveLength(0)
|
|
266
|
+
}
|
|
267
|
+
*/
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Smart Test Strategy - {{PROJECT_NAME}}
|
|
3
|
+
# Generated by create-qa-architect (Pro/Team/Enterprise feature)
|
|
4
|
+
# https://vibebuildlab.com/tools/qa-architect
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
echo "๐ง Analyzing changes for optimal test strategy..."
|
|
8
|
+
|
|
9
|
+
# Environment variable overrides
|
|
10
|
+
if [[ "$SKIP_SMART" == "1" ]]; then
|
|
11
|
+
echo "โ ๏ธ SKIP_SMART=1 - Running comprehensive tests"
|
|
12
|
+
npm run test:comprehensive
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [[ "$FORCE_COMPREHENSIVE" == "1" ]]; then
|
|
17
|
+
echo "๐ด FORCE_COMPREHENSIVE=1 - Running all tests"
|
|
18
|
+
npm run test:comprehensive
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [[ "$FORCE_MINIMAL" == "1" ]]; then
|
|
23
|
+
echo "โช FORCE_MINIMAL=1 - Running lint only"
|
|
24
|
+
npm run lint && npm run format:check
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Collect metrics
|
|
29
|
+
CHANGED_FILES=$(git diff --name-only HEAD~1..HEAD 2>/dev/null | wc -l | tr -d ' ')
|
|
30
|
+
CHANGED_LINES=$(git diff --stat HEAD~1..HEAD 2>/dev/null | tail -1 | grep -o '[0-9]* insertions' | grep -o '[0-9]*' || echo "0")
|
|
31
|
+
CURRENT_BRANCH=$(git branch --show-current)
|
|
32
|
+
HOUR=$(date +%H)
|
|
33
|
+
DAY_OF_WEEK=$(date +%u)
|
|
34
|
+
|
|
35
|
+
# Project-specific high-risk patterns (customized per project type)
|
|
36
|
+
# {{HIGH_RISK_PATTERN}}
|
|
37
|
+
HIGH_RISK_FILES=$(git diff --name-only HEAD~1..HEAD 2>/dev/null | grep -E "{{HIGH_RISK_REGEX}}" || true)
|
|
38
|
+
API_FILES=$(git diff --name-only HEAD~1..HEAD 2>/dev/null | grep -E "api/|routes/|endpoints/" || true)
|
|
39
|
+
CONFIG_FILES=$(git diff --name-only HEAD~1..HEAD 2>/dev/null | grep -E "(package\.json|\.env|config|tsconfig)" || true)
|
|
40
|
+
SECURITY_FILES=$(git diff --name-only HEAD~1..HEAD 2>/dev/null | grep -E "(auth|security|crypto|payment|billing)" || true)
|
|
41
|
+
TEST_FILES=$(git diff --name-only HEAD~1..HEAD 2>/dev/null | grep -E "test|spec|__tests__" || true)
|
|
42
|
+
|
|
43
|
+
# Calculate risk score (0-10)
|
|
44
|
+
RISK_SCORE=0
|
|
45
|
+
|
|
46
|
+
# File-based risk
|
|
47
|
+
[[ -n "$HIGH_RISK_FILES" ]] && RISK_SCORE=$((RISK_SCORE + 4))
|
|
48
|
+
[[ -n "$SECURITY_FILES" ]] && RISK_SCORE=$((RISK_SCORE + 3))
|
|
49
|
+
[[ -n "$API_FILES" ]] && RISK_SCORE=$((RISK_SCORE + 2))
|
|
50
|
+
[[ -n "$CONFIG_FILES" ]] && RISK_SCORE=$((RISK_SCORE + 2))
|
|
51
|
+
|
|
52
|
+
# Size-based risk
|
|
53
|
+
[[ $CHANGED_FILES -gt 10 ]] && RISK_SCORE=$((RISK_SCORE + 2))
|
|
54
|
+
[[ $CHANGED_FILES -gt 20 ]] && RISK_SCORE=$((RISK_SCORE + 3))
|
|
55
|
+
[[ $CHANGED_LINES -gt 200 ]] && RISK_SCORE=$((RISK_SCORE + 2))
|
|
56
|
+
|
|
57
|
+
# Branch-based risk
|
|
58
|
+
case $CURRENT_BRANCH in
|
|
59
|
+
main|master|production) RISK_SCORE=$((RISK_SCORE + 3)) ;;
|
|
60
|
+
hotfix/*) RISK_SCORE=$((RISK_SCORE + 4)) ;;
|
|
61
|
+
release/*) RISK_SCORE=$((RISK_SCORE + 2)) ;;
|
|
62
|
+
develop) RISK_SCORE=$((RISK_SCORE + 1)) ;;
|
|
63
|
+
esac
|
|
64
|
+
|
|
65
|
+
# Time pressure adjustment (strip leading zeros for arithmetic)
|
|
66
|
+
HOUR_NUM=$((10#$HOUR))
|
|
67
|
+
if [[ $HOUR_NUM -ge 9 && $HOUR_NUM -le 17 && $DAY_OF_WEEK -le 5 ]]; then
|
|
68
|
+
echo "โฐ Work hours - Optimizing for speed"
|
|
69
|
+
SPEED_BONUS=true
|
|
70
|
+
else
|
|
71
|
+
SPEED_BONUS=false
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Display analysis
|
|
75
|
+
echo ""
|
|
76
|
+
echo "๐ Analysis Results:"
|
|
77
|
+
echo " ๐ Files changed: $CHANGED_FILES"
|
|
78
|
+
echo " ๐ Lines changed: $CHANGED_LINES"
|
|
79
|
+
echo " ๐ฟ Branch: $CURRENT_BRANCH"
|
|
80
|
+
echo " ๐ฏ Risk Score: $RISK_SCORE/10"
|
|
81
|
+
echo " โก Speed Bonus: $SPEED_BONUS"
|
|
82
|
+
echo ""
|
|
83
|
+
|
|
84
|
+
# Test tier selection based on risk score
|
|
85
|
+
if [[ $RISK_SCORE -ge 7 ]]; then
|
|
86
|
+
echo "๐ด HIGH RISK - Comprehensive validation"
|
|
87
|
+
echo " โข All tests + security audit"
|
|
88
|
+
# {{COMPREHENSIVE_COMMAND}}
|
|
89
|
+
{{TEST_COMPREHENSIVE}}
|
|
90
|
+
elif [[ $RISK_SCORE -ge 4 ]]; then
|
|
91
|
+
echo "๐ก MEDIUM RISK - Standard validation"
|
|
92
|
+
echo " โข Fast tests + integration (excludes slow tests)"
|
|
93
|
+
# {{MEDIUM_COMMAND}}
|
|
94
|
+
{{TEST_MEDIUM}}
|
|
95
|
+
elif [[ $RISK_SCORE -ge 2 || "$SPEED_BONUS" == "false" ]]; then
|
|
96
|
+
echo "๐ข LOW RISK - Fast validation"
|
|
97
|
+
echo " โข Unit tests only"
|
|
98
|
+
# {{FAST_COMMAND}}
|
|
99
|
+
{{TEST_FAST}}
|
|
100
|
+
else
|
|
101
|
+
echo "โช MINIMAL RISK - Quality checks only"
|
|
102
|
+
echo " โข Lint + format check"
|
|
103
|
+
# {{MINIMAL_COMMAND}}
|
|
104
|
+
{{TEST_MINIMAL}}
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
echo ""
|
|
108
|
+
echo "๐ก Tip: Run 'npm run test:comprehensive' locally for full validation"
|
|
109
|
+
echo "๐ Smart Test Strategy powered by QA Architect Pro"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
// Replace this stub with Playwright/Cypress flows for your app.
|
|
4
|
+
describe.skip('smoke: homepage renders', () => {
|
|
5
|
+
it('runs a placeholder e2e flow', async () => {
|
|
6
|
+
// TODO: add real browser automation (Playwright/Cypress/Webdriver)
|
|
7
|
+
// Example:
|
|
8
|
+
// const page = await chromium.launch().newPage()
|
|
9
|
+
// await page.goto('http://localhost:3000')
|
|
10
|
+
// await page.getByText('Welcome').isVisible()
|
|
11
|
+
})
|
|
12
|
+
})
|