maskweaver 0.11.0 → 0.11.2
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/LICENSE +21 -21
- package/README.ko.md +640 -640
- package/README.md +672 -672
- package/assets/agents/dummy-human.md +31 -31
- package/assets/agents/dummy-template.md +57 -57
- package/assets/agents/mask-weaver.md +412 -412
- package/assets/agents/squad-operator.md +242 -242
- package/assets/masks/ai-ml/andrew-ng.yaml +207 -207
- package/assets/masks/architecture/jeff-dean.yaml +208 -208
- package/assets/masks/index.json +65 -65
- package/assets/masks/software-engineering/dan-abramov.yaml +188 -188
- package/assets/masks/software-engineering/kent-beck.yaml +191 -191
- package/assets/masks/software-engineering/linus-torvalds.yaml +152 -152
- package/assets/masks/software-engineering/martin-fowler.yaml +173 -173
- package/dist/memory/store/sqlite.js +102 -102
- package/dist/plugin/tools/context.js +15 -15
- package/dist/plugin/tools/maskSave.js +8 -8
- package/dist/plugin/tools/memoryIndexer.js +5 -5
- package/dist/plugin/tools/memorySearch.js +8 -8
- package/dist/plugin/tools/memoryWrite.js +3 -3
- package/dist/plugin/tools/retrospect.js +3 -3
- package/dist/plugin/tools/squad.js +39 -39
- package/dist/retrospect/mask-save.js +21 -21
- package/dist/retrospect/retrospect.js +9 -9
- package/dist/shared/generate-agents.d.ts +3 -15
- package/dist/shared/generate-agents.js +13 -172
- package/dist/shared/subscription-detection.d.ts +20 -0
- package/dist/shared/subscription-detection.js +162 -0
- package/dist/verify/prompts.js +114 -114
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/weave/knowledge/global.js +86 -86
- package/dist/weave/verification/playwright.js +127 -127
- package/masks/ai-ml/andrew-ng.yaml +207 -207
- package/masks/architecture/jeff-dean.yaml +208 -208
- package/masks/index.json +65 -65
- package/masks/orchestration/squad-operator.yaml +205 -205
- package/masks/software-engineering/dan-abramov.yaml +188 -188
- package/masks/software-engineering/kent-beck.yaml +191 -191
- package/masks/software-engineering/linus-torvalds.yaml +152 -152
- package/masks/software-engineering/martin-fowler.yaml +173 -173
- package/package.json +1 -1
- package/postinstall.mjs +22 -112
|
@@ -153,60 +153,60 @@ export class GlobalKnowledge {
|
|
|
153
153
|
createTables() {
|
|
154
154
|
if (!this.db)
|
|
155
155
|
return;
|
|
156
|
-
this.db.exec(`
|
|
157
|
-
CREATE TABLE IF NOT EXISTS troubleshooting (
|
|
158
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
159
|
-
error_signature TEXT NOT NULL,
|
|
160
|
-
error_message TEXT NOT NULL,
|
|
161
|
-
context TEXT NOT NULL,
|
|
162
|
-
solution TEXT NOT NULL,
|
|
163
|
-
project_type TEXT,
|
|
164
|
-
tech_stack TEXT,
|
|
165
|
-
tags TEXT,
|
|
166
|
-
effectiveness INTEGER DEFAULT 5,
|
|
167
|
-
created_at TEXT NOT NULL,
|
|
168
|
-
used_count INTEGER DEFAULT 0,
|
|
169
|
-
last_used_at TEXT
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
CREATE INDEX IF NOT EXISTS idx_error_signature
|
|
173
|
-
ON troubleshooting(error_signature);
|
|
174
|
-
|
|
175
|
-
CREATE INDEX IF NOT EXISTS idx_project_type
|
|
176
|
-
ON troubleshooting(project_type);
|
|
156
|
+
this.db.exec(`
|
|
157
|
+
CREATE TABLE IF NOT EXISTS troubleshooting (
|
|
158
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
159
|
+
error_signature TEXT NOT NULL,
|
|
160
|
+
error_message TEXT NOT NULL,
|
|
161
|
+
context TEXT NOT NULL,
|
|
162
|
+
solution TEXT NOT NULL,
|
|
163
|
+
project_type TEXT,
|
|
164
|
+
tech_stack TEXT,
|
|
165
|
+
tags TEXT,
|
|
166
|
+
effectiveness INTEGER DEFAULT 5,
|
|
167
|
+
created_at TEXT NOT NULL,
|
|
168
|
+
used_count INTEGER DEFAULT 0,
|
|
169
|
+
last_used_at TEXT
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
CREATE INDEX IF NOT EXISTS idx_error_signature
|
|
173
|
+
ON troubleshooting(error_signature);
|
|
174
|
+
|
|
175
|
+
CREATE INDEX IF NOT EXISTS idx_project_type
|
|
176
|
+
ON troubleshooting(project_type);
|
|
177
177
|
`);
|
|
178
178
|
if (this.usingSqlJs) {
|
|
179
179
|
this.ftsAvailable = false;
|
|
180
180
|
}
|
|
181
181
|
else {
|
|
182
182
|
try {
|
|
183
|
-
this.db.exec(`
|
|
184
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS troubleshooting_fts USING fts5(
|
|
185
|
-
error_message,
|
|
186
|
-
context,
|
|
187
|
-
solution,
|
|
188
|
-
tags,
|
|
189
|
-
content='troubleshooting',
|
|
190
|
-
content_rowid='id'
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
-- Triggers to keep FTS in sync
|
|
194
|
-
CREATE TRIGGER IF NOT EXISTS troubleshooting_ai AFTER INSERT ON troubleshooting BEGIN
|
|
195
|
-
INSERT INTO troubleshooting_fts(rowid, error_message, context, solution, tags)
|
|
196
|
-
VALUES (new.id, new.error_message, new.context, new.solution, new.tags);
|
|
197
|
-
END;
|
|
198
|
-
|
|
199
|
-
CREATE TRIGGER IF NOT EXISTS troubleshooting_ad AFTER DELETE ON troubleshooting BEGIN
|
|
200
|
-
INSERT INTO troubleshooting_fts(troubleshooting_fts, rowid, error_message, context, solution, tags)
|
|
201
|
-
VALUES ('delete', old.id, old.error_message, old.context, old.solution, old.tags);
|
|
202
|
-
END;
|
|
203
|
-
|
|
204
|
-
CREATE TRIGGER IF NOT EXISTS troubleshooting_au AFTER UPDATE ON troubleshooting BEGIN
|
|
205
|
-
INSERT INTO troubleshooting_fts(troubleshooting_fts, rowid, error_message, context, solution, tags)
|
|
206
|
-
VALUES ('delete', old.id, old.error_message, old.context, old.solution, old.tags);
|
|
207
|
-
INSERT INTO troubleshooting_fts(rowid, error_message, context, solution, tags)
|
|
208
|
-
VALUES (new.id, new.error_message, new.context, new.solution, new.tags);
|
|
209
|
-
END;
|
|
183
|
+
this.db.exec(`
|
|
184
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS troubleshooting_fts USING fts5(
|
|
185
|
+
error_message,
|
|
186
|
+
context,
|
|
187
|
+
solution,
|
|
188
|
+
tags,
|
|
189
|
+
content='troubleshooting',
|
|
190
|
+
content_rowid='id'
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
-- Triggers to keep FTS in sync
|
|
194
|
+
CREATE TRIGGER IF NOT EXISTS troubleshooting_ai AFTER INSERT ON troubleshooting BEGIN
|
|
195
|
+
INSERT INTO troubleshooting_fts(rowid, error_message, context, solution, tags)
|
|
196
|
+
VALUES (new.id, new.error_message, new.context, new.solution, new.tags);
|
|
197
|
+
END;
|
|
198
|
+
|
|
199
|
+
CREATE TRIGGER IF NOT EXISTS troubleshooting_ad AFTER DELETE ON troubleshooting BEGIN
|
|
200
|
+
INSERT INTO troubleshooting_fts(troubleshooting_fts, rowid, error_message, context, solution, tags)
|
|
201
|
+
VALUES ('delete', old.id, old.error_message, old.context, old.solution, old.tags);
|
|
202
|
+
END;
|
|
203
|
+
|
|
204
|
+
CREATE TRIGGER IF NOT EXISTS troubleshooting_au AFTER UPDATE ON troubleshooting BEGIN
|
|
205
|
+
INSERT INTO troubleshooting_fts(troubleshooting_fts, rowid, error_message, context, solution, tags)
|
|
206
|
+
VALUES ('delete', old.id, old.error_message, old.context, old.solution, old.tags);
|
|
207
|
+
INSERT INTO troubleshooting_fts(rowid, error_message, context, solution, tags)
|
|
208
|
+
VALUES (new.id, new.error_message, new.context, new.solution, new.tags);
|
|
209
|
+
END;
|
|
210
210
|
`);
|
|
211
211
|
this.ftsAvailable = true;
|
|
212
212
|
}
|
|
@@ -223,12 +223,12 @@ export class GlobalKnowledge {
|
|
|
223
223
|
}
|
|
224
224
|
const signature = normalizeErrorSignature(entry.errorMessage);
|
|
225
225
|
const now = new Date().toISOString();
|
|
226
|
-
const stmt = this.db.prepare(`
|
|
227
|
-
INSERT INTO troubleshooting (
|
|
228
|
-
error_signature, error_message, context, solution,
|
|
229
|
-
project_type, tech_stack, tags, effectiveness,
|
|
230
|
-
created_at, used_count
|
|
231
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0)
|
|
226
|
+
const stmt = this.db.prepare(`
|
|
227
|
+
INSERT INTO troubleshooting (
|
|
228
|
+
error_signature, error_message, context, solution,
|
|
229
|
+
project_type, tech_stack, tags, effectiveness,
|
|
230
|
+
created_at, used_count
|
|
231
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0)
|
|
232
232
|
`);
|
|
233
233
|
const result = stmt.run(signature, entry.errorMessage, entry.context, entry.solution, entry.projectType || null, entry.techStack ? JSON.stringify(entry.techStack) : null, entry.tags ? JSON.stringify(entry.tags) : null, entry.effectiveness || 5, now);
|
|
234
234
|
return result.lastInsertRowid;
|
|
@@ -240,12 +240,12 @@ export class GlobalKnowledge {
|
|
|
240
240
|
const { projectType, limit = 5, minEffectiveness = 3 } = options;
|
|
241
241
|
const signature = normalizeErrorSignature(errorMessage);
|
|
242
242
|
const results = [];
|
|
243
|
-
const exactStmt = this.db.prepare(`
|
|
244
|
-
SELECT * FROM troubleshooting
|
|
245
|
-
WHERE error_signature = ?
|
|
246
|
-
AND effectiveness >= ?
|
|
247
|
-
ORDER BY effectiveness DESC, used_count DESC
|
|
248
|
-
LIMIT ?
|
|
243
|
+
const exactStmt = this.db.prepare(`
|
|
244
|
+
SELECT * FROM troubleshooting
|
|
245
|
+
WHERE error_signature = ?
|
|
246
|
+
AND effectiveness >= ?
|
|
247
|
+
ORDER BY effectiveness DESC, used_count DESC
|
|
248
|
+
LIMIT ?
|
|
249
249
|
`);
|
|
250
250
|
const exactMatches = exactStmt.all(signature, minEffectiveness, limit);
|
|
251
251
|
for (const row of exactMatches) {
|
|
@@ -266,16 +266,16 @@ export class GlobalKnowledge {
|
|
|
266
266
|
.join(' OR ');
|
|
267
267
|
if (searchTerms) {
|
|
268
268
|
try {
|
|
269
|
-
const ftsStmt = this.db.prepare(`
|
|
270
|
-
SELECT t.*, bm25(troubleshooting_fts) as rank
|
|
271
|
-
FROM troubleshooting_fts f
|
|
272
|
-
JOIN troubleshooting t ON f.rowid = t.id
|
|
273
|
-
WHERE troubleshooting_fts MATCH ?
|
|
274
|
-
AND t.effectiveness >= ?
|
|
275
|
-
${existingIds.length > 0 ? `AND t.id NOT IN (${existingIds.join(',')})` : ''}
|
|
276
|
-
${projectType ? 'AND t.project_type = ?' : ''}
|
|
277
|
-
ORDER BY rank
|
|
278
|
-
LIMIT ?
|
|
269
|
+
const ftsStmt = this.db.prepare(`
|
|
270
|
+
SELECT t.*, bm25(troubleshooting_fts) as rank
|
|
271
|
+
FROM troubleshooting_fts f
|
|
272
|
+
JOIN troubleshooting t ON f.rowid = t.id
|
|
273
|
+
WHERE troubleshooting_fts MATCH ?
|
|
274
|
+
AND t.effectiveness >= ?
|
|
275
|
+
${existingIds.length > 0 ? `AND t.id NOT IN (${existingIds.join(',')})` : ''}
|
|
276
|
+
${projectType ? 'AND t.project_type = ?' : ''}
|
|
277
|
+
ORDER BY rank
|
|
278
|
+
LIMIT ?
|
|
279
279
|
`);
|
|
280
280
|
const params = projectType
|
|
281
281
|
? [searchTerms, minEffectiveness, projectType, remaining]
|
|
@@ -299,11 +299,11 @@ export class GlobalKnowledge {
|
|
|
299
299
|
await this.init();
|
|
300
300
|
if (!this.db)
|
|
301
301
|
return;
|
|
302
|
-
const stmt = this.db.prepare(`
|
|
303
|
-
UPDATE troubleshooting
|
|
304
|
-
SET used_count = used_count + 1,
|
|
305
|
-
last_used_at = ?
|
|
306
|
-
WHERE id = ?
|
|
302
|
+
const stmt = this.db.prepare(`
|
|
303
|
+
UPDATE troubleshooting
|
|
304
|
+
SET used_count = used_count + 1,
|
|
305
|
+
last_used_at = ?
|
|
306
|
+
WHERE id = ?
|
|
307
307
|
`);
|
|
308
308
|
stmt.run(new Date().toISOString(), id);
|
|
309
309
|
}
|
|
@@ -311,10 +311,10 @@ export class GlobalKnowledge {
|
|
|
311
311
|
await this.init();
|
|
312
312
|
if (!this.db)
|
|
313
313
|
return;
|
|
314
|
-
const stmt = this.db.prepare(`
|
|
315
|
-
UPDATE troubleshooting
|
|
316
|
-
SET effectiveness = ?
|
|
317
|
-
WHERE id = ?
|
|
314
|
+
const stmt = this.db.prepare(`
|
|
315
|
+
UPDATE troubleshooting
|
|
316
|
+
SET effectiveness = ?
|
|
317
|
+
WHERE id = ?
|
|
318
318
|
`);
|
|
319
319
|
stmt.run(Math.max(1, Math.min(10, effectiveness)), id);
|
|
320
320
|
}
|
|
@@ -330,13 +330,13 @@ export class GlobalKnowledge {
|
|
|
330
330
|
}
|
|
331
331
|
const totalStmt = this.db.prepare('SELECT COUNT(*) as count FROM troubleshooting');
|
|
332
332
|
const total = totalStmt.get();
|
|
333
|
-
const projectTypeStmt = this.db.prepare(`
|
|
334
|
-
SELECT project_type as type, COUNT(*) as count
|
|
335
|
-
FROM troubleshooting
|
|
336
|
-
WHERE project_type IS NOT NULL
|
|
337
|
-
GROUP BY project_type
|
|
338
|
-
ORDER BY count DESC
|
|
339
|
-
LIMIT 10
|
|
333
|
+
const projectTypeStmt = this.db.prepare(`
|
|
334
|
+
SELECT project_type as type, COUNT(*) as count
|
|
335
|
+
FROM troubleshooting
|
|
336
|
+
WHERE project_type IS NOT NULL
|
|
337
|
+
GROUP BY project_type
|
|
338
|
+
ORDER BY count DESC
|
|
339
|
+
LIMIT 10
|
|
340
340
|
`);
|
|
341
341
|
const projectTypes = projectTypeStmt.all();
|
|
342
342
|
const avgStmt = this.db.prepare('SELECT AVG(effectiveness) as avg FROM troubleshooting');
|
|
@@ -141,16 +141,16 @@ export async function runVisualRegressionTests(projectPath, options) {
|
|
|
141
141
|
export async function capturePageScreenshot(projectPath, url, outputPath) {
|
|
142
142
|
const screenshotPath = outputPath || path.join(projectPath, '.weave', 'screenshots', `capture-${Date.now()}.png`);
|
|
143
143
|
await fs.mkdir(path.dirname(screenshotPath), { recursive: true });
|
|
144
|
-
const script = `
|
|
145
|
-
const { chromium } = require('playwright');
|
|
146
|
-
(async () => {
|
|
147
|
-
const browser = await chromium.launch();
|
|
148
|
-
const page = await browser.newPage();
|
|
149
|
-
await page.goto('${url}');
|
|
150
|
-
await page.screenshot({ path: '${screenshotPath.replace(/\\/g, '/')}', fullPage: true });
|
|
151
|
-
await browser.close();
|
|
152
|
-
console.log('Screenshot saved to: ${screenshotPath.replace(/\\/g, '/')}');
|
|
153
|
-
})();
|
|
144
|
+
const script = `
|
|
145
|
+
const { chromium } = require('playwright');
|
|
146
|
+
(async () => {
|
|
147
|
+
const browser = await chromium.launch();
|
|
148
|
+
const page = await browser.newPage();
|
|
149
|
+
await page.goto('${url}');
|
|
150
|
+
await page.screenshot({ path: '${screenshotPath.replace(/\\/g, '/')}', fullPage: true });
|
|
151
|
+
await browser.close();
|
|
152
|
+
console.log('Screenshot saved to: ${screenshotPath.replace(/\\/g, '/')}');
|
|
153
|
+
})();
|
|
154
154
|
`;
|
|
155
155
|
const scriptPath = path.join(projectPath, '.weave', 'temp-screenshot.js');
|
|
156
156
|
await fs.mkdir(path.dirname(scriptPath), { recursive: true });
|
|
@@ -334,126 +334,126 @@ export function analyzePlaywrightError(failure) {
|
|
|
334
334
|
return analysis;
|
|
335
335
|
}
|
|
336
336
|
function generatePlaywrightConfig(projectPath) {
|
|
337
|
-
return `import { defineConfig, devices } from '@playwright/test';
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Playwright E2E Test Configuration
|
|
341
|
-
* Generated by Maskweaver Weave
|
|
342
|
-
*
|
|
343
|
-
* @see https://playwright.dev/docs/test-configuration
|
|
344
|
-
*/
|
|
345
|
-
export default defineConfig({
|
|
346
|
-
// Test directory
|
|
347
|
-
testDir: './e2e',
|
|
348
|
-
|
|
349
|
-
// Run tests in parallel
|
|
350
|
-
fullyParallel: true,
|
|
351
|
-
|
|
352
|
-
// Fail the build on test.only in CI
|
|
353
|
-
forbidOnly: !!process.env.CI,
|
|
354
|
-
|
|
355
|
-
// Retry on CI only
|
|
356
|
-
retries: process.env.CI ? 2 : 0,
|
|
357
|
-
|
|
358
|
-
// Number of workers
|
|
359
|
-
workers: process.env.CI ? 1 : undefined,
|
|
360
|
-
|
|
361
|
-
// Reporter
|
|
362
|
-
reporter: [
|
|
363
|
-
['html', { open: 'never' }],
|
|
364
|
-
['json', { outputFile: 'test-results/results.json' }],
|
|
365
|
-
],
|
|
366
|
-
|
|
367
|
-
// Shared settings for all projects
|
|
368
|
-
use: {
|
|
369
|
-
// Base URL for navigation
|
|
370
|
-
baseURL: 'http://localhost:3000',
|
|
371
|
-
|
|
372
|
-
// Collect trace when retrying
|
|
373
|
-
trace: 'on-first-retry',
|
|
374
|
-
|
|
375
|
-
// Screenshot on failure
|
|
376
|
-
screenshot: 'only-on-failure',
|
|
377
|
-
|
|
378
|
-
// Record video on failure
|
|
379
|
-
video: 'on-first-retry',
|
|
380
|
-
},
|
|
381
|
-
|
|
382
|
-
// Configure projects for browsers
|
|
383
|
-
projects: [
|
|
384
|
-
{
|
|
385
|
-
name: 'chromium',
|
|
386
|
-
use: { ...devices['Desktop Chrome'] },
|
|
387
|
-
},
|
|
388
|
-
// Uncomment for additional browsers
|
|
389
|
-
// {
|
|
390
|
-
// name: 'firefox',
|
|
391
|
-
// use: { ...devices['Desktop Firefox'] },
|
|
392
|
-
// },
|
|
393
|
-
// {
|
|
394
|
-
// name: 'webkit',
|
|
395
|
-
// use: { ...devices['Desktop Safari'] },
|
|
396
|
-
// },
|
|
397
|
-
// Mobile testing
|
|
398
|
-
// {
|
|
399
|
-
// name: 'mobile-chrome',
|
|
400
|
-
// use: { ...devices['Pixel 5'] },
|
|
401
|
-
// },
|
|
402
|
-
],
|
|
403
|
-
|
|
404
|
-
// Run local dev server before tests
|
|
405
|
-
webServer: {
|
|
406
|
-
command: 'npm run dev',
|
|
407
|
-
url: 'http://localhost:3000',
|
|
408
|
-
reuseExistingServer: !process.env.CI,
|
|
409
|
-
timeout: 120 * 1000,
|
|
410
|
-
},
|
|
411
|
-
});
|
|
337
|
+
return `import { defineConfig, devices } from '@playwright/test';
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Playwright E2E Test Configuration
|
|
341
|
+
* Generated by Maskweaver Weave
|
|
342
|
+
*
|
|
343
|
+
* @see https://playwright.dev/docs/test-configuration
|
|
344
|
+
*/
|
|
345
|
+
export default defineConfig({
|
|
346
|
+
// Test directory
|
|
347
|
+
testDir: './e2e',
|
|
348
|
+
|
|
349
|
+
// Run tests in parallel
|
|
350
|
+
fullyParallel: true,
|
|
351
|
+
|
|
352
|
+
// Fail the build on test.only in CI
|
|
353
|
+
forbidOnly: !!process.env.CI,
|
|
354
|
+
|
|
355
|
+
// Retry on CI only
|
|
356
|
+
retries: process.env.CI ? 2 : 0,
|
|
357
|
+
|
|
358
|
+
// Number of workers
|
|
359
|
+
workers: process.env.CI ? 1 : undefined,
|
|
360
|
+
|
|
361
|
+
// Reporter
|
|
362
|
+
reporter: [
|
|
363
|
+
['html', { open: 'never' }],
|
|
364
|
+
['json', { outputFile: 'test-results/results.json' }],
|
|
365
|
+
],
|
|
366
|
+
|
|
367
|
+
// Shared settings for all projects
|
|
368
|
+
use: {
|
|
369
|
+
// Base URL for navigation
|
|
370
|
+
baseURL: 'http://localhost:3000',
|
|
371
|
+
|
|
372
|
+
// Collect trace when retrying
|
|
373
|
+
trace: 'on-first-retry',
|
|
374
|
+
|
|
375
|
+
// Screenshot on failure
|
|
376
|
+
screenshot: 'only-on-failure',
|
|
377
|
+
|
|
378
|
+
// Record video on failure
|
|
379
|
+
video: 'on-first-retry',
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
// Configure projects for browsers
|
|
383
|
+
projects: [
|
|
384
|
+
{
|
|
385
|
+
name: 'chromium',
|
|
386
|
+
use: { ...devices['Desktop Chrome'] },
|
|
387
|
+
},
|
|
388
|
+
// Uncomment for additional browsers
|
|
389
|
+
// {
|
|
390
|
+
// name: 'firefox',
|
|
391
|
+
// use: { ...devices['Desktop Firefox'] },
|
|
392
|
+
// },
|
|
393
|
+
// {
|
|
394
|
+
// name: 'webkit',
|
|
395
|
+
// use: { ...devices['Desktop Safari'] },
|
|
396
|
+
// },
|
|
397
|
+
// Mobile testing
|
|
398
|
+
// {
|
|
399
|
+
// name: 'mobile-chrome',
|
|
400
|
+
// use: { ...devices['Pixel 5'] },
|
|
401
|
+
// },
|
|
402
|
+
],
|
|
403
|
+
|
|
404
|
+
// Run local dev server before tests
|
|
405
|
+
webServer: {
|
|
406
|
+
command: 'npm run dev',
|
|
407
|
+
url: 'http://localhost:3000',
|
|
408
|
+
reuseExistingServer: !process.env.CI,
|
|
409
|
+
timeout: 120 * 1000,
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
412
|
`;
|
|
413
413
|
}
|
|
414
414
|
function generateExampleTest() {
|
|
415
|
-
return `import { test, expect } from '@playwright/test';
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Example E2E Test
|
|
419
|
-
* Generated by Maskweaver Weave
|
|
420
|
-
*
|
|
421
|
-
* Run with: npx playwright test
|
|
422
|
-
*/
|
|
423
|
-
|
|
424
|
-
test.describe('Application', () => {
|
|
425
|
-
test('should load home page', async ({ page }) => {
|
|
426
|
-
// Navigate to home
|
|
427
|
-
await page.goto('/');
|
|
428
|
-
|
|
429
|
-
// Check if page loaded
|
|
430
|
-
await expect(page).toHaveTitle(/.+/);
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
test('should have working navigation', async ({ page }) => {
|
|
434
|
-
await page.goto('/');
|
|
435
|
-
|
|
436
|
-
// Example: check for a navigation element
|
|
437
|
-
const nav = page.locator('nav');
|
|
438
|
-
// await expect(nav).toBeVisible();
|
|
439
|
-
});
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
test.describe('Visual Regression', () => {
|
|
443
|
-
test('home page screenshot', async ({ page }) => {
|
|
444
|
-
await page.goto('/');
|
|
445
|
-
|
|
446
|
-
// Wait for any animations to complete
|
|
447
|
-
await page.waitForTimeout(1000);
|
|
448
|
-
|
|
449
|
-
// Take a screenshot for visual comparison
|
|
450
|
-
await expect(page).toHaveScreenshot('home.png', {
|
|
451
|
-
fullPage: true,
|
|
452
|
-
// Allow 0.2% pixel difference for anti-aliasing
|
|
453
|
-
maxDiffPixelRatio: 0.002,
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
});
|
|
415
|
+
return `import { test, expect } from '@playwright/test';
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Example E2E Test
|
|
419
|
+
* Generated by Maskweaver Weave
|
|
420
|
+
*
|
|
421
|
+
* Run with: npx playwright test
|
|
422
|
+
*/
|
|
423
|
+
|
|
424
|
+
test.describe('Application', () => {
|
|
425
|
+
test('should load home page', async ({ page }) => {
|
|
426
|
+
// Navigate to home
|
|
427
|
+
await page.goto('/');
|
|
428
|
+
|
|
429
|
+
// Check if page loaded
|
|
430
|
+
await expect(page).toHaveTitle(/.+/);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test('should have working navigation', async ({ page }) => {
|
|
434
|
+
await page.goto('/');
|
|
435
|
+
|
|
436
|
+
// Example: check for a navigation element
|
|
437
|
+
const nav = page.locator('nav');
|
|
438
|
+
// await expect(nav).toBeVisible();
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test.describe('Visual Regression', () => {
|
|
443
|
+
test('home page screenshot', async ({ page }) => {
|
|
444
|
+
await page.goto('/');
|
|
445
|
+
|
|
446
|
+
// Wait for any animations to complete
|
|
447
|
+
await page.waitForTimeout(1000);
|
|
448
|
+
|
|
449
|
+
// Take a screenshot for visual comparison
|
|
450
|
+
await expect(page).toHaveScreenshot('home.png', {
|
|
451
|
+
fullPage: true,
|
|
452
|
+
// Allow 0.2% pixel difference for anti-aliasing
|
|
453
|
+
maxDiffPixelRatio: 0.002,
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
457
|
`;
|
|
458
458
|
}
|
|
459
459
|
async function runCommand(command, args, cwd) {
|