@_deep4wee/agent-lens 1.0.1
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 -0
- package/README.md +221 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1853 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +1924 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +170 -0
- package/dist/index.d.ts +170 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +19 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
- package/scripts/postinstall.js +47 -0
- package/skills/agent-lens/SKILL.md +200 -0
- package/skills/agent-lens/examples/01-instant-verification-snap.md +53 -0
- package/skills/agent-lens/examples/02-dev-server-live-testing.md +62 -0
- package/skills/agent-lens/examples/03-component-isolation-and-animations.md +53 -0
- package/skills/agent-lens/examples/04-desktop-native-testing.md +68 -0
- package/skills/agent-lens/examples/05-clean-teardown-and-sandboxing.md +68 -0
- package/skills/agent-lens/examples/06-state-testing-with-mock-ipc.md +62 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Page, BrowserContext } from 'playwright';
|
|
2
|
+
|
|
3
|
+
interface ConsoleEntry {
|
|
4
|
+
level: 'error' | 'warning' | 'info' | 'log' | 'debug';
|
|
5
|
+
text: string;
|
|
6
|
+
url: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
stack?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generic Mock IPC Registry for Preview Mode
|
|
13
|
+
*
|
|
14
|
+
* Injects a global `window.__mockIpc` that web apps or mocked electron/tauri APIs can call.
|
|
15
|
+
*/
|
|
16
|
+
type MockIpcResponseType = 'SUCCESS' | 'ERROR';
|
|
17
|
+
|
|
18
|
+
interface ViewportPreset {
|
|
19
|
+
name: string;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
declare const VIEWPORT_PRESETS: {
|
|
24
|
+
/** Minimum supported window size (e.g., ) */
|
|
25
|
+
MIN_SUPPORTED: ViewportPreset;
|
|
26
|
+
/** Standard default window size (e.g., ) */
|
|
27
|
+
DEFAULT: ViewportPreset;
|
|
28
|
+
/** Wide screen for checking grids and tables */
|
|
29
|
+
WIDE: ViewportPreset;
|
|
30
|
+
/** Full HD */
|
|
31
|
+
FULL_HD: ViewportPreset;
|
|
32
|
+
};
|
|
33
|
+
interface CaptureOptions {
|
|
34
|
+
selector?: string;
|
|
35
|
+
fullPage?: boolean;
|
|
36
|
+
mask?: string[];
|
|
37
|
+
}
|
|
38
|
+
interface CaptureBurstOptions {
|
|
39
|
+
/** Total duration of burst animation capture in milliseconds */
|
|
40
|
+
durationMs: number;
|
|
41
|
+
/** Interval between frames in milliseconds (default: 80ms) */
|
|
42
|
+
intervalMs?: number;
|
|
43
|
+
/** Restrict capture to a specific element */
|
|
44
|
+
selector?: string;
|
|
45
|
+
}
|
|
46
|
+
interface SnapshotMetadata {
|
|
47
|
+
index: number;
|
|
48
|
+
name: string;
|
|
49
|
+
fileName: string;
|
|
50
|
+
filePath: string;
|
|
51
|
+
relativeUri: string;
|
|
52
|
+
viewport: {
|
|
53
|
+
width: number;
|
|
54
|
+
height: number;
|
|
55
|
+
};
|
|
56
|
+
timestamp: string;
|
|
57
|
+
isBurstFrame?: boolean;
|
|
58
|
+
burstGroup?: string;
|
|
59
|
+
frameIndex?: number;
|
|
60
|
+
selector?: string;
|
|
61
|
+
}
|
|
62
|
+
interface TestContext {
|
|
63
|
+
page: Page;
|
|
64
|
+
context: BrowserContext;
|
|
65
|
+
targetMode: 'desktop' | 'preview';
|
|
66
|
+
currentViewport: {
|
|
67
|
+
width: number;
|
|
68
|
+
height: number;
|
|
69
|
+
};
|
|
70
|
+
/** Capture a single high-quality snapshot of the window or element */
|
|
71
|
+
capture: (name: string, options?: CaptureOptions) => Promise<SnapshotMetadata>;
|
|
72
|
+
/** Capture a series of frames for animations (Framer Motion, modals, lists) */
|
|
73
|
+
captureBurst: (name: string, options: CaptureBurstOptions) => Promise<SnapshotMetadata[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Dynamically navigate to another page during the test.
|
|
76
|
+
* Example: await ctx.navigate('/settings');
|
|
77
|
+
*/
|
|
78
|
+
navigate: (route: string) => Promise<void>;
|
|
79
|
+
/** Change window / viewport size */
|
|
80
|
+
resize: (width: number, height: number) => Promise<void>;
|
|
81
|
+
/** Set one of the standard presets (MIN_SUPPORTED, DEFAULT, WIDE) */
|
|
82
|
+
setPreset: (preset: ViewportPreset) => Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Dynamically resize the window so it perfectly fits the specified element (or body).
|
|
85
|
+
* Useful when an agent wants to test an isolated component without background clutter.
|
|
86
|
+
*/
|
|
87
|
+
resizeToFit: (selector?: string, padding?: number) => Promise<void>;
|
|
88
|
+
/** Wait the specified amount of milliseconds */
|
|
89
|
+
wait: (ms: number) => Promise<void>;
|
|
90
|
+
/** Wait for the selector to appear in the DOM */
|
|
91
|
+
waitForSelector: (selector: string, timeoutMs?: number) => Promise<void>;
|
|
92
|
+
/** Left-click the element */
|
|
93
|
+
click: (selector: string) => Promise<void>;
|
|
94
|
+
/** Right-click the element (Context Menu) */
|
|
95
|
+
rightClick: (selector: string) => Promise<void>;
|
|
96
|
+
/** Type text into an input field */
|
|
97
|
+
type: (selector: string, text: string) => Promise<void>;
|
|
98
|
+
/** Select an option in a <select> by its value */
|
|
99
|
+
selectOption: (selector: string, value: string) => Promise<void>;
|
|
100
|
+
/** Hover the cursor to check highlights / tooltips */
|
|
101
|
+
hover: (selector: string) => Promise<void>;
|
|
102
|
+
/** Scroll the element (or window) down by the specified number of pixels */
|
|
103
|
+
scroll: (selector: string, deltaY: number) => Promise<void>;
|
|
104
|
+
/** Log a step for the agent */
|
|
105
|
+
log: (message: string) => void;
|
|
106
|
+
/**
|
|
107
|
+
* Set a mock response for an IPC action.
|
|
108
|
+
* Changes the data returned by window.external.sendMessage in preview mode.
|
|
109
|
+
* Ignored in desktop mode (IPC goes through real backend).
|
|
110
|
+
*
|
|
111
|
+
* Example:
|
|
112
|
+
* await ctx.setMockIpc('GET_INSTANCES', [{ id: '...', name: 'Test' }]);
|
|
113
|
+
* await ctx.setMockIpc('CREATE_INSTANCE', 'Some error', { type: 'ERROR' });
|
|
114
|
+
*/
|
|
115
|
+
setMockIpc: (action: string, data: any, options?: {
|
|
116
|
+
type?: MockIpcResponseType;
|
|
117
|
+
delayMs?: number;
|
|
118
|
+
}) => Promise<void>;
|
|
119
|
+
/** Return all intercepted console.error and pageerror logs */
|
|
120
|
+
getConsoleErrors: () => ConsoleEntry[];
|
|
121
|
+
/** Return all intercepted console.warn logs */
|
|
122
|
+
getConsoleWarnings: () => ConsoleEntry[];
|
|
123
|
+
/** Returns true if there are critical errors in the console */
|
|
124
|
+
hasConsoleErrors: () => boolean;
|
|
125
|
+
/** Get the visible text of a specific element */
|
|
126
|
+
readText: (selector: string) => Promise<string | null>;
|
|
127
|
+
/** Get ALL visible text on the page (useful for quick analysis without Vision) */
|
|
128
|
+
getPageText: () => Promise<string>;
|
|
129
|
+
/** Check if the element is visible on the screen */
|
|
130
|
+
isVisible: (selector: string) => Promise<boolean>;
|
|
131
|
+
/** Count the number of elements matching the selector */
|
|
132
|
+
getElementCount: (selector: string) => Promise<number>;
|
|
133
|
+
}
|
|
134
|
+
interface VisualScenario {
|
|
135
|
+
/** Unique scenario identifier (kebab-case) */
|
|
136
|
+
id: string;
|
|
137
|
+
/** Human-readable title for the report */
|
|
138
|
+
title: string;
|
|
139
|
+
/** Description of what is being tested */
|
|
140
|
+
description?: string;
|
|
141
|
+
/** Initial route to navigate to (e.g., '/instances', '/settings') */
|
|
142
|
+
route?: string;
|
|
143
|
+
/** List of viewport sizes to test */
|
|
144
|
+
viewports?: ViewportPreset[];
|
|
145
|
+
/**
|
|
146
|
+
* Initial IPC mock data for preview mode.
|
|
147
|
+
* Applied BEFORE navigation to the route.
|
|
148
|
+
*/
|
|
149
|
+
mockIpc?: Array<{
|
|
150
|
+
action: string;
|
|
151
|
+
data: any;
|
|
152
|
+
type?: MockIpcResponseType;
|
|
153
|
+
delayMs?: number;
|
|
154
|
+
}>;
|
|
155
|
+
/**
|
|
156
|
+
* Optional setup hook: executed before the browser navigates and scenario runs.
|
|
157
|
+
* Ideal for preparing mock files, test directories, or initial state.
|
|
158
|
+
*/
|
|
159
|
+
setup?: () => Promise<void> | void;
|
|
160
|
+
/** The main body of the scenario */
|
|
161
|
+
run: (ctx: TestContext) => Promise<void>;
|
|
162
|
+
/**
|
|
163
|
+
* Optional teardown hook: guaranteed to execute in finally, even if run() fails.
|
|
164
|
+
* Ideal for cleaning up test artifacts, cache directories, or temporary state.
|
|
165
|
+
*/
|
|
166
|
+
teardown?: () => Promise<void> | void;
|
|
167
|
+
}
|
|
168
|
+
declare function defineVisualTest(scenario: VisualScenario): VisualScenario;
|
|
169
|
+
|
|
170
|
+
export { type CaptureBurstOptions, type CaptureOptions, type SnapshotMetadata, type TestContext, VIEWPORT_PRESETS, type ViewportPreset, type VisualScenario, defineVisualTest };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/shared/api/dsl.ts
|
|
21
|
+
var dsl_exports = {};
|
|
22
|
+
__export(dsl_exports, {
|
|
23
|
+
VIEWPORT_PRESETS: () => VIEWPORT_PRESETS,
|
|
24
|
+
defineVisualTest: () => defineVisualTest
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(dsl_exports);
|
|
27
|
+
var VIEWPORT_PRESETS = {
|
|
28
|
+
/** Minimum supported window size (e.g., ) */
|
|
29
|
+
MIN_SUPPORTED: { name: "min-supported", width: 1024, height: 768 },
|
|
30
|
+
/** Standard default window size (e.g., ) */
|
|
31
|
+
DEFAULT: { name: "default", width: 1200, height: 800 },
|
|
32
|
+
/** Wide screen for checking grids and tables */
|
|
33
|
+
WIDE: { name: "wide", width: 1600, height: 900 },
|
|
34
|
+
/** Full HD */
|
|
35
|
+
FULL_HD: { name: "full-hd", width: 1920, height: 1080 }
|
|
36
|
+
};
|
|
37
|
+
function defineVisualTest(scenario) {
|
|
38
|
+
return scenario;
|
|
39
|
+
}
|
|
40
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
41
|
+
0 && (module.exports = {
|
|
42
|
+
VIEWPORT_PRESETS,
|
|
43
|
+
defineVisualTest
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/api/dsl.ts"],"sourcesContent":["import type { Page, BrowserContext } from 'playwright';\nimport type { ConsoleEntry } from '../../features/console-tracker/consoleTracker';\nimport type { MockIpcResponseType } from '../../features/mock-ipc/mockIpc';\n\nexport interface ViewportPreset {\n name: string;\n width: number;\n height: number;\n}\n\nexport const VIEWPORT_PRESETS = {\n /** Minimum supported window size (e.g., ) */\n MIN_SUPPORTED: { name: 'min-supported', width: 1024, height: 768 } as ViewportPreset,\n /** Standard default window size (e.g., ) */\n DEFAULT: { name: 'default', width: 1200, height: 800 } as ViewportPreset,\n /** Wide screen for checking grids and tables */\n WIDE: { name: 'wide', width: 1600, height: 900 } as ViewportPreset,\n /** Full HD */\n FULL_HD: { name: 'full-hd', width: 1920, height: 1080 } as ViewportPreset,\n};\n\nexport interface CaptureOptions {\n selector?: string;\n fullPage?: boolean;\n mask?: string[];\n}\n\nexport interface CaptureBurstOptions {\n /** Total duration of burst animation capture in milliseconds */\n durationMs: number;\n /** Interval between frames in milliseconds (default: 80ms) */\n intervalMs?: number;\n /** Restrict capture to a specific element */\n selector?: string;\n}\n\nexport interface SnapshotMetadata {\n index: number;\n name: string;\n fileName: string;\n filePath: string;\n relativeUri: string;\n viewport: { width: number; height: number };\n timestamp: string;\n isBurstFrame?: boolean;\n burstGroup?: string;\n frameIndex?: number;\n selector?: string;\n}\n\nexport interface TestContext {\n page: Page;\n context: BrowserContext;\n targetMode: 'desktop' | 'preview';\n currentViewport: { width: number; height: number };\n \n // --- Snapshots ---\n\n /** Capture a single high-quality snapshot of the window or element */\n capture: (name: string, options?: CaptureOptions) => Promise<SnapshotMetadata>;\n \n /** Capture a series of frames for animations (Framer Motion, modals, lists) */\n captureBurst: (name: string, options: CaptureBurstOptions) => Promise<SnapshotMetadata[]>;\n \n // --- Navigation ---\n\n /** \n * Dynamically navigate to another page during the test.\n * Example: await ctx.navigate('/settings');\n */\n navigate: (route: string) => Promise<void>;\n\n // --- Viewport ---\n\n /** Change window / viewport size */\n resize: (width: number, height: number) => Promise<void>;\n \n /** Set one of the standard presets (MIN_SUPPORTED, DEFAULT, WIDE) */\n setPreset: (preset: ViewportPreset) => Promise<void>;\n\n /** \n * Dynamically resize the window so it perfectly fits the specified element (or body).\n * Useful when an agent wants to test an isolated component without background clutter.\n */\n resizeToFit: (selector?: string, padding?: number) => Promise<void>;\n \n // --- Waiting ---\n\n /** Wait the specified amount of milliseconds */\n wait: (ms: number) => Promise<void>;\n \n /** Wait for the selector to appear in the DOM */\n waitForSelector: (selector: string, timeoutMs?: number) => Promise<void>;\n \n // --- Interaction ---\n\n /** Left-click the element */\n click: (selector: string) => Promise<void>;\n \n /** Right-click the element (Context Menu) */\n rightClick: (selector: string) => Promise<void>;\n \n /** Type text into an input field */\n type: (selector: string, text: string) => Promise<void>;\n \n /** Select an option in a <select> by its value */\n selectOption: (selector: string, value: string) => Promise<void>;\n \n /** Hover the cursor to check highlights / tooltips */\n hover: (selector: string) => Promise<void>;\n \n /** Scroll the element (or window) down by the specified number of pixels */\n scroll: (selector: string, deltaY: number) => Promise<void>;\n \n // --- Logging ---\n\n /** Log a step for the agent */\n log: (message: string) => void;\n\n // --- Mock IPC (preview mode only) ---\n\n /**\n * Set a mock response for an IPC action.\n * Changes the data returned by window.external.sendMessage in preview mode.\n * Ignored in desktop mode (IPC goes through real backend).\n *\n * Example:\n * await ctx.setMockIpc('GET_INSTANCES', [{ id: '...', name: 'Test' }]);\n * await ctx.setMockIpc('CREATE_INSTANCE', 'Some error', { type: 'ERROR' });\n */\n setMockIpc: (action: string, data: any, options?: { type?: MockIpcResponseType; delayMs?: number }) => Promise<void>;\n\n // --- Console Errors ---\n\n /** Return all intercepted console.error and pageerror logs */\n getConsoleErrors: () => ConsoleEntry[];\n\n /** Return all intercepted console.warn logs */\n getConsoleWarnings: () => ConsoleEntry[];\n\n /** Returns true if there are critical errors in the console */\n hasConsoleErrors: () => boolean;\n\n // --- DOM Assertions ---\n\n /** Get the visible text of a specific element */\n readText: (selector: string) => Promise<string | null>;\n \n /** Get ALL visible text on the page (useful for quick analysis without Vision) */\n getPageText: () => Promise<string>;\n \n /** Check if the element is visible on the screen */\n isVisible: (selector: string) => Promise<boolean>;\n\n /** Count the number of elements matching the selector */\n getElementCount: (selector: string) => Promise<number>;\n}\n\nexport interface VisualScenario {\n /** Unique scenario identifier (kebab-case) */\n id: string;\n /** Human-readable title for the report */\n title: string;\n /** Description of what is being tested */\n description?: string;\n /** Initial route to navigate to (e.g., '/instances', '/settings') */\n route?: string;\n /** List of viewport sizes to test */\n viewports?: ViewportPreset[];\n /**\n * Initial IPC mock data for preview mode.\n * Applied BEFORE navigation to the route.\n */\n mockIpc?: Array<{ action: string; data: any; type?: MockIpcResponseType; delayMs?: number }>;\n /**\n * Optional setup hook: executed before the browser navigates and scenario runs.\n * Ideal for preparing mock files, test directories, or initial state.\n */\n setup?: () => Promise<void> | void;\n /** The main body of the scenario */\n run: (ctx: TestContext) => Promise<void>;\n /**\n * Optional teardown hook: guaranteed to execute in finally, even if run() fails.\n * Ideal for cleaning up test artifacts, cache directories, or temporary state.\n */\n teardown?: () => Promise<void> | void;\n}\n\nexport function defineVisualTest(scenario: VisualScenario): VisualScenario {\n return scenario;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUO,IAAM,mBAAmB;AAAA;AAAA,EAE9B,eAAe,EAAE,MAAM,iBAAiB,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAEjE,SAAS,EAAE,MAAM,WAAW,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAErD,MAAM,EAAE,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAE/C,SAAS,EAAE,MAAM,WAAW,OAAO,MAAM,QAAQ,KAAK;AACxD;AAyKO,SAAS,iBAAiB,UAA0C;AACzE,SAAO;AACT;","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/shared/api/dsl.ts
|
|
2
|
+
var VIEWPORT_PRESETS = {
|
|
3
|
+
/** Minimum supported window size (e.g., ) */
|
|
4
|
+
MIN_SUPPORTED: { name: "min-supported", width: 1024, height: 768 },
|
|
5
|
+
/** Standard default window size (e.g., ) */
|
|
6
|
+
DEFAULT: { name: "default", width: 1200, height: 800 },
|
|
7
|
+
/** Wide screen for checking grids and tables */
|
|
8
|
+
WIDE: { name: "wide", width: 1600, height: 900 },
|
|
9
|
+
/** Full HD */
|
|
10
|
+
FULL_HD: { name: "full-hd", width: 1920, height: 1080 }
|
|
11
|
+
};
|
|
12
|
+
function defineVisualTest(scenario) {
|
|
13
|
+
return scenario;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
VIEWPORT_PRESETS,
|
|
17
|
+
defineVisualTest
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/api/dsl.ts"],"sourcesContent":["import type { Page, BrowserContext } from 'playwright';\nimport type { ConsoleEntry } from '../../features/console-tracker/consoleTracker';\nimport type { MockIpcResponseType } from '../../features/mock-ipc/mockIpc';\n\nexport interface ViewportPreset {\n name: string;\n width: number;\n height: number;\n}\n\nexport const VIEWPORT_PRESETS = {\n /** Minimum supported window size (e.g., ) */\n MIN_SUPPORTED: { name: 'min-supported', width: 1024, height: 768 } as ViewportPreset,\n /** Standard default window size (e.g., ) */\n DEFAULT: { name: 'default', width: 1200, height: 800 } as ViewportPreset,\n /** Wide screen for checking grids and tables */\n WIDE: { name: 'wide', width: 1600, height: 900 } as ViewportPreset,\n /** Full HD */\n FULL_HD: { name: 'full-hd', width: 1920, height: 1080 } as ViewportPreset,\n};\n\nexport interface CaptureOptions {\n selector?: string;\n fullPage?: boolean;\n mask?: string[];\n}\n\nexport interface CaptureBurstOptions {\n /** Total duration of burst animation capture in milliseconds */\n durationMs: number;\n /** Interval between frames in milliseconds (default: 80ms) */\n intervalMs?: number;\n /** Restrict capture to a specific element */\n selector?: string;\n}\n\nexport interface SnapshotMetadata {\n index: number;\n name: string;\n fileName: string;\n filePath: string;\n relativeUri: string;\n viewport: { width: number; height: number };\n timestamp: string;\n isBurstFrame?: boolean;\n burstGroup?: string;\n frameIndex?: number;\n selector?: string;\n}\n\nexport interface TestContext {\n page: Page;\n context: BrowserContext;\n targetMode: 'desktop' | 'preview';\n currentViewport: { width: number; height: number };\n \n // --- Snapshots ---\n\n /** Capture a single high-quality snapshot of the window or element */\n capture: (name: string, options?: CaptureOptions) => Promise<SnapshotMetadata>;\n \n /** Capture a series of frames for animations (Framer Motion, modals, lists) */\n captureBurst: (name: string, options: CaptureBurstOptions) => Promise<SnapshotMetadata[]>;\n \n // --- Navigation ---\n\n /** \n * Dynamically navigate to another page during the test.\n * Example: await ctx.navigate('/settings');\n */\n navigate: (route: string) => Promise<void>;\n\n // --- Viewport ---\n\n /** Change window / viewport size */\n resize: (width: number, height: number) => Promise<void>;\n \n /** Set one of the standard presets (MIN_SUPPORTED, DEFAULT, WIDE) */\n setPreset: (preset: ViewportPreset) => Promise<void>;\n\n /** \n * Dynamically resize the window so it perfectly fits the specified element (or body).\n * Useful when an agent wants to test an isolated component without background clutter.\n */\n resizeToFit: (selector?: string, padding?: number) => Promise<void>;\n \n // --- Waiting ---\n\n /** Wait the specified amount of milliseconds */\n wait: (ms: number) => Promise<void>;\n \n /** Wait for the selector to appear in the DOM */\n waitForSelector: (selector: string, timeoutMs?: number) => Promise<void>;\n \n // --- Interaction ---\n\n /** Left-click the element */\n click: (selector: string) => Promise<void>;\n \n /** Right-click the element (Context Menu) */\n rightClick: (selector: string) => Promise<void>;\n \n /** Type text into an input field */\n type: (selector: string, text: string) => Promise<void>;\n \n /** Select an option in a <select> by its value */\n selectOption: (selector: string, value: string) => Promise<void>;\n \n /** Hover the cursor to check highlights / tooltips */\n hover: (selector: string) => Promise<void>;\n \n /** Scroll the element (or window) down by the specified number of pixels */\n scroll: (selector: string, deltaY: number) => Promise<void>;\n \n // --- Logging ---\n\n /** Log a step for the agent */\n log: (message: string) => void;\n\n // --- Mock IPC (preview mode only) ---\n\n /**\n * Set a mock response for an IPC action.\n * Changes the data returned by window.external.sendMessage in preview mode.\n * Ignored in desktop mode (IPC goes through real backend).\n *\n * Example:\n * await ctx.setMockIpc('GET_INSTANCES', [{ id: '...', name: 'Test' }]);\n * await ctx.setMockIpc('CREATE_INSTANCE', 'Some error', { type: 'ERROR' });\n */\n setMockIpc: (action: string, data: any, options?: { type?: MockIpcResponseType; delayMs?: number }) => Promise<void>;\n\n // --- Console Errors ---\n\n /** Return all intercepted console.error and pageerror logs */\n getConsoleErrors: () => ConsoleEntry[];\n\n /** Return all intercepted console.warn logs */\n getConsoleWarnings: () => ConsoleEntry[];\n\n /** Returns true if there are critical errors in the console */\n hasConsoleErrors: () => boolean;\n\n // --- DOM Assertions ---\n\n /** Get the visible text of a specific element */\n readText: (selector: string) => Promise<string | null>;\n \n /** Get ALL visible text on the page (useful for quick analysis without Vision) */\n getPageText: () => Promise<string>;\n \n /** Check if the element is visible on the screen */\n isVisible: (selector: string) => Promise<boolean>;\n\n /** Count the number of elements matching the selector */\n getElementCount: (selector: string) => Promise<number>;\n}\n\nexport interface VisualScenario {\n /** Unique scenario identifier (kebab-case) */\n id: string;\n /** Human-readable title for the report */\n title: string;\n /** Description of what is being tested */\n description?: string;\n /** Initial route to navigate to (e.g., '/instances', '/settings') */\n route?: string;\n /** List of viewport sizes to test */\n viewports?: ViewportPreset[];\n /**\n * Initial IPC mock data for preview mode.\n * Applied BEFORE navigation to the route.\n */\n mockIpc?: Array<{ action: string; data: any; type?: MockIpcResponseType; delayMs?: number }>;\n /**\n * Optional setup hook: executed before the browser navigates and scenario runs.\n * Ideal for preparing mock files, test directories, or initial state.\n */\n setup?: () => Promise<void> | void;\n /** The main body of the scenario */\n run: (ctx: TestContext) => Promise<void>;\n /**\n * Optional teardown hook: guaranteed to execute in finally, even if run() fails.\n * Ideal for cleaning up test artifacts, cache directories, or temporary state.\n */\n teardown?: () => Promise<void> | void;\n}\n\nexport function defineVisualTest(scenario: VisualScenario): VisualScenario {\n return scenario;\n}\n"],"mappings":";AAUO,IAAM,mBAAmB;AAAA;AAAA,EAE9B,eAAe,EAAE,MAAM,iBAAiB,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAEjE,SAAS,EAAE,MAAM,WAAW,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAErD,MAAM,EAAE,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAE/C,SAAS,EAAE,MAAM,WAAW,OAAO,MAAM,QAAQ,KAAK;AACxD;AAyKO,SAAS,iBAAiB,UAA0C;AACzE,SAAO;AACT;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@_deep4wee/agent-lens",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Visual self-check UI runner for AI coding agents",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"agent-lens": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"skills",
|
|
16
|
+
"scripts",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"clean": "node -e \"const fs = require('fs'); ['dist', 'artifacts'].forEach(d => fs.rmSync(d, {recursive: true, force: true})); const files = fs.readdirSync('.'); files.filter(f => f.endsWith('.tgz')).forEach(f => fs.unlinkSync(f));\"",
|
|
22
|
+
"build": "npm run clean && tsup",
|
|
23
|
+
"build:pack": "npm run build && npm pack",
|
|
24
|
+
"postinstall": "node scripts/postinstall.js"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"playwright": "^1.40.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.0.0",
|
|
31
|
+
"playwright": "^1.63.0",
|
|
32
|
+
"tsup": "^8.0.2",
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"jiti": "^2.7.0",
|
|
37
|
+
"tree-kill": "^1.2.2"
|
|
38
|
+
},
|
|
39
|
+
"author": "deep4wee",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/deep4wee/agent-lens.git"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"ai",
|
|
47
|
+
"agent",
|
|
48
|
+
"visual",
|
|
49
|
+
"testing",
|
|
50
|
+
"playwright",
|
|
51
|
+
"automation",
|
|
52
|
+
"screenshot",
|
|
53
|
+
"ui"
|
|
54
|
+
],
|
|
55
|
+
"type": "commonjs",
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/deep4wee/agent-lens/issues"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://github.com/deep4wee/agent-lens#readme"
|
|
60
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
// When installed via npm, INIT_CWD is the root of the project installing this package.
|
|
5
|
+
// If not available, fallback to process.cwd() (though usually that's the package dir itself).
|
|
6
|
+
const targetProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
7
|
+
|
|
8
|
+
const skillSourceDir = path.join(__dirname, '../skills/agent-lens');
|
|
9
|
+
const targetSkillDir = path.join(targetProjectRoot, '.agents', 'skills', 'agent-lens');
|
|
10
|
+
|
|
11
|
+
function copySkillRecursive(src, dest) {
|
|
12
|
+
if (!fs.existsSync(src)) return;
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(dest)) {
|
|
15
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
const srcPath = path.join(src, entry.name);
|
|
21
|
+
const destPath = path.join(dest, entry.name);
|
|
22
|
+
|
|
23
|
+
if (entry.isDirectory()) {
|
|
24
|
+
copySkillRecursive(srcPath, destPath);
|
|
25
|
+
} else {
|
|
26
|
+
fs.copyFileSync(srcPath, destPath);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function copySkill() {
|
|
32
|
+
try {
|
|
33
|
+
if (!fs.existsSync(skillSourceDir)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
copySkillRecursive(skillSourceDir, targetSkillDir);
|
|
38
|
+
console.log(`[AgentLens] Successfully equipped AI skill at ${targetSkillDir}`);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.warn('[AgentLens] Warning: Failed to copy SKILL.md automatically:', error.message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Don't run postinstall if we are developing inside the agent-lens repo itself
|
|
45
|
+
if (path.resolve(targetProjectRoot) !== path.resolve(path.join(__dirname, '..'))) {
|
|
46
|
+
copySkill();
|
|
47
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-lens
|
|
3
|
+
description: Visual UI self-verification tool for AI agents. Take responsive multi-viewport snapshots of live URLs or run scripted interaction scenarios in Chromium or native desktop WebView2. Catches visual regressions, layout shifts, silent console errors, and runtime exceptions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 👁️ AgentLens
|
|
7
|
+
|
|
8
|
+
**AgentLens** is a visual self-verification tool designed specifically for autonomous AI coding agents.
|
|
9
|
+
|
|
10
|
+
Instead of writing frontend code and blindly guessing if it looks right, AgentLens gives you **eyes**. You can capture multi-viewport screenshots of your running app, focus on isolated components, test animations, intercept silent JavaScript errors, and inspect the resulting markdown report to iterate autonomously before showing the final result to the user.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## ⚡ Two Modes of Agent Verification
|
|
15
|
+
|
|
16
|
+
### 1. Instant Verification (`snap`) — No test files needed!
|
|
17
|
+
Ideal for 90% of tasks when you just modified a page, component, or layout:
|
|
18
|
+
```bash
|
|
19
|
+
# Verify a live dev server across desktop & mobile viewports:
|
|
20
|
+
npx agent-lens snap --url=http://localhost:5173
|
|
21
|
+
|
|
22
|
+
# Auto-start dev server in a monorepo (e.g. Frontend/), snap, and auto-terminate:
|
|
23
|
+
npx agent-lens snap --start="npm run dev" --start-cwd=./Frontend
|
|
24
|
+
|
|
25
|
+
# Zero-config smart snap (auto-detects active server or static build):
|
|
26
|
+
npx agent-lens snap
|
|
27
|
+
|
|
28
|
+
# Focus strictly on one component:
|
|
29
|
+
npx agent-lens snap --url=http://localhost:5173/settings --selector=".pricing-card"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. Scripted Scenarios (`npx agent-lens --scenario=<name>`)
|
|
33
|
+
For multi-step flows, form submissions, state mocks, and complex assertions:
|
|
34
|
+
```bash
|
|
35
|
+
npx agent-lens --scenario=checkout-flow --url=http://localhost:5173
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🧭 The Agent Workflow
|
|
41
|
+
|
|
42
|
+
1. **Write or Edit the Code**: Implement the requested UI changes or components.
|
|
43
|
+
2. **Choose Your Verification Method**:
|
|
44
|
+
- **Quick check**: Run `npx agent-lens snap`.
|
|
45
|
+
- **Interactive check**: Write a scenario file in `scenarios/<name>.scenario.ts`.
|
|
46
|
+
3. **Execute the Runner**:
|
|
47
|
+
```bash
|
|
48
|
+
npx agent-lens snap --url=http://localhost:5173
|
|
49
|
+
```
|
|
50
|
+
4. **Inspect the Output**:
|
|
51
|
+
- AgentLens always writes the latest report to:
|
|
52
|
+
`artifacts/latest/report.md`
|
|
53
|
+
- Use `view_file` to read `artifacts/latest/report.md` immediately without worrying about timestamped folder names.
|
|
54
|
+
- Check the **Console Errors** section — if errors exist, fix them!
|
|
55
|
+
5. **Self-Correct & Iterate**: Re-run verification until the layout is solid and the console is clean.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## ⚠️ Agent Best Practices & Pitfalls to Avoid
|
|
60
|
+
|
|
61
|
+
### 1. Avoid Localized Text in Selectors (Localization Pitfall)
|
|
62
|
+
❌ **Never write:**
|
|
63
|
+
```typescript
|
|
64
|
+
await ctx.type('input[placeholder*="Search"]', 'text'); // Fails if language is Ukrainian "Пошук"!
|
|
65
|
+
await ctx.click('button:has-text("Save")'); // Fails on non-English UI!
|
|
66
|
+
```
|
|
67
|
+
✅ **Always use semantic attributes, test IDs, or structural CSS:**
|
|
68
|
+
```typescript
|
|
69
|
+
await ctx.type('input[type="search"]', 'text');
|
|
70
|
+
await ctx.type('input[name="query"]', 'text');
|
|
71
|
+
await ctx.click('button[type="submit"]');
|
|
72
|
+
await ctx.click('[data-testid="save-button"]');
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. Base Mocks in `scenarios/mocks.ts` (Prevent Root Crashes)
|
|
76
|
+
When your app mounts, the root component (Navbar, AuthContext, Layout) often queries base endpoints (e.g. `GET_USER`, `GET_SETTINGS`, `GET_ACCOUNTS`). If your scenario only mocks one sub-action, unmocked root actions may return empty and crash the app with `Cannot read properties of null (reading 'length')`.
|
|
77
|
+
- Place common base mocks in `scenarios/mocks.ts`.
|
|
78
|
+
- AgentLens automatically merges `scenarios/mocks.ts` with your scenario's specific `mockIpc: [...]` overrides!
|
|
79
|
+
|
|
80
|
+
### 3. Monorepos & Subdirectories (`--start-cwd`)
|
|
81
|
+
If your frontend lives in a subfolder like `Frontend/` or `client/`:
|
|
82
|
+
- Pass `--start-cwd=./Frontend` (or configure `"startCwd": "./Frontend"` in `agent-lens.json`).
|
|
83
|
+
- AgentLens automatically auto-detects `Frontend/`, `client/`, or `web/` if root `package.json` lacks a `dev` script.
|
|
84
|
+
|
|
85
|
+
### 4. Preventing Artifact Folder Bloat
|
|
86
|
+
To keep the workspace tidy across multiple test iterations, use:
|
|
87
|
+
```bash
|
|
88
|
+
npx agent-lens snap --clean-artifacts
|
|
89
|
+
```
|
|
90
|
+
Or always inspect the single persistent pointer:
|
|
91
|
+
`artifacts/latest/report.md`
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 💻 CLI Flags & Options
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx agent-lens [command] [options]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Commands:
|
|
102
|
+
- `snap`: Instant one-shot snapshot & console check of a URL or static build.
|
|
103
|
+
- `init`: Generate starter template in `scenarios/template.scenario.ts` and `scenarios/mocks.ts`.
|
|
104
|
+
- *(default)*: Runs matching scenarios from `scenarios/`.
|
|
105
|
+
|
|
106
|
+
### Options:
|
|
107
|
+
| Flag | Description | Default |
|
|
108
|
+
| :--- | :--- | :--- |
|
|
109
|
+
| `--url=<url>` | Target URL to test (e.g. `http://localhost:5173`) | *Auto-detected* |
|
|
110
|
+
| `--start="<cmd>"` | Auto-launch dev server / backend before test | *(none)* |
|
|
111
|
+
| `--start-cwd=<path>` | Directory to run `--start` in (e.g. `--start-cwd=./Frontend`) | *Auto-detected* |
|
|
112
|
+
| `--clean-artifacts` | Purge older test runs in `artifacts/` | `false` |
|
|
113
|
+
| `--selector=<css>` | Focus and resize-to-fit a specific component | *(none)* |
|
|
114
|
+
| `--viewports=<list>` | Viewports to capture (`desktop,mobile,tablet` or `1200x800`) | `desktop,mobile` |
|
|
115
|
+
| `--wait=<ms>` | Wait time after page load before taking snapshots | `1000` |
|
|
116
|
+
| `--scenario=<id>` | Name or prefix of scenario file to run | *(none)* |
|
|
117
|
+
| `--all` | Run all discovered scenarios | `false` |
|
|
118
|
+
| `--mode=preview\|desktop` | Engine: `preview` (Web / Live URL) or `desktop` (native .exe) | `preview` |
|
|
119
|
+
| `--exe=<path>` | Path to compiled desktop executable for desktop mode | *(none)* |
|
|
120
|
+
| `--port=<port>` | CDP remote debugging port for desktop mode | `9222` |
|
|
121
|
+
| `--build[=<cmd>]` | Build command to run before testing | `npm run build` |
|
|
122
|
+
| `--clean=<paths>` | Comma-separated paths to purge on exit | *(none)* |
|
|
123
|
+
| `--folder=<path>` | Folder to store report & screenshots (also `--outDir`) | `artifacts` |
|
|
124
|
+
| `--headed` | Open visible browser window | `false` |
|
|
125
|
+
| `--detach` | Do not close browser or app on finish | `false` |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 🛠️ DSL API Reference (`scenarios/*.scenario.ts`)
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { defineVisualTest, VIEWPORT_PRESETS, type TestContext } from 'agent-lens';
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Scenario Definition Structure:
|
|
136
|
+
```typescript
|
|
137
|
+
export default defineVisualTest({
|
|
138
|
+
id: 'my-feature-check',
|
|
139
|
+
title: 'Feature Verification',
|
|
140
|
+
route: '/dashboard', // Route or URL
|
|
141
|
+
viewports: [VIEWPORT_PRESETS.DEFAULT, VIEWPORT_PRESETS.MIN_SUPPORTED],
|
|
142
|
+
|
|
143
|
+
// Lifecycle Setup: prepare temporary state or mock folders
|
|
144
|
+
setup: async () => {},
|
|
145
|
+
|
|
146
|
+
// Test Body: interact and capture
|
|
147
|
+
run: async (ctx) => {},
|
|
148
|
+
|
|
149
|
+
// Lifecycle Teardown: ALWAYS executes, guaranteed cleanup
|
|
150
|
+
teardown: async () => {}
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Available `ctx` Methods:
|
|
155
|
+
|
|
156
|
+
#### 📸 Capturing
|
|
157
|
+
- `await ctx.capture('01_name', options?)`: Capture full page or element snapshot.
|
|
158
|
+
- `await ctx.captureBurst('02_anim', { durationMs: 300, intervalMs: 50, selector? })`: Capture animation frames.
|
|
159
|
+
|
|
160
|
+
#### 📐 Viewport Control
|
|
161
|
+
- `await ctx.setPreset(VIEWPORT_PRESETS.DEFAULT)`: Standard desktop (1200x800).
|
|
162
|
+
- `await ctx.setPreset(VIEWPORT_PRESETS.MIN_SUPPORTED)`: Compact viewport (1024x768).
|
|
163
|
+
- `await ctx.setPreset(VIEWPORT_PRESETS.WIDE)`: Widescreen (1600x900).
|
|
164
|
+
- `await ctx.resize(width, height)`: Custom dimensions.
|
|
165
|
+
- `await ctx.resizeToFit('selector', padding?)`: Dynamically resize viewport to wrap an element tightly.
|
|
166
|
+
|
|
167
|
+
#### 🖱️ Interactions
|
|
168
|
+
- `await ctx.click('selector')` / `await ctx.rightClick('selector')`
|
|
169
|
+
- `await ctx.type('selector', 'text')`
|
|
170
|
+
- `await ctx.selectOption('selector', 'value')`
|
|
171
|
+
- `await ctx.hover('selector')`
|
|
172
|
+
- `await ctx.scroll('selector', deltaY)`
|
|
173
|
+
|
|
174
|
+
#### 🕒 Waiting & Assertions
|
|
175
|
+
- `await ctx.wait(ms)`
|
|
176
|
+
- `await ctx.waitForSelector('selector', timeoutMs?)`
|
|
177
|
+
- `const text = await ctx.readText('selector')`
|
|
178
|
+
- `const pageText = await ctx.getPageText()`
|
|
179
|
+
- `const isVisible = await ctx.isVisible('selector')`
|
|
180
|
+
- `const count = await ctx.getElementCount('selector')`
|
|
181
|
+
|
|
182
|
+
#### 🐛 Console & Logs
|
|
183
|
+
- `ctx.log('Message')`: Writes custom log entry into the final markdown report.
|
|
184
|
+
- `ctx.getConsoleErrors()`: Array of caught errors with stack traces.
|
|
185
|
+
- `ctx.getConsoleWarnings()`: Array of caught warnings.
|
|
186
|
+
|
|
187
|
+
#### 🎭 Dynamic Mock IPC (Web / Preview mode)
|
|
188
|
+
- `await ctx.setMockIpc('ACTION_NAME', payload, { type: 'SUCCESS' | 'ERROR', delayMs?: number })`: Dynamically alters mock data during test execution.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 📚 Detailed Examples & Use Cases
|
|
193
|
+
|
|
194
|
+
Check the dedicated example guides in `examples/` for complete walk-throughs:
|
|
195
|
+
1. [Instant Verification (`snap`)](examples/01-instant-verification-snap.md) — One-shot snapshotting without writing test files.
|
|
196
|
+
2. [Live Dev Server Workflow](examples/02-dev-server-live-testing.md) — Testing active Vite/Next.js servers with `--start` or `--url`.
|
|
197
|
+
3. [Component Isolation & Burst Animations](examples/03-component-isolation-and-animations.md) — Inspecting isolated components and CSS transitions.
|
|
198
|
+
4. [Native Desktop App Testing](examples/04-desktop-native-testing.md) — Testing compiled `.exe` binaries with CDP and crash diagnostics.
|
|
199
|
+
5. [Clean Teardown & Sandboxing](examples/05-clean-teardown-and-sandboxing.md) — Guaranteeing zero leftover test data using `teardown()` and `--clean`.
|
|
200
|
+
6. [State Testing with Mock IPC](examples/06-state-testing-with-mock-ipc.md) — Testing empty states, errors, and data tables.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Example 1: Instant Visual Self-Check (`snap`)
|
|
2
|
+
|
|
3
|
+
The `snap` command is the fastest way for an AI agent to "see" its work immediately without writing any scenario files.
|
|
4
|
+
|
|
5
|
+
## Use Cases
|
|
6
|
+
- After generating or editing a component, web page, or modal.
|
|
7
|
+
- Quickly verifying responsiveness across desktop and mobile screens.
|
|
8
|
+
- Checking for silent console errors and runtime exceptions.
|
|
9
|
+
|
|
10
|
+
## Basic Usage
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Check an already-running dev server
|
|
14
|
+
npx agent-lens snap --url=http://localhost:5173
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Options & Combinations
|
|
18
|
+
|
|
19
|
+
### 1. Test Multiple Viewports (Desktop, Tablet, Mobile)
|
|
20
|
+
```bash
|
|
21
|
+
npx agent-lens snap \
|
|
22
|
+
--url=http://localhost:3000/dashboard \
|
|
23
|
+
--viewports=desktop,tablet,mobile
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. Auto-Start Dev Server
|
|
27
|
+
If your dev server is not running yet, AgentLens can start it automatically, wait until it's ready, take the snapshots, and cleanly terminate the server when finished:
|
|
28
|
+
```bash
|
|
29
|
+
npx agent-lens snap \
|
|
30
|
+
--start="npm run dev" \
|
|
31
|
+
--url=http://localhost:5173 \
|
|
32
|
+
--wait=1500
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 3. Focus on a Specific Component (`--selector`)
|
|
36
|
+
Take responsive snapshots of the entire page AND an isolated, fitted snapshot of a single component:
|
|
37
|
+
```bash
|
|
38
|
+
npx agent-lens snap \
|
|
39
|
+
--url=http://localhost:5173/settings \
|
|
40
|
+
--selector=".pricing-card" \
|
|
41
|
+
--name="pricing_component"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 4. Custom Output Directory
|
|
45
|
+
Save visual reports directly to a project folder (make sure to add it to `.gitignore`):
|
|
46
|
+
```bash
|
|
47
|
+
npx agent-lens snap \
|
|
48
|
+
--url=http://localhost:5173 \
|
|
49
|
+
--folder=visual-reports
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Reading the Result
|
|
53
|
+
AgentLens outputs the absolute path to `report.md`. Inspect the markdown report using your file reading tool to review the images and console logs.
|