@qulib/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -0
- package/bin/qulib.js +17 -0
- package/dist/adapters/adapter-factory.d.ts +5 -0
- package/dist/adapters/adapter-factory.d.ts.map +1 -0
- package/dist/adapters/adapter-factory.js +21 -0
- package/dist/adapters/adapter.interface.d.ts +7 -0
- package/dist/adapters/adapter.interface.d.ts.map +1 -0
- package/dist/adapters/adapter.interface.js +1 -0
- package/dist/adapters/api-adapter.d.ts +8 -0
- package/dist/adapters/api-adapter.d.ts.map +1 -0
- package/dist/adapters/api-adapter.js +9 -0
- package/dist/adapters/cypress-component-adapter.d.ts +8 -0
- package/dist/adapters/cypress-component-adapter.d.ts.map +1 -0
- package/dist/adapters/cypress-component-adapter.js +9 -0
- package/dist/adapters/cypress-e2e-adapter.d.ts +8 -0
- package/dist/adapters/cypress-e2e-adapter.d.ts.map +1 -0
- package/dist/adapters/cypress-e2e-adapter.js +9 -0
- package/dist/adapters/playwright-adapter.d.ts +8 -0
- package/dist/adapters/playwright-adapter.d.ts.map +1 -0
- package/dist/adapters/playwright-adapter.js +9 -0
- package/dist/analyze.d.ts +20 -0
- package/dist/analyze.d.ts.map +1 -0
- package/dist/analyze.js +21 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +102 -0
- package/dist/harness/decision-logger.d.ts +7 -0
- package/dist/harness/decision-logger.d.ts.map +1 -0
- package/dist/harness/decision-logger.js +68 -0
- package/dist/harness/run-options.d.ts +6 -0
- package/dist/harness/run-options.d.ts.map +1 -0
- package/dist/harness/run-options.js +1 -0
- package/dist/harness/state-manager.d.ts +6 -0
- package/dist/harness/state-manager.d.ts.map +1 -0
- package/dist/harness/state-manager.js +64 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/llm/context-builder.d.ts +3 -0
- package/dist/llm/context-builder.d.ts.map +1 -0
- package/dist/llm/context-builder.js +33 -0
- package/dist/llm/provider.d.ts +4 -0
- package/dist/llm/provider.d.ts.map +1 -0
- package/dist/llm/provider.js +56 -0
- package/dist/phases/act.d.ts +5 -0
- package/dist/phases/act.d.ts.map +1 -0
- package/dist/phases/act.js +41 -0
- package/dist/phases/observe.d.ts +10 -0
- package/dist/phases/observe.d.ts.map +1 -0
- package/dist/phases/observe.js +48 -0
- package/dist/phases/think.d.ts +6 -0
- package/dist/phases/think.d.ts.map +1 -0
- package/dist/phases/think.js +85 -0
- package/dist/reporters/json-reporter.d.ts +3 -0
- package/dist/reporters/json-reporter.d.ts.map +1 -0
- package/dist/reporters/json-reporter.js +8 -0
- package/dist/reporters/markdown-reporter.d.ts +3 -0
- package/dist/reporters/markdown-reporter.d.ts.map +1 -0
- package/dist/reporters/markdown-reporter.js +42 -0
- package/dist/schemas/config.schema.d.ts +327 -0
- package/dist/schemas/config.schema.d.ts.map +1 -0
- package/dist/schemas/config.schema.js +39 -0
- package/dist/schemas/decision-log.schema.d.ts +22 -0
- package/dist/schemas/decision-log.schema.d.ts.map +1 -0
- package/dist/schemas/decision-log.schema.js +8 -0
- package/dist/schemas/gap-analysis.schema.d.ts +363 -0
- package/dist/schemas/gap-analysis.schema.d.ts.map +1 -0
- package/dist/schemas/gap-analysis.schema.js +60 -0
- package/dist/schemas/index.d.ts +6 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +5 -0
- package/dist/schemas/repo-analysis.schema.d.ts +165 -0
- package/dist/schemas/repo-analysis.schema.d.ts.map +1 -0
- package/dist/schemas/repo-analysis.schema.js +29 -0
- package/dist/schemas/route-inventory.schema.d.ts +241 -0
- package/dist/schemas/route-inventory.schema.d.ts.map +1 -0
- package/dist/schemas/route-inventory.schema.js +30 -0
- package/dist/tools/auth.d.ts +4 -0
- package/dist/tools/auth.d.ts.map +1 -0
- package/dist/tools/auth.js +35 -0
- package/dist/tools/cypress-explorer.d.ts +7 -0
- package/dist/tools/cypress-explorer.d.ts.map +1 -0
- package/dist/tools/cypress-explorer.js +5 -0
- package/dist/tools/explorer-factory.d.ts +4 -0
- package/dist/tools/explorer-factory.d.ts.map +1 -0
- package/dist/tools/explorer-factory.js +12 -0
- package/dist/tools/explorer.interface.d.ts +6 -0
- package/dist/tools/explorer.interface.d.ts.map +1 -0
- package/dist/tools/explorer.interface.js +1 -0
- package/dist/tools/gap-engine.d.ts +6 -0
- package/dist/tools/gap-engine.d.ts.map +1 -0
- package/dist/tools/gap-engine.js +101 -0
- package/dist/tools/playwright-explorer.d.ts +7 -0
- package/dist/tools/playwright-explorer.d.ts.map +1 -0
- package/dist/tools/playwright-explorer.js +150 -0
- package/dist/tools/repo-scanner.d.ts +3 -0
- package/dist/tools/repo-scanner.d.ts.map +1 -0
- package/dist/tools/repo-scanner.js +147 -0
- package/package.json +54 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const A11yViolationSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodString;
|
|
4
|
+
impact: z.ZodString;
|
|
5
|
+
helpUrl: z.ZodString;
|
|
6
|
+
nodeCount: z.ZodNumber;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
id: string;
|
|
9
|
+
impact: string;
|
|
10
|
+
helpUrl: string;
|
|
11
|
+
nodeCount: number;
|
|
12
|
+
}, {
|
|
13
|
+
id: string;
|
|
14
|
+
impact: string;
|
|
15
|
+
helpUrl: string;
|
|
16
|
+
nodeCount: number;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const BrokenLinkSchema: z.ZodObject<{
|
|
19
|
+
url: z.ZodString;
|
|
20
|
+
status: z.ZodNullable<z.ZodNumber>;
|
|
21
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, "strip", z.ZodTypeAny, {
|
|
23
|
+
status: number | null;
|
|
24
|
+
url: string;
|
|
25
|
+
reason?: string | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
status: number | null;
|
|
28
|
+
url: string;
|
|
29
|
+
reason?: string | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
export declare const RouteSchema: z.ZodObject<{
|
|
32
|
+
path: z.ZodString;
|
|
33
|
+
pageTitle: z.ZodString;
|
|
34
|
+
links: z.ZodArray<z.ZodString, "many">;
|
|
35
|
+
formCount: z.ZodNumber;
|
|
36
|
+
buttonLabels: z.ZodArray<z.ZodString, "many">;
|
|
37
|
+
consoleErrors: z.ZodArray<z.ZodString, "many">;
|
|
38
|
+
brokenLinks: z.ZodArray<z.ZodObject<{
|
|
39
|
+
url: z.ZodString;
|
|
40
|
+
status: z.ZodNullable<z.ZodNumber>;
|
|
41
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
42
|
+
}, "strip", z.ZodTypeAny, {
|
|
43
|
+
status: number | null;
|
|
44
|
+
url: string;
|
|
45
|
+
reason?: string | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
status: number | null;
|
|
48
|
+
url: string;
|
|
49
|
+
reason?: string | undefined;
|
|
50
|
+
}>, "many">;
|
|
51
|
+
a11yViolations: z.ZodArray<z.ZodObject<{
|
|
52
|
+
id: z.ZodString;
|
|
53
|
+
impact: z.ZodString;
|
|
54
|
+
helpUrl: z.ZodString;
|
|
55
|
+
nodeCount: z.ZodNumber;
|
|
56
|
+
}, "strip", z.ZodTypeAny, {
|
|
57
|
+
id: string;
|
|
58
|
+
impact: string;
|
|
59
|
+
helpUrl: string;
|
|
60
|
+
nodeCount: number;
|
|
61
|
+
}, {
|
|
62
|
+
id: string;
|
|
63
|
+
impact: string;
|
|
64
|
+
helpUrl: string;
|
|
65
|
+
nodeCount: number;
|
|
66
|
+
}>, "many">;
|
|
67
|
+
statusCode: z.ZodOptional<z.ZodNumber>;
|
|
68
|
+
}, "strip", z.ZodTypeAny, {
|
|
69
|
+
path: string;
|
|
70
|
+
pageTitle: string;
|
|
71
|
+
links: string[];
|
|
72
|
+
formCount: number;
|
|
73
|
+
buttonLabels: string[];
|
|
74
|
+
consoleErrors: string[];
|
|
75
|
+
brokenLinks: {
|
|
76
|
+
status: number | null;
|
|
77
|
+
url: string;
|
|
78
|
+
reason?: string | undefined;
|
|
79
|
+
}[];
|
|
80
|
+
a11yViolations: {
|
|
81
|
+
id: string;
|
|
82
|
+
impact: string;
|
|
83
|
+
helpUrl: string;
|
|
84
|
+
nodeCount: number;
|
|
85
|
+
}[];
|
|
86
|
+
statusCode?: number | undefined;
|
|
87
|
+
}, {
|
|
88
|
+
path: string;
|
|
89
|
+
pageTitle: string;
|
|
90
|
+
links: string[];
|
|
91
|
+
formCount: number;
|
|
92
|
+
buttonLabels: string[];
|
|
93
|
+
consoleErrors: string[];
|
|
94
|
+
brokenLinks: {
|
|
95
|
+
status: number | null;
|
|
96
|
+
url: string;
|
|
97
|
+
reason?: string | undefined;
|
|
98
|
+
}[];
|
|
99
|
+
a11yViolations: {
|
|
100
|
+
id: string;
|
|
101
|
+
impact: string;
|
|
102
|
+
helpUrl: string;
|
|
103
|
+
nodeCount: number;
|
|
104
|
+
}[];
|
|
105
|
+
statusCode?: number | undefined;
|
|
106
|
+
}>;
|
|
107
|
+
export declare const RouteInventorySchema: z.ZodObject<{
|
|
108
|
+
scannedAt: z.ZodString;
|
|
109
|
+
baseUrl: z.ZodString;
|
|
110
|
+
routes: z.ZodArray<z.ZodObject<{
|
|
111
|
+
path: z.ZodString;
|
|
112
|
+
pageTitle: z.ZodString;
|
|
113
|
+
links: z.ZodArray<z.ZodString, "many">;
|
|
114
|
+
formCount: z.ZodNumber;
|
|
115
|
+
buttonLabels: z.ZodArray<z.ZodString, "many">;
|
|
116
|
+
consoleErrors: z.ZodArray<z.ZodString, "many">;
|
|
117
|
+
brokenLinks: z.ZodArray<z.ZodObject<{
|
|
118
|
+
url: z.ZodString;
|
|
119
|
+
status: z.ZodNullable<z.ZodNumber>;
|
|
120
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
121
|
+
}, "strip", z.ZodTypeAny, {
|
|
122
|
+
status: number | null;
|
|
123
|
+
url: string;
|
|
124
|
+
reason?: string | undefined;
|
|
125
|
+
}, {
|
|
126
|
+
status: number | null;
|
|
127
|
+
url: string;
|
|
128
|
+
reason?: string | undefined;
|
|
129
|
+
}>, "many">;
|
|
130
|
+
a11yViolations: z.ZodArray<z.ZodObject<{
|
|
131
|
+
id: z.ZodString;
|
|
132
|
+
impact: z.ZodString;
|
|
133
|
+
helpUrl: z.ZodString;
|
|
134
|
+
nodeCount: z.ZodNumber;
|
|
135
|
+
}, "strip", z.ZodTypeAny, {
|
|
136
|
+
id: string;
|
|
137
|
+
impact: string;
|
|
138
|
+
helpUrl: string;
|
|
139
|
+
nodeCount: number;
|
|
140
|
+
}, {
|
|
141
|
+
id: string;
|
|
142
|
+
impact: string;
|
|
143
|
+
helpUrl: string;
|
|
144
|
+
nodeCount: number;
|
|
145
|
+
}>, "many">;
|
|
146
|
+
statusCode: z.ZodOptional<z.ZodNumber>;
|
|
147
|
+
}, "strip", z.ZodTypeAny, {
|
|
148
|
+
path: string;
|
|
149
|
+
pageTitle: string;
|
|
150
|
+
links: string[];
|
|
151
|
+
formCount: number;
|
|
152
|
+
buttonLabels: string[];
|
|
153
|
+
consoleErrors: string[];
|
|
154
|
+
brokenLinks: {
|
|
155
|
+
status: number | null;
|
|
156
|
+
url: string;
|
|
157
|
+
reason?: string | undefined;
|
|
158
|
+
}[];
|
|
159
|
+
a11yViolations: {
|
|
160
|
+
id: string;
|
|
161
|
+
impact: string;
|
|
162
|
+
helpUrl: string;
|
|
163
|
+
nodeCount: number;
|
|
164
|
+
}[];
|
|
165
|
+
statusCode?: number | undefined;
|
|
166
|
+
}, {
|
|
167
|
+
path: string;
|
|
168
|
+
pageTitle: string;
|
|
169
|
+
links: string[];
|
|
170
|
+
formCount: number;
|
|
171
|
+
buttonLabels: string[];
|
|
172
|
+
consoleErrors: string[];
|
|
173
|
+
brokenLinks: {
|
|
174
|
+
status: number | null;
|
|
175
|
+
url: string;
|
|
176
|
+
reason?: string | undefined;
|
|
177
|
+
}[];
|
|
178
|
+
a11yViolations: {
|
|
179
|
+
id: string;
|
|
180
|
+
impact: string;
|
|
181
|
+
helpUrl: string;
|
|
182
|
+
nodeCount: number;
|
|
183
|
+
}[];
|
|
184
|
+
statusCode?: number | undefined;
|
|
185
|
+
}>, "many">;
|
|
186
|
+
pagesSkipped: z.ZodNumber;
|
|
187
|
+
budgetExceeded: z.ZodBoolean;
|
|
188
|
+
}, "strip", z.ZodTypeAny, {
|
|
189
|
+
scannedAt: string;
|
|
190
|
+
baseUrl: string;
|
|
191
|
+
routes: {
|
|
192
|
+
path: string;
|
|
193
|
+
pageTitle: string;
|
|
194
|
+
links: string[];
|
|
195
|
+
formCount: number;
|
|
196
|
+
buttonLabels: string[];
|
|
197
|
+
consoleErrors: string[];
|
|
198
|
+
brokenLinks: {
|
|
199
|
+
status: number | null;
|
|
200
|
+
url: string;
|
|
201
|
+
reason?: string | undefined;
|
|
202
|
+
}[];
|
|
203
|
+
a11yViolations: {
|
|
204
|
+
id: string;
|
|
205
|
+
impact: string;
|
|
206
|
+
helpUrl: string;
|
|
207
|
+
nodeCount: number;
|
|
208
|
+
}[];
|
|
209
|
+
statusCode?: number | undefined;
|
|
210
|
+
}[];
|
|
211
|
+
pagesSkipped: number;
|
|
212
|
+
budgetExceeded: boolean;
|
|
213
|
+
}, {
|
|
214
|
+
scannedAt: string;
|
|
215
|
+
baseUrl: string;
|
|
216
|
+
routes: {
|
|
217
|
+
path: string;
|
|
218
|
+
pageTitle: string;
|
|
219
|
+
links: string[];
|
|
220
|
+
formCount: number;
|
|
221
|
+
buttonLabels: string[];
|
|
222
|
+
consoleErrors: string[];
|
|
223
|
+
brokenLinks: {
|
|
224
|
+
status: number | null;
|
|
225
|
+
url: string;
|
|
226
|
+
reason?: string | undefined;
|
|
227
|
+
}[];
|
|
228
|
+
a11yViolations: {
|
|
229
|
+
id: string;
|
|
230
|
+
impact: string;
|
|
231
|
+
helpUrl: string;
|
|
232
|
+
nodeCount: number;
|
|
233
|
+
}[];
|
|
234
|
+
statusCode?: number | undefined;
|
|
235
|
+
}[];
|
|
236
|
+
pagesSkipped: number;
|
|
237
|
+
budgetExceeded: boolean;
|
|
238
|
+
}>;
|
|
239
|
+
export type RouteInventory = z.infer<typeof RouteInventorySchema>;
|
|
240
|
+
export type Route = z.infer<typeof RouteSchema>;
|
|
241
|
+
//# sourceMappingURL=route-inventory.schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-inventory.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/route-inventory.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAK9B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAI3B,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUtB,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAM/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const A11yViolationSchema = z.object({
|
|
3
|
+
id: z.string(),
|
|
4
|
+
impact: z.string(),
|
|
5
|
+
helpUrl: z.string(),
|
|
6
|
+
nodeCount: z.number().int(),
|
|
7
|
+
});
|
|
8
|
+
export const BrokenLinkSchema = z.object({
|
|
9
|
+
url: z.string(),
|
|
10
|
+
status: z.number().nullable(),
|
|
11
|
+
reason: z.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
export const RouteSchema = z.object({
|
|
14
|
+
path: z.string(),
|
|
15
|
+
pageTitle: z.string(),
|
|
16
|
+
links: z.array(z.string()),
|
|
17
|
+
formCount: z.number().int(),
|
|
18
|
+
buttonLabels: z.array(z.string()),
|
|
19
|
+
consoleErrors: z.array(z.string()),
|
|
20
|
+
brokenLinks: z.array(BrokenLinkSchema),
|
|
21
|
+
a11yViolations: z.array(A11yViolationSchema),
|
|
22
|
+
statusCode: z.number().int().optional(),
|
|
23
|
+
});
|
|
24
|
+
export const RouteInventorySchema = z.object({
|
|
25
|
+
scannedAt: z.string().datetime(),
|
|
26
|
+
baseUrl: z.string().url(),
|
|
27
|
+
routes: z.array(RouteSchema),
|
|
28
|
+
pagesSkipped: z.number().int(),
|
|
29
|
+
budgetExceeded: z.boolean(),
|
|
30
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Browser, BrowserContext } from '@playwright/test';
|
|
2
|
+
import type { AuthConfig } from '../schemas/config.schema.js';
|
|
3
|
+
export declare function createAuthenticatedContext(browser: Browser, auth: AuthConfig | undefined, timeoutMs: number): Promise<BrowserContext>;
|
|
4
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/tools/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC,CAsCzB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
export async function createAuthenticatedContext(browser, auth, timeoutMs) {
|
|
3
|
+
if (!auth) {
|
|
4
|
+
return browser.newContext();
|
|
5
|
+
}
|
|
6
|
+
if (auth.type === 'storage-state') {
|
|
7
|
+
const storagePath = resolve(process.cwd(), auth.path);
|
|
8
|
+
return browser.newContext({ storageState: storagePath });
|
|
9
|
+
}
|
|
10
|
+
const context = await browser.newContext();
|
|
11
|
+
const page = await context.newPage();
|
|
12
|
+
try {
|
|
13
|
+
await page.goto(auth.loginUrl, { timeout: timeoutMs, waitUntil: 'domcontentloaded' });
|
|
14
|
+
await page.fill(auth.selectors.username, auth.credentials.username);
|
|
15
|
+
await page.fill(auth.selectors.password, auth.credentials.password);
|
|
16
|
+
await page.click(auth.selectors.submit);
|
|
17
|
+
const urlFragment = auth.successIndicator.urlContains;
|
|
18
|
+
if (urlFragment) {
|
|
19
|
+
await page.waitForURL((url) => url.toString().includes(urlFragment), {
|
|
20
|
+
timeout: timeoutMs,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const visibleSelector = auth.successIndicator.selectorVisible;
|
|
24
|
+
if (visibleSelector) {
|
|
25
|
+
await page.waitForSelector(visibleSelector, {
|
|
26
|
+
timeout: timeoutMs,
|
|
27
|
+
state: 'visible',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
await page.close();
|
|
33
|
+
}
|
|
34
|
+
return context;
|
|
35
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AppExplorer } from './explorer.interface.js';
|
|
2
|
+
import type { HarnessConfig } from '../schemas/config.schema.js';
|
|
3
|
+
import type { RouteInventory } from '../schemas/route-inventory.schema.js';
|
|
4
|
+
export declare class CypressExplorer implements AppExplorer {
|
|
5
|
+
explore(baseUrl: string, config: HarnessConfig): Promise<RouteInventory>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=cypress-explorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cypress-explorer.d.ts","sourceRoot":"","sources":["../../src/tools/cypress-explorer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAE3E,qBAAa,eAAgB,YAAW,WAAW;IAC3C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;CAG/E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explorer-factory.d.ts","sourceRoot":"","sources":["../../src/tools/explorer-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAI3D,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAS9D"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PlaywrightExplorer } from './playwright-explorer.js';
|
|
2
|
+
import { CypressExplorer } from './cypress-explorer.js';
|
|
3
|
+
export function createExplorer(type) {
|
|
4
|
+
switch (type) {
|
|
5
|
+
case 'playwright':
|
|
6
|
+
return new PlaywrightExplorer();
|
|
7
|
+
case 'cypress':
|
|
8
|
+
return new CypressExplorer();
|
|
9
|
+
default:
|
|
10
|
+
throw new Error(`Unknown explorer type: ${type}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HarnessConfig } from '../schemas/config.schema.js';
|
|
2
|
+
import type { RouteInventory } from '../schemas/route-inventory.schema.js';
|
|
3
|
+
export interface AppExplorer {
|
|
4
|
+
explore(baseUrl: string, config: HarnessConfig): Promise<RouteInventory>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=explorer.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explorer.interface.d.ts","sourceRoot":"","sources":["../../src/tools/explorer.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAE3E,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC1E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type GapAnalysis } from '../schemas/gap-analysis.schema.js';
|
|
2
|
+
import type { RouteInventory } from '../schemas/route-inventory.schema.js';
|
|
3
|
+
import type { RepoAnalysis } from '../schemas/repo-analysis.schema.js';
|
|
4
|
+
import type { HarnessConfig } from '../schemas/config.schema.js';
|
|
5
|
+
export declare function analyzeGaps(routes: RouteInventory, repo: RepoAnalysis | null, mode: 'url-only' | 'url-repo', config: HarnessConfig): Omit<GapAnalysis, 'scenarios' | 'generatedTests'>;
|
|
6
|
+
//# sourceMappingURL=gap-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gap-engine.d.ts","sourceRoot":"","sources":["../../src/tools/gap-engine.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,WAAW,EAAY,MAAM,mCAAmC,CAAC;AAC1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEjE,wBAAgB,WAAW,CACzB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,YAAY,GAAG,IAAI,EACzB,IAAI,EAAE,UAAU,GAAG,UAAU,EAC7B,MAAM,EAAE,aAAa,GACpB,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,gBAAgB,CAAC,CA0GnD"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { GapSchema } from '../schemas/gap-analysis.schema.js';
|
|
3
|
+
export function analyzeGaps(routes, repo, mode, config) {
|
|
4
|
+
const coveredPaths = new Set();
|
|
5
|
+
if (repo) {
|
|
6
|
+
for (const testFile of repo.testFiles) {
|
|
7
|
+
for (const path of testFile.coveredPaths) {
|
|
8
|
+
coveredPaths.add(path);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const gaps = [];
|
|
13
|
+
const addGap = (gap) => {
|
|
14
|
+
const validated = GapSchema.parse(gap);
|
|
15
|
+
gaps.push(validated);
|
|
16
|
+
};
|
|
17
|
+
let hasNavigationFailures = false;
|
|
18
|
+
for (const route of routes.routes) {
|
|
19
|
+
if (repo && !coveredPaths.has(route.path)) {
|
|
20
|
+
const highRisk = /checkout|payment|auth|login|order/i.test(route.path);
|
|
21
|
+
addGap({
|
|
22
|
+
id: randomUUID(),
|
|
23
|
+
path: route.path,
|
|
24
|
+
severity: highRisk ? 'high' : 'medium',
|
|
25
|
+
reason: `Route is not covered by existing tests: ${route.path}`,
|
|
26
|
+
category: 'untested-route',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const navErrors = route.consoleErrors.filter((e) => e.startsWith('Navigation error:'));
|
|
30
|
+
if (navErrors.length > 0) {
|
|
31
|
+
hasNavigationFailures = true;
|
|
32
|
+
addGap({
|
|
33
|
+
id: randomUUID(),
|
|
34
|
+
path: route.path,
|
|
35
|
+
severity: 'high',
|
|
36
|
+
reason: `Navigation failed: ${navErrors.join('; ')}`,
|
|
37
|
+
category: 'console-error',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else if (route.consoleErrors.length > 0) {
|
|
41
|
+
addGap({
|
|
42
|
+
id: randomUUID(),
|
|
43
|
+
path: route.path,
|
|
44
|
+
severity: 'high',
|
|
45
|
+
reason: `Console errors detected (${route.consoleErrors.length})`,
|
|
46
|
+
category: 'console-error',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (route.brokenLinks.length > 0) {
|
|
50
|
+
addGap({
|
|
51
|
+
id: randomUUID(),
|
|
52
|
+
path: route.path,
|
|
53
|
+
severity: 'medium',
|
|
54
|
+
reason: `Broken or invalid links detected (${route.brokenLinks.length})`,
|
|
55
|
+
category: 'broken-link',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
for (const violation of route.a11yViolations) {
|
|
59
|
+
const impact = violation.impact.toLowerCase();
|
|
60
|
+
const severity = impact === 'critical' || impact === 'serious'
|
|
61
|
+
? 'high'
|
|
62
|
+
: impact === 'moderate'
|
|
63
|
+
? 'medium'
|
|
64
|
+
: 'low';
|
|
65
|
+
addGap({
|
|
66
|
+
id: randomUUID(),
|
|
67
|
+
path: route.path,
|
|
68
|
+
severity,
|
|
69
|
+
reason: `A11y violation ${violation.id} (${violation.impact}): ${violation.helpUrl}`,
|
|
70
|
+
category: 'a11y',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const highCount = gaps.filter((g) => g.severity === 'high').length;
|
|
75
|
+
const mediumCount = gaps.filter((g) => g.severity === 'medium').length;
|
|
76
|
+
const lowCount = gaps.filter((g) => g.severity === 'low').length;
|
|
77
|
+
let releaseConfidence = Math.max(0, 100 - highCount * 20 - mediumCount * 8 - lowCount * 3);
|
|
78
|
+
const pagesScanned = routes.routes.length;
|
|
79
|
+
if (pagesScanned < config.minPagesForConfidence) {
|
|
80
|
+
releaseConfidence = Math.min(releaseConfidence, 40);
|
|
81
|
+
}
|
|
82
|
+
let coverageWarning;
|
|
83
|
+
if (routes.budgetExceeded) {
|
|
84
|
+
coverageWarning = 'budget-exceeded';
|
|
85
|
+
}
|
|
86
|
+
else if (hasNavigationFailures) {
|
|
87
|
+
coverageWarning = 'navigation-failures';
|
|
88
|
+
}
|
|
89
|
+
else if (pagesScanned < config.minPagesForConfidence) {
|
|
90
|
+
coverageWarning = 'low-coverage';
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
analyzedAt: new Date().toISOString(),
|
|
94
|
+
mode,
|
|
95
|
+
releaseConfidence,
|
|
96
|
+
coveragePagesScanned: pagesScanned,
|
|
97
|
+
coverageBudgetExceeded: routes.budgetExceeded,
|
|
98
|
+
coverageWarning,
|
|
99
|
+
gaps,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AppExplorer } from './explorer.interface.js';
|
|
2
|
+
import { type RouteInventory } from '../schemas/route-inventory.schema.js';
|
|
3
|
+
import type { HarnessConfig } from '../schemas/config.schema.js';
|
|
4
|
+
export declare class PlaywrightExplorer implements AppExplorer {
|
|
5
|
+
explore(baseUrl: string, config: HarnessConfig): Promise<RouteInventory>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=playwright-explorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-explorer.d.ts","sourceRoot":"","sources":["../../src/tools/playwright-explorer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAwB,KAAK,cAAc,EAAc,MAAM,sCAAsC,CAAC;AAC7G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEjE,qBAAa,kBAAmB,YAAW,WAAW;IAC9C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;CA6J/E"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { chromium } from '@playwright/test';
|
|
2
|
+
import { AxeBuilder } from '@axe-core/playwright';
|
|
3
|
+
import { createAuthenticatedContext } from './auth.js';
|
|
4
|
+
import { RouteInventorySchema } from '../schemas/route-inventory.schema.js';
|
|
5
|
+
export class PlaywrightExplorer {
|
|
6
|
+
async explore(baseUrl, config) {
|
|
7
|
+
const browser = await chromium.launch({ headless: true });
|
|
8
|
+
let context;
|
|
9
|
+
try {
|
|
10
|
+
context = await createAuthenticatedContext(browser, config.auth, config.timeoutMs);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
await browser.close();
|
|
14
|
+
throw new Error(`Authentication failed: ${String(err)}. Check your auth config and credentials.`);
|
|
15
|
+
}
|
|
16
|
+
if (config.auth) {
|
|
17
|
+
const label = config.auth.type === 'form-login' ? config.auth.credentials.username : 'storage-state';
|
|
18
|
+
console.error(`[qulib] authenticated as ${label}`);
|
|
19
|
+
}
|
|
20
|
+
const visited = new Set();
|
|
21
|
+
const queue = [baseUrl];
|
|
22
|
+
const routes = [];
|
|
23
|
+
let budgetExceeded = false;
|
|
24
|
+
try {
|
|
25
|
+
while (queue.length > 0) {
|
|
26
|
+
if (visited.size >= config.maxPagesToScan) {
|
|
27
|
+
budgetExceeded = queue.length > 0;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
const url = queue.shift();
|
|
31
|
+
if (!url) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const normalized = url.split('?')[0].split('#')[0];
|
|
35
|
+
if (visited.has(normalized))
|
|
36
|
+
continue;
|
|
37
|
+
visited.add(normalized);
|
|
38
|
+
const page = await context.newPage();
|
|
39
|
+
const consoleErrors = [];
|
|
40
|
+
page.on('console', (msg) => {
|
|
41
|
+
if (msg.type() === 'error') {
|
|
42
|
+
consoleErrors.push(msg.text());
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
try {
|
|
46
|
+
await page.goto(url, {
|
|
47
|
+
timeout: config.timeoutMs,
|
|
48
|
+
waitUntil: 'domcontentloaded',
|
|
49
|
+
});
|
|
50
|
+
const pageTitle = await page.title();
|
|
51
|
+
const formCount = await page.locator('form').count();
|
|
52
|
+
const buttonLabels = await page.locator('button').allInnerTexts();
|
|
53
|
+
const hrefs = await page.evaluate(() => Array.from(document.querySelectorAll('a[href]'))
|
|
54
|
+
.map((a) => a.href)
|
|
55
|
+
.filter(Boolean));
|
|
56
|
+
const base = new URL(baseUrl);
|
|
57
|
+
const internalLinks = hrefs
|
|
58
|
+
.filter((href) => {
|
|
59
|
+
try {
|
|
60
|
+
const u = new URL(href);
|
|
61
|
+
return u.origin === base.origin;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.map((href) => href.split('?')[0].split('#')[0]);
|
|
68
|
+
const uniqueInternal = [...new Set(internalLinks)];
|
|
69
|
+
for (const link of uniqueInternal) {
|
|
70
|
+
if (!visited.has(link) && !queue.includes(link)) {
|
|
71
|
+
queue.push(link);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const brokenLinks = [];
|
|
75
|
+
for (const link of uniqueInternal.slice(0, 20)) {
|
|
76
|
+
try {
|
|
77
|
+
const response = await page.request.head(link, { timeout: 5000 });
|
|
78
|
+
if (response.status() >= 400) {
|
|
79
|
+
brokenLinks.push({ url: link, status: response.status() });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
brokenLinks.push({ url: link, status: null, reason: String(err) });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
let a11yViolations = [];
|
|
87
|
+
try {
|
|
88
|
+
const axeResults = await new AxeBuilder({ page })
|
|
89
|
+
.withTags(['wcag2a', 'wcag2aa'])
|
|
90
|
+
.analyze();
|
|
91
|
+
a11yViolations = axeResults.violations.map((v) => ({
|
|
92
|
+
id: v.id,
|
|
93
|
+
impact: v.impact ?? 'unknown',
|
|
94
|
+
helpUrl: v.helpUrl,
|
|
95
|
+
nodeCount: v.nodes.length,
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
consoleErrors.push(`axe-core failure: ${String(err)}`);
|
|
100
|
+
}
|
|
101
|
+
const path = new URL(url).pathname || '/';
|
|
102
|
+
routes.push({
|
|
103
|
+
path,
|
|
104
|
+
pageTitle,
|
|
105
|
+
links: uniqueInternal,
|
|
106
|
+
formCount,
|
|
107
|
+
buttonLabels: buttonLabels.map((b) => b.trim()).filter(Boolean),
|
|
108
|
+
consoleErrors,
|
|
109
|
+
brokenLinks,
|
|
110
|
+
a11yViolations,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
const path = (() => {
|
|
115
|
+
try {
|
|
116
|
+
return new URL(url).pathname || '/';
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return url;
|
|
120
|
+
}
|
|
121
|
+
})();
|
|
122
|
+
routes.push({
|
|
123
|
+
path,
|
|
124
|
+
pageTitle: '',
|
|
125
|
+
links: [],
|
|
126
|
+
formCount: 0,
|
|
127
|
+
buttonLabels: [],
|
|
128
|
+
consoleErrors: [`Navigation error: ${String(err)}`],
|
|
129
|
+
brokenLinks: [],
|
|
130
|
+
a11yViolations: [],
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
await page.close();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
await context.close();
|
|
140
|
+
await browser.close();
|
|
141
|
+
}
|
|
142
|
+
return RouteInventorySchema.parse({
|
|
143
|
+
scannedAt: new Date().toISOString(),
|
|
144
|
+
baseUrl,
|
|
145
|
+
routes,
|
|
146
|
+
pagesSkipped: budgetExceeded ? queue.length : 0,
|
|
147
|
+
budgetExceeded,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-scanner.d.ts","sourceRoot":"","sources":["../../src/tools/repo-scanner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAiC3F,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA4HtE"}
|