@kosuke-ai/cli 0.0.12 ā 0.0.14
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/dist/index.d.ts +14 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -6
- package/dist/index.js.map +1 -1
- package/dist/kosuke/commands/build.d.ts +22 -0
- package/dist/kosuke/commands/build.d.ts.map +1 -0
- package/dist/kosuke/commands/build.js +135 -0
- package/dist/kosuke/commands/build.js.map +1 -0
- package/dist/kosuke/commands/review.d.ts +21 -0
- package/dist/kosuke/commands/review.d.ts.map +1 -0
- package/dist/kosuke/commands/review.js +185 -0
- package/dist/kosuke/commands/review.js.map +1 -0
- package/dist/kosuke/commands/ship.d.ts +23 -0
- package/dist/kosuke/commands/ship.d.ts.map +1 -0
- package/dist/kosuke/commands/ship.js +419 -0
- package/dist/kosuke/commands/ship.js.map +1 -0
- package/dist/kosuke/commands/test.d.ts +27 -0
- package/dist/kosuke/commands/test.d.ts.map +1 -0
- package/dist/kosuke/commands/test.js +338 -0
- package/dist/kosuke/commands/test.js.map +1 -0
- package/dist/kosuke/types.d.ts +82 -2
- package/dist/kosuke/types.d.ts.map +1 -1
- package/dist/kosuke/utils/error-analyzer.d.ts +32 -0
- package/dist/kosuke/utils/error-analyzer.d.ts.map +1 -0
- package/dist/kosuke/utils/error-analyzer.js +129 -0
- package/dist/kosuke/utils/error-analyzer.js.map +1 -0
- package/dist/kosuke/utils/git.d.ts +12 -0
- package/dist/kosuke/utils/git.d.ts.map +1 -1
- package/dist/kosuke/utils/git.js +39 -0
- package/dist/kosuke/utils/git.js.map +1 -1
- package/dist/kosuke/utils/log-collector.d.ts +81 -0
- package/dist/kosuke/utils/log-collector.d.ts.map +1 -0
- package/dist/kosuke/utils/log-collector.js +226 -0
- package/dist/kosuke/utils/log-collector.js.map +1 -0
- package/dist/kosuke/utils/playwright-agent.d.ts +39 -0
- package/dist/kosuke/utils/playwright-agent.d.ts.map +1 -0
- package/dist/kosuke/utils/playwright-agent.js +245 -0
- package/dist/kosuke/utils/playwright-agent.js.map +1 -0
- package/dist/kosuke/utils/test-generator.d.ts +22 -0
- package/dist/kosuke/utils/test-generator.d.ts.map +1 -0
- package/dist/kosuke/utils/test-generator.js +174 -0
- package/dist/kosuke/utils/test-generator.js.map +1 -0
- package/dist/kosuke/utils/validator.d.ts +11 -0
- package/dist/kosuke/utils/validator.d.ts.map +1 -1
- package/dist/kosuke/utils/validator.js +100 -0
- package/dist/kosuke/utils/validator.js.map +1 -1
- package/dist/kosuke/utils/visual-tester.d.ts +71 -0
- package/dist/kosuke/utils/visual-tester.d.ts.map +1 -0
- package/dist/kosuke/utils/visual-tester.js +202 -0
- package/dist/kosuke/utils/visual-tester.js.map +1 -0
- package/dist/lib.d.ts +6 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +5 -1
- package/dist/lib.js.map +1 -1
- package/package.json +6 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-agent.js","sourceRoot":"","sources":["../../../kosuke/utils/playwright-agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Generator - AI-powered Playwright test generation
|
|
3
|
+
*
|
|
4
|
+
* Uses Claude to generate Playwright tests from ticket descriptions
|
|
5
|
+
*/
|
|
6
|
+
import type { Ticket } from '../types.js';
|
|
7
|
+
export interface GeneratedTest {
|
|
8
|
+
filePath: string;
|
|
9
|
+
content: string;
|
|
10
|
+
tokensUsed: {
|
|
11
|
+
input: number;
|
|
12
|
+
output: number;
|
|
13
|
+
cacheCreation: number;
|
|
14
|
+
cacheRead: number;
|
|
15
|
+
};
|
|
16
|
+
cost: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate Playwright test from ticket description
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateTest(ticket: Ticket, baseUrl: string, cwd?: string): Promise<GeneratedTest>;
|
|
22
|
+
//# sourceMappingURL=test-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-generator.d.ts","sourceRoot":"","sources":["../../../kosuke/utils/test-generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,aAAa,CAAC,CAkDxB"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Generator - AI-powered Playwright test generation
|
|
3
|
+
*
|
|
4
|
+
* Uses Claude to generate Playwright tests from ticket descriptions
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { runAgent } from './claude-agent.js';
|
|
9
|
+
/**
|
|
10
|
+
* Generate Playwright test from ticket description
|
|
11
|
+
*/
|
|
12
|
+
export async function generateTest(ticket, baseUrl, cwd = process.cwd()) {
|
|
13
|
+
const testDir = join(cwd, '.kosuke', 'tests');
|
|
14
|
+
const testFilePath = join(testDir, `${ticket.id}.spec.ts`);
|
|
15
|
+
// Ensure test directory exists
|
|
16
|
+
if (!existsSync(testDir)) {
|
|
17
|
+
mkdirSync(testDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
// Check if test already exists
|
|
20
|
+
if (existsSync(testFilePath)) {
|
|
21
|
+
console.log(` ā
Test already exists: ${testFilePath}`);
|
|
22
|
+
return {
|
|
23
|
+
filePath: testFilePath,
|
|
24
|
+
content: '',
|
|
25
|
+
tokensUsed: {
|
|
26
|
+
input: 0,
|
|
27
|
+
output: 0,
|
|
28
|
+
cacheCreation: 0,
|
|
29
|
+
cacheRead: 0,
|
|
30
|
+
},
|
|
31
|
+
cost: 0,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
console.log(` š¤ Generating test using Claude...`);
|
|
35
|
+
const systemPrompt = buildTestGenerationPrompt(ticket, baseUrl);
|
|
36
|
+
const result = await runAgent(`Generate Playwright test for ticket ${ticket.id}`, {
|
|
37
|
+
systemPrompt,
|
|
38
|
+
cwd,
|
|
39
|
+
maxTurns: 15,
|
|
40
|
+
verbosity: 'minimal',
|
|
41
|
+
});
|
|
42
|
+
// Extract test code from response
|
|
43
|
+
const testContent = extractTestCode(result.response, ticket, baseUrl);
|
|
44
|
+
// Save test file
|
|
45
|
+
writeFileSync(testFilePath, testContent, 'utf-8');
|
|
46
|
+
console.log(` ā
Test generated: ${testFilePath}`);
|
|
47
|
+
return {
|
|
48
|
+
filePath: testFilePath,
|
|
49
|
+
content: testContent,
|
|
50
|
+
tokensUsed: result.tokensUsed,
|
|
51
|
+
cost: result.cost,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build system prompt for test generation
|
|
56
|
+
*/
|
|
57
|
+
function buildTestGenerationPrompt(ticket, baseUrl) {
|
|
58
|
+
return `You are an expert QA engineer creating Playwright end-to-end tests.
|
|
59
|
+
|
|
60
|
+
**Your Task:**
|
|
61
|
+
Generate a comprehensive Playwright test file for the following ticket.
|
|
62
|
+
|
|
63
|
+
**Ticket Information:**
|
|
64
|
+
- ID: ${ticket.id}
|
|
65
|
+
- Title: ${ticket.title}
|
|
66
|
+
- Description:
|
|
67
|
+
${ticket.description}
|
|
68
|
+
|
|
69
|
+
**Application URL:** ${baseUrl}
|
|
70
|
+
|
|
71
|
+
**Test Requirements:**
|
|
72
|
+
1. Create a complete Playwright test file in TypeScript
|
|
73
|
+
2. Include multiple test cases covering:
|
|
74
|
+
- Happy path (main functionality)
|
|
75
|
+
- Edge cases (validation, errors)
|
|
76
|
+
- Visual regression test (one screenshot)
|
|
77
|
+
3. Use proper Playwright selectors (prefer getByRole, getByLabel, getByText)
|
|
78
|
+
4. Include proper assertions with expect()
|
|
79
|
+
5. Add comments explaining what each test does
|
|
80
|
+
6. Follow Playwright best practices
|
|
81
|
+
|
|
82
|
+
**Test Structure:**
|
|
83
|
+
\`\`\`typescript
|
|
84
|
+
import { test, expect } from '@playwright/test';
|
|
85
|
+
|
|
86
|
+
test.describe('${ticket.id}: ${ticket.title}', () => {
|
|
87
|
+
test.beforeEach(async ({ page }) => {
|
|
88
|
+
// Navigate to the relevant page
|
|
89
|
+
await page.goto('${baseUrl}/...');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('should [describe main functionality]', async ({ page }) => {
|
|
93
|
+
// Test implementation
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('should handle [edge case]', async ({ page }) => {
|
|
97
|
+
// Test implementation
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('visual regression: [page/component] renders correctly', async ({ page }) => {
|
|
101
|
+
// Wait for page to be stable
|
|
102
|
+
await page.waitForLoadState('networkidle');
|
|
103
|
+
|
|
104
|
+
// Take screenshot
|
|
105
|
+
const screenshot = await page.screenshot({ fullPage: true });
|
|
106
|
+
|
|
107
|
+
// Compare with baseline (handled by visual tester)
|
|
108
|
+
expect(screenshot).toBeDefined();
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
\`\`\`
|
|
112
|
+
|
|
113
|
+
**Critical Instructions:**
|
|
114
|
+
1. Analyze the ticket description to understand what features to test
|
|
115
|
+
2. Determine the correct URL paths based on the feature
|
|
116
|
+
3. Generate realistic test data (emails, names, etc.)
|
|
117
|
+
4. Include proper waits (waitForLoadState, waitForSelector)
|
|
118
|
+
5. Test both success and failure scenarios
|
|
119
|
+
6. Add one visual regression test at the end
|
|
120
|
+
7. Output ONLY the test code - no explanations outside the code
|
|
121
|
+
|
|
122
|
+
Generate the complete test file now:`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Extract test code from Claude's response
|
|
126
|
+
*/
|
|
127
|
+
function extractTestCode(response, ticket, baseUrl) {
|
|
128
|
+
// Look for code blocks in the response
|
|
129
|
+
const codeBlockMatch = response.match(/```(?:typescript|ts)?\n([\s\S]+?)\n```/);
|
|
130
|
+
if (codeBlockMatch) {
|
|
131
|
+
return codeBlockMatch[1].trim();
|
|
132
|
+
}
|
|
133
|
+
// If no code block found, try to use the whole response
|
|
134
|
+
// or generate a basic template
|
|
135
|
+
if (response.includes('import') && response.includes('test(')) {
|
|
136
|
+
return response.trim();
|
|
137
|
+
}
|
|
138
|
+
// Fallback: generate basic template
|
|
139
|
+
console.log(' ā ļø Could not extract test code, generating basic template');
|
|
140
|
+
return generateBasicTemplate(ticket, baseUrl);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Generate a basic test template as fallback
|
|
144
|
+
*/
|
|
145
|
+
function generateBasicTemplate(ticket, baseUrl) {
|
|
146
|
+
return `import { test, expect } from '@playwright/test';
|
|
147
|
+
|
|
148
|
+
test.describe('${ticket.id}: ${ticket.title}', () => {
|
|
149
|
+
test.beforeEach(async ({ page }) => {
|
|
150
|
+
// TODO: Update this URL to the correct path for the feature
|
|
151
|
+
await page.goto('${baseUrl}');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('should load the page', async ({ page }) => {
|
|
155
|
+
// TODO: Add assertions to verify the page loaded correctly
|
|
156
|
+
await expect(page.locator('body')).toBeVisible();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('should interact with main feature', async ({ page }) => {
|
|
160
|
+
// TODO: Add test steps for the main functionality
|
|
161
|
+
// Example:
|
|
162
|
+
// await page.getByRole('button', { name: 'Submit' }).click();
|
|
163
|
+
// await expect(page.getByText('Success')).toBeVisible();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('visual regression: page renders correctly', async ({ page }) => {
|
|
167
|
+
await page.waitForLoadState('networkidle');
|
|
168
|
+
const screenshot = await page.screenshot({ fullPage: true });
|
|
169
|
+
expect(screenshot).toBeDefined();
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
`;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=test-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-generator.js","sourceRoot":"","sources":["../../../kosuke/utils/test-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,5 +30,16 @@ export declare function runLint(): Promise<ValidationResult>;
|
|
|
30
30
|
* Run TypeScript type checking using detected package manager
|
|
31
31
|
*/
|
|
32
32
|
export declare function runTypecheck(): Promise<ValidationResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Run tests using package.json test script
|
|
35
|
+
*/
|
|
36
|
+
export declare function runTests(): Promise<ValidationResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Run comprehensive linting and fixing
|
|
39
|
+
*/
|
|
40
|
+
export declare function runComprehensiveLinting(): Promise<{
|
|
41
|
+
success: boolean;
|
|
42
|
+
fixCount: number;
|
|
43
|
+
}>;
|
|
33
44
|
export {};
|
|
34
45
|
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../kosuke/utils/validator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAQxE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAsB,GAAG,WAAW,CAAC,SAAS,CAAC,GAAG,IAAI,CAcjG;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAqC3D;AAED;;GAEG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAwCzD;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAqC9D"}
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../kosuke/utils/validator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAQxE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAsB,GAAG,WAAW,CAAC,SAAS,CAAC,GAAG,IAAI,CAcjG;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAqC3D;AAED;;GAEG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAwCzD;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAqC9D;AAED;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,gBAAgB,CAAC,CA4B1D;AAOD;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmF/F"}
|
|
@@ -149,4 +149,104 @@ export async function runTypecheck() {
|
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Run tests using package.json test script
|
|
154
|
+
*/
|
|
155
|
+
export async function runTests() {
|
|
156
|
+
const cwd = process.cwd();
|
|
157
|
+
const scripts = readPackageJsonScripts(cwd);
|
|
158
|
+
if (!scripts || !scripts.test) {
|
|
159
|
+
return {
|
|
160
|
+
success: true,
|
|
161
|
+
warning: 'ā ļø No test script found in package.json. Skipping tests.',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const packageManager = detectPackageManager(cwd);
|
|
165
|
+
const command = `${packageManager} run test`;
|
|
166
|
+
try {
|
|
167
|
+
const output = execSync(command, {
|
|
168
|
+
cwd,
|
|
169
|
+
encoding: 'utf-8',
|
|
170
|
+
stdio: 'pipe',
|
|
171
|
+
});
|
|
172
|
+
return { success: true, output };
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
const err = error;
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
error: `$ ${command}\n\n${err.stdout || err.stderr || err.message}`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Run comprehensive linting and fixing
|
|
184
|
+
*/
|
|
185
|
+
export async function runComprehensiveLinting() {
|
|
186
|
+
console.log('\nš Running comprehensive code quality checks...\n');
|
|
187
|
+
const validationSteps = [
|
|
188
|
+
{ name: 'šØ Format', run: runFormat },
|
|
189
|
+
{ name: 'š Lint', run: runLint },
|
|
190
|
+
{ name: 'š TypeCheck', run: runTypecheck },
|
|
191
|
+
{ name: 'š§Ŗ Tests', run: runTests },
|
|
192
|
+
];
|
|
193
|
+
let totalFixCount = 0;
|
|
194
|
+
// Run each validation step
|
|
195
|
+
for (const step of validationSteps) {
|
|
196
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
197
|
+
console.log(`Running: ${step.name}`);
|
|
198
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
199
|
+
let result = await step.run();
|
|
200
|
+
// Handle warnings (non-blocking)
|
|
201
|
+
if (result.warning) {
|
|
202
|
+
console.log(result.warning);
|
|
203
|
+
console.log(`ā
${step.name} - SKIPPED\n`);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
// Handle success
|
|
207
|
+
if (result.success) {
|
|
208
|
+
console.log(`ā
${step.name} - PASSED\n`);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
// Handle errors - attempt to fix
|
|
212
|
+
console.log(`ā ${step.name} - FAILED:\n`);
|
|
213
|
+
console.log(result.error);
|
|
214
|
+
// Attempt to fix errors with Claude (max 3 attempts per step)
|
|
215
|
+
let attemptCount = 0;
|
|
216
|
+
const maxAttempts = 3;
|
|
217
|
+
while (!result.success && attemptCount < maxAttempts) {
|
|
218
|
+
attemptCount++;
|
|
219
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
220
|
+
console.log(`š ${step.name} Fix Attempt ${attemptCount}/${maxAttempts}`);
|
|
221
|
+
console.log(`${'='.repeat(60)}`);
|
|
222
|
+
const { fixCodeQualityErrors } = await import('../commands/lint.js');
|
|
223
|
+
const fixApplied = await fixCodeQualityErrors(step.name, result.error || '');
|
|
224
|
+
if (!fixApplied) {
|
|
225
|
+
console.log(`\nā ļø No fixes were applied by Claude for ${step.name}`);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
totalFixCount++;
|
|
229
|
+
// Verify fixes by running validation again
|
|
230
|
+
console.log(`\nš Verifying ${step.name} fixes...\n`);
|
|
231
|
+
result = await step.run();
|
|
232
|
+
if (result.success) {
|
|
233
|
+
console.log(`ā
${step.name} - All errors fixed!\n`);
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
const errorLines = result.error?.split('\n').length || 0;
|
|
238
|
+
console.log(`\nā ļø Some ${step.name} errors remain (${errorLines} lines):`);
|
|
239
|
+
console.log(result.error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Check if step still has errors after attempts
|
|
243
|
+
if (!result.success) {
|
|
244
|
+
throw new Error(`${step.name} errors remain after ${maxAttempts} attempts. Cannot proceed.`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
console.log('\n' + '='.repeat(60));
|
|
248
|
+
console.log('ā
All validation steps passed!');
|
|
249
|
+
console.log('='.repeat(60));
|
|
250
|
+
return { success: true, fixCount: totalFixCount };
|
|
251
|
+
}
|
|
152
252
|
//# sourceMappingURL=validator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../kosuke/utils/validator.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../kosuke/utils/validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visual Tester - Visual regression testing with screenshot comparison
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Subito.it's approach:
|
|
5
|
+
* - Capture screenshots of pages
|
|
6
|
+
* - Compare against baseline images
|
|
7
|
+
* - Detect pixel differences above threshold
|
|
8
|
+
* - Hide dynamic content (ads, analytics)
|
|
9
|
+
*/
|
|
10
|
+
import type { Page } from '@playwright/test';
|
|
11
|
+
export interface VisualDiff {
|
|
12
|
+
testName: string;
|
|
13
|
+
diffPixels: number;
|
|
14
|
+
diffPercentage: number;
|
|
15
|
+
threshold: number;
|
|
16
|
+
passed: boolean;
|
|
17
|
+
baselinePath: string;
|
|
18
|
+
currentPath: string;
|
|
19
|
+
diffPath: string;
|
|
20
|
+
}
|
|
21
|
+
export interface VisualTestOptions {
|
|
22
|
+
ticketId: string;
|
|
23
|
+
testName: string;
|
|
24
|
+
threshold?: number;
|
|
25
|
+
updateBaseline?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Visual Tester class for screenshot comparison
|
|
29
|
+
*/
|
|
30
|
+
export declare class VisualTester {
|
|
31
|
+
private cwd;
|
|
32
|
+
private diffs;
|
|
33
|
+
constructor(cwd?: string);
|
|
34
|
+
/**
|
|
35
|
+
* Hide dynamic content that shouldn't be tested
|
|
36
|
+
*/
|
|
37
|
+
hideDynamicContent(page: Page): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Take a screenshot and compare with baseline
|
|
40
|
+
*/
|
|
41
|
+
captureAndCompare(page: Page, options: VisualTestOptions): Promise<VisualDiff>;
|
|
42
|
+
/**
|
|
43
|
+
* Compare two images and generate diff
|
|
44
|
+
*/
|
|
45
|
+
private compareImages;
|
|
46
|
+
/**
|
|
47
|
+
* Get all visual diffs
|
|
48
|
+
*/
|
|
49
|
+
getDiffs(): VisualDiff[];
|
|
50
|
+
/**
|
|
51
|
+
* Get failed visual diffs
|
|
52
|
+
*/
|
|
53
|
+
getFailedDiffs(): VisualDiff[];
|
|
54
|
+
/**
|
|
55
|
+
* Check if there are any visual regressions
|
|
56
|
+
*/
|
|
57
|
+
hasRegressions(): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Format visual diff report
|
|
60
|
+
*/
|
|
61
|
+
formatReport(): string;
|
|
62
|
+
/**
|
|
63
|
+
* Clear all diffs
|
|
64
|
+
*/
|
|
65
|
+
clear(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Ensure directory exists
|
|
68
|
+
*/
|
|
69
|
+
private ensureDir;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=visual-tester.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-tester.d.ts","sourceRoot":"","sources":["../../../kosuke/utils/visual-tester.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI7C,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,KAAK,CAAoB;gBAErB,GAAG,GAAE,MAAsB;IAIvC;;OAEG;IACG,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBnD;;OAEG;IACG,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;IAuDpF;;OAEG;IACH,OAAO,CAAC,aAAa;IAwErB;;OAEG;IACH,QAAQ,IAAI,UAAU,EAAE;IAIxB;;OAEG;IACH,cAAc,IAAI,UAAU,EAAE;IAI9B;;OAEG;IACH,cAAc,IAAI,OAAO;IAIzB;;OAEG;IACH,YAAY,IAAI,MAAM;IA2BtB;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,OAAO,CAAC,SAAS;CAKlB"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visual Tester - Visual regression testing with screenshot comparison
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Subito.it's approach:
|
|
5
|
+
* - Capture screenshots of pages
|
|
6
|
+
* - Compare against baseline images
|
|
7
|
+
* - Detect pixel differences above threshold
|
|
8
|
+
* - Hide dynamic content (ads, analytics)
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { PNG } from 'pngjs';
|
|
13
|
+
import pixelmatch from 'pixelmatch';
|
|
14
|
+
/**
|
|
15
|
+
* Visual Tester class for screenshot comparison
|
|
16
|
+
*/
|
|
17
|
+
export class VisualTester {
|
|
18
|
+
constructor(cwd = process.cwd()) {
|
|
19
|
+
this.diffs = [];
|
|
20
|
+
this.cwd = cwd;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Hide dynamic content that shouldn't be tested
|
|
24
|
+
*/
|
|
25
|
+
async hideDynamicContent(page) {
|
|
26
|
+
await page.addStyleTag({
|
|
27
|
+
content: `
|
|
28
|
+
/* Hide common dynamic content */
|
|
29
|
+
.advertisement,
|
|
30
|
+
.ad-banner,
|
|
31
|
+
.analytics-widget,
|
|
32
|
+
.third-party-banner,
|
|
33
|
+
[class*="ad-"],
|
|
34
|
+
[id*="ad-"],
|
|
35
|
+
[class*="analytics"],
|
|
36
|
+
[id*="analytics"] {
|
|
37
|
+
display: none !important;
|
|
38
|
+
visibility: hidden !important;
|
|
39
|
+
}
|
|
40
|
+
`,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Take a screenshot and compare with baseline
|
|
45
|
+
*/
|
|
46
|
+
async captureAndCompare(page, options) {
|
|
47
|
+
const { ticketId, testName, threshold = 0.01, updateBaseline = false } = options;
|
|
48
|
+
// Ensure visual test directories exist
|
|
49
|
+
const baselineDir = join(this.cwd, '.kosuke', 'visual-tests', 'baselines', ticketId);
|
|
50
|
+
const currentDir = join(this.cwd, '.kosuke', 'visual-tests', 'current', ticketId);
|
|
51
|
+
const diffDir = join(this.cwd, '.kosuke', 'visual-tests', 'diffs', ticketId);
|
|
52
|
+
this.ensureDir(baselineDir);
|
|
53
|
+
this.ensureDir(currentDir);
|
|
54
|
+
this.ensureDir(diffDir);
|
|
55
|
+
const baselinePath = join(baselineDir, `${testName}.png`);
|
|
56
|
+
const currentPath = join(currentDir, `${testName}.png`);
|
|
57
|
+
const diffPath = join(diffDir, `${testName}.png`);
|
|
58
|
+
// Hide dynamic content before taking screenshot
|
|
59
|
+
await this.hideDynamicContent(page);
|
|
60
|
+
// Wait for page to be stable
|
|
61
|
+
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {
|
|
62
|
+
// Ignore timeout - some pages never reach networkidle
|
|
63
|
+
});
|
|
64
|
+
// Take screenshot
|
|
65
|
+
const screenshot = await page.screenshot({ fullPage: true });
|
|
66
|
+
writeFileSync(currentPath, screenshot);
|
|
67
|
+
// If no baseline exists or updateBaseline flag is set, create/update baseline
|
|
68
|
+
if (!existsSync(baselinePath) || updateBaseline) {
|
|
69
|
+
writeFileSync(baselinePath, screenshot);
|
|
70
|
+
console.log(` šø ${updateBaseline ? 'Updated' : 'Created'} baseline: ${testName}`);
|
|
71
|
+
const diff = {
|
|
72
|
+
testName,
|
|
73
|
+
diffPixels: 0,
|
|
74
|
+
diffPercentage: 0,
|
|
75
|
+
threshold,
|
|
76
|
+
passed: true,
|
|
77
|
+
baselinePath,
|
|
78
|
+
currentPath,
|
|
79
|
+
diffPath,
|
|
80
|
+
};
|
|
81
|
+
this.diffs.push(diff);
|
|
82
|
+
return diff;
|
|
83
|
+
}
|
|
84
|
+
// Compare with baseline
|
|
85
|
+
const result = this.compareImages(baselinePath, currentPath, diffPath, threshold);
|
|
86
|
+
this.diffs.push(result);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Compare two images and generate diff
|
|
91
|
+
*/
|
|
92
|
+
compareImages(baselinePath, currentPath, diffPath, threshold) {
|
|
93
|
+
const testName = baselinePath.split('/').pop()?.replace('.png', '') || 'unknown';
|
|
94
|
+
try {
|
|
95
|
+
const baseline = PNG.sync.read(readFileSync(baselinePath));
|
|
96
|
+
const current = PNG.sync.read(readFileSync(currentPath));
|
|
97
|
+
// Check if dimensions match
|
|
98
|
+
if (baseline.width !== current.width || baseline.height !== current.height) {
|
|
99
|
+
return {
|
|
100
|
+
testName,
|
|
101
|
+
diffPixels: baseline.width * baseline.height, // All pixels are different
|
|
102
|
+
diffPercentage: 1,
|
|
103
|
+
threshold,
|
|
104
|
+
passed: false,
|
|
105
|
+
baselinePath,
|
|
106
|
+
currentPath,
|
|
107
|
+
diffPath,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// Create diff image
|
|
111
|
+
const diff = new PNG({ width: baseline.width, height: baseline.height });
|
|
112
|
+
// Compare pixels
|
|
113
|
+
const diffPixels = pixelmatch(baseline.data, current.data, diff.data, baseline.width, baseline.height, { threshold: 0.1 } // Pixel sensitivity
|
|
114
|
+
);
|
|
115
|
+
const totalPixels = baseline.width * baseline.height;
|
|
116
|
+
const diffPercentage = diffPixels / totalPixels;
|
|
117
|
+
// Save diff image if there are differences
|
|
118
|
+
if (diffPixels > 0) {
|
|
119
|
+
writeFileSync(diffPath, PNG.sync.write(diff));
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
testName,
|
|
123
|
+
diffPixels,
|
|
124
|
+
diffPercentage,
|
|
125
|
+
threshold,
|
|
126
|
+
passed: diffPercentage <= threshold,
|
|
127
|
+
baselinePath,
|
|
128
|
+
currentPath,
|
|
129
|
+
diffPath,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error(` ā ļø Failed to compare images: ${error}`);
|
|
134
|
+
return {
|
|
135
|
+
testName,
|
|
136
|
+
diffPixels: 0,
|
|
137
|
+
diffPercentage: 0,
|
|
138
|
+
threshold,
|
|
139
|
+
passed: false,
|
|
140
|
+
baselinePath,
|
|
141
|
+
currentPath,
|
|
142
|
+
diffPath,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get all visual diffs
|
|
148
|
+
*/
|
|
149
|
+
getDiffs() {
|
|
150
|
+
return this.diffs;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get failed visual diffs
|
|
154
|
+
*/
|
|
155
|
+
getFailedDiffs() {
|
|
156
|
+
return this.diffs.filter((diff) => !diff.passed);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check if there are any visual regressions
|
|
160
|
+
*/
|
|
161
|
+
hasRegressions() {
|
|
162
|
+
return this.getFailedDiffs().length > 0;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Format visual diff report
|
|
166
|
+
*/
|
|
167
|
+
formatReport() {
|
|
168
|
+
if (this.diffs.length === 0) {
|
|
169
|
+
return 'No visual tests run';
|
|
170
|
+
}
|
|
171
|
+
const failed = this.getFailedDiffs();
|
|
172
|
+
const passed = this.diffs.filter((diff) => diff.passed);
|
|
173
|
+
const lines = [];
|
|
174
|
+
lines.push(`\nšø Visual Regression Report:`);
|
|
175
|
+
lines.push(` ā
Passed: ${passed.length}`);
|
|
176
|
+
lines.push(` ā Failed: ${failed.length}`);
|
|
177
|
+
if (failed.length > 0) {
|
|
178
|
+
lines.push('\n Failed tests:');
|
|
179
|
+
for (const diff of failed) {
|
|
180
|
+
const percentage = (diff.diffPercentage * 100).toFixed(2);
|
|
181
|
+
lines.push(` - ${diff.testName}: ${percentage}% difference (threshold: ${diff.threshold * 100}%)`);
|
|
182
|
+
lines.push(` Diff image: ${diff.diffPath}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return lines.join('\n');
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Clear all diffs
|
|
189
|
+
*/
|
|
190
|
+
clear() {
|
|
191
|
+
this.diffs = [];
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Ensure directory exists
|
|
195
|
+
*/
|
|
196
|
+
ensureDir(dir) {
|
|
197
|
+
if (!existsSync(dir)) {
|
|
198
|
+
mkdirSync(dir, { recursive: true });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=visual-tester.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-tester.js","sourceRoot":"","sources":["../../../kosuke/utils/visual-tester.ts"],"names":[],"mappings":"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"}
|