qa360 2.0.11 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/ai.js +26 -14
- package/dist/commands/ask.d.ts +75 -23
- package/dist/commands/ask.js +413 -265
- package/dist/commands/crawl.d.ts +24 -0
- package/dist/commands/crawl.js +121 -0
- package/dist/commands/history.js +38 -3
- package/dist/commands/init.d.ts +89 -95
- package/dist/commands/init.js +282 -200
- package/dist/commands/run.d.ts +1 -0
- package/dist/core/adapters/playwright-ui.d.ts +45 -7
- package/dist/core/adapters/playwright-ui.js +365 -59
- package/dist/core/assertions/engine.d.ts +51 -0
- package/dist/core/assertions/engine.js +530 -0
- package/dist/core/assertions/index.d.ts +11 -0
- package/dist/core/assertions/index.js +11 -0
- package/dist/core/assertions/types.d.ts +121 -0
- package/dist/core/assertions/types.js +37 -0
- package/dist/core/crawler/index.d.ts +57 -0
- package/dist/core/crawler/index.js +281 -0
- package/dist/core/crawler/journey-generator.d.ts +49 -0
- package/dist/core/crawler/journey-generator.js +412 -0
- package/dist/core/crawler/page-analyzer.d.ts +88 -0
- package/dist/core/crawler/page-analyzer.js +709 -0
- package/dist/core/crawler/selector-generator.d.ts +34 -0
- package/dist/core/crawler/selector-generator.js +240 -0
- package/dist/core/crawler/types.d.ts +353 -0
- package/dist/core/crawler/types.js +6 -0
- package/dist/core/generation/crawler-pack-generator.d.ts +44 -0
- package/dist/core/generation/crawler-pack-generator.js +231 -0
- package/dist/core/generation/index.d.ts +2 -0
- package/dist/core/generation/index.js +2 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +4 -0
- package/dist/core/types/pack-v1.d.ts +90 -0
- package/dist/index.js +6 -2
- package/examples/accessibility.yml +39 -16
- package/examples/api-basic.yml +19 -14
- package/examples/complete.yml +134 -42
- package/examples/fullstack.yml +66 -31
- package/examples/security.yml +47 -15
- package/examples/ui-basic.yml +16 -12
- package/package.json +3 -2
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Journey Generator
|
|
3
|
+
*
|
|
4
|
+
* Automatically generates user journeys from crawled pages
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Journey Generator class
|
|
8
|
+
*/
|
|
9
|
+
export class JourneyGenerator {
|
|
10
|
+
siteMap;
|
|
11
|
+
constructor(siteMap) {
|
|
12
|
+
this.siteMap = siteMap;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generate all user journeys from the site map
|
|
16
|
+
*/
|
|
17
|
+
generateJourneys() {
|
|
18
|
+
const journeys = [];
|
|
19
|
+
// Generate authentication journeys
|
|
20
|
+
journeys.push(...this.generateAuthJourneys());
|
|
21
|
+
// Generate navigation journeys
|
|
22
|
+
journeys.push(...this.generateNavigationJourneys());
|
|
23
|
+
// Generate form submission journeys
|
|
24
|
+
journeys.push(...this.generateFormJourneys());
|
|
25
|
+
// Generate search journeys
|
|
26
|
+
journeys.push(...this.generateSearchJourneys());
|
|
27
|
+
// Generate transaction journeys (checkout, etc.)
|
|
28
|
+
journeys.push(...this.generateTransactionJourneys());
|
|
29
|
+
return journeys.filter(j => j.steps.length > 1);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Generate authentication journeys (login, signup)
|
|
33
|
+
*/
|
|
34
|
+
generateAuthJourneys() {
|
|
35
|
+
const journeys = [];
|
|
36
|
+
// Find login page
|
|
37
|
+
const loginPage = this.siteMap.pages.find(p => p.pageType === 'login');
|
|
38
|
+
if (loginPage) {
|
|
39
|
+
const loginForm = loginPage.elements.forms.find(f => f.purpose === 'login');
|
|
40
|
+
if (loginForm) {
|
|
41
|
+
journeys.push({
|
|
42
|
+
name: 'User Login',
|
|
43
|
+
description: 'Standard user login flow with email and password',
|
|
44
|
+
type: 'authentication',
|
|
45
|
+
entryPoint: loginPage.url,
|
|
46
|
+
finalUrl: this.siteMap.pages.find(p => p.pageType === 'dashboard')?.url,
|
|
47
|
+
estimatedDuration: 5,
|
|
48
|
+
steps: this.generateLoginSteps(loginPage, loginForm),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Find signup page
|
|
53
|
+
const signupPage = this.siteMap.pages.find(p => p.pageType === 'signup');
|
|
54
|
+
if (signupPage) {
|
|
55
|
+
const signupForm = signupPage.elements.forms.find(f => f.purpose === 'signup');
|
|
56
|
+
if (signupForm) {
|
|
57
|
+
journeys.push({
|
|
58
|
+
name: 'User Registration',
|
|
59
|
+
description: 'New user registration flow',
|
|
60
|
+
type: 'authentication',
|
|
61
|
+
entryPoint: signupPage.url,
|
|
62
|
+
estimatedDuration: 8,
|
|
63
|
+
steps: this.generateSignupSteps(signupPage, signupForm),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return journeys;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Generate login flow steps
|
|
71
|
+
*/
|
|
72
|
+
generateLoginSteps(page, form) {
|
|
73
|
+
const steps = [];
|
|
74
|
+
// Navigate to login page
|
|
75
|
+
steps.push({
|
|
76
|
+
order: steps.length + 1,
|
|
77
|
+
description: 'Navigate to login page',
|
|
78
|
+
action: 'navigate',
|
|
79
|
+
value: page.url,
|
|
80
|
+
expected: { url: page.url },
|
|
81
|
+
});
|
|
82
|
+
// Fill email field
|
|
83
|
+
const emailField = form.fields.find(f => f.inputType === 'email' || f.name?.includes('email') || f.name?.includes('username'));
|
|
84
|
+
if (emailField) {
|
|
85
|
+
steps.push({
|
|
86
|
+
order: steps.length + 1,
|
|
87
|
+
description: 'Enter email/username',
|
|
88
|
+
action: 'fill',
|
|
89
|
+
selector: emailField.selector,
|
|
90
|
+
value: '${TEST_USER_EMAIL}',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Fill password field
|
|
94
|
+
const passwordField = form.fields.find(f => f.inputType === 'password');
|
|
95
|
+
if (passwordField) {
|
|
96
|
+
steps.push({
|
|
97
|
+
order: steps.length + 1,
|
|
98
|
+
description: 'Enter password',
|
|
99
|
+
action: 'fill',
|
|
100
|
+
selector: passwordField.selector,
|
|
101
|
+
value: '${TEST_USER_PASSWORD}',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Click submit
|
|
105
|
+
if (form.submitButton) {
|
|
106
|
+
steps.push({
|
|
107
|
+
order: steps.length + 1,
|
|
108
|
+
description: 'Click login button',
|
|
109
|
+
action: 'click',
|
|
110
|
+
selector: form.submitButton.selector,
|
|
111
|
+
expected: { visible: '.user-menu, .dashboard, .welcome' },
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return steps;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Generate signup flow steps
|
|
118
|
+
*/
|
|
119
|
+
generateSignupSteps(page, form) {
|
|
120
|
+
const steps = [];
|
|
121
|
+
// Navigate to signup page
|
|
122
|
+
steps.push({
|
|
123
|
+
order: steps.length + 1,
|
|
124
|
+
description: 'Navigate to registration page',
|
|
125
|
+
action: 'navigate',
|
|
126
|
+
value: page.url,
|
|
127
|
+
expected: { url: page.url },
|
|
128
|
+
});
|
|
129
|
+
// Fill all required fields
|
|
130
|
+
for (const field of form.fields) {
|
|
131
|
+
if (!field.required)
|
|
132
|
+
continue;
|
|
133
|
+
const step = {
|
|
134
|
+
order: steps.length + 1,
|
|
135
|
+
description: `Enter ${field.name || field.inputType}`,
|
|
136
|
+
action: field.inputType === 'select-one' ? 'select' : 'fill',
|
|
137
|
+
selector: field.selector,
|
|
138
|
+
};
|
|
139
|
+
// Set test value based on field type
|
|
140
|
+
if (field.inputType === 'email') {
|
|
141
|
+
step.value = '${TEST_USER_EMAIL}';
|
|
142
|
+
}
|
|
143
|
+
else if (field.inputType === 'password') {
|
|
144
|
+
step.value = '${TEST_USER_PASSWORD}';
|
|
145
|
+
}
|
|
146
|
+
else if (field.inputType === 'select-one' && field.options && field.options.length > 0) {
|
|
147
|
+
step.value = field.options[0];
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
step.value = 'test-value';
|
|
151
|
+
}
|
|
152
|
+
steps.push(step);
|
|
153
|
+
}
|
|
154
|
+
// Click submit
|
|
155
|
+
if (form.submitButton) {
|
|
156
|
+
steps.push({
|
|
157
|
+
order: steps.length + 1,
|
|
158
|
+
description: 'Submit registration form',
|
|
159
|
+
action: 'click',
|
|
160
|
+
selector: form.submitButton.selector,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return steps;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Generate navigation journeys
|
|
167
|
+
*/
|
|
168
|
+
generateNavigationJourneys() {
|
|
169
|
+
const journeys = [];
|
|
170
|
+
// Homepage navigation journey
|
|
171
|
+
const homepage = this.siteMap.pages.find(p => p.pageType === 'homepage');
|
|
172
|
+
if (homepage) {
|
|
173
|
+
const steps = [];
|
|
174
|
+
// Start at homepage
|
|
175
|
+
steps.push({
|
|
176
|
+
order: steps.length + 1,
|
|
177
|
+
description: 'Navigate to homepage',
|
|
178
|
+
action: 'navigate',
|
|
179
|
+
value: homepage.url,
|
|
180
|
+
expected: { url: homepage.url },
|
|
181
|
+
});
|
|
182
|
+
// Add navigation menu clicks
|
|
183
|
+
if (homepage.navigation.main) {
|
|
184
|
+
for (const link of homepage.navigation.main.items.slice(0, 5)) {
|
|
185
|
+
steps.push({
|
|
186
|
+
order: steps.length + 1,
|
|
187
|
+
description: `Navigate to "${link.text || link.selector}"`,
|
|
188
|
+
action: 'click',
|
|
189
|
+
selector: link.selector,
|
|
190
|
+
expected: { url: link.url },
|
|
191
|
+
});
|
|
192
|
+
steps.push({
|
|
193
|
+
order: steps.length + 1,
|
|
194
|
+
description: 'Wait for page load',
|
|
195
|
+
action: 'waitForNavigation',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
journeys.push({
|
|
200
|
+
name: 'Main Navigation Flow',
|
|
201
|
+
description: 'Navigate through main menu items',
|
|
202
|
+
type: 'navigation',
|
|
203
|
+
entryPoint: homepage.url,
|
|
204
|
+
estimatedDuration: steps.length * 3,
|
|
205
|
+
steps,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return journeys;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Generate form submission journeys
|
|
212
|
+
*/
|
|
213
|
+
generateFormJourneys() {
|
|
214
|
+
const journeys = [];
|
|
215
|
+
for (const page of this.siteMap.pages) {
|
|
216
|
+
for (const form of page.elements.forms) {
|
|
217
|
+
if (form.purpose === 'login' || form.purpose === 'signup')
|
|
218
|
+
continue;
|
|
219
|
+
const steps = [];
|
|
220
|
+
// Navigate to page
|
|
221
|
+
steps.push({
|
|
222
|
+
order: steps.length + 1,
|
|
223
|
+
description: `Navigate to ${form.purpose} form`,
|
|
224
|
+
action: 'navigate',
|
|
225
|
+
value: page.url,
|
|
226
|
+
});
|
|
227
|
+
// Fill form fields
|
|
228
|
+
for (const field of form.fields) {
|
|
229
|
+
if (!field.required && Math.random() > 0.5)
|
|
230
|
+
continue;
|
|
231
|
+
const step = {
|
|
232
|
+
order: steps.length + 1,
|
|
233
|
+
description: `Fill ${field.name || field.inputType} field`,
|
|
234
|
+
action: field.inputType === 'select-one' ? 'select' : 'fill',
|
|
235
|
+
selector: field.selector,
|
|
236
|
+
};
|
|
237
|
+
// Set appropriate test value
|
|
238
|
+
if (field.inputType === 'email') {
|
|
239
|
+
step.value = 'test@example.com';
|
|
240
|
+
}
|
|
241
|
+
else if (field.inputType === 'tel') {
|
|
242
|
+
step.value = '+1234567890';
|
|
243
|
+
}
|
|
244
|
+
else if (field.inputType === 'number') {
|
|
245
|
+
const min = field.validation?.min;
|
|
246
|
+
const max = field.validation?.max;
|
|
247
|
+
const num = typeof min === 'number' ? min + 1 : 1;
|
|
248
|
+
step.value = String(typeof max === 'number' ? Math.min(num, max - 1) : num);
|
|
249
|
+
}
|
|
250
|
+
else if (field.inputType === 'select-one' && field.options && field.options.length > 0) {
|
|
251
|
+
step.value = field.options[0];
|
|
252
|
+
}
|
|
253
|
+
else if (field.inputType === 'checkbox') {
|
|
254
|
+
step.action = 'check';
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
step.value = 'Test value';
|
|
258
|
+
}
|
|
259
|
+
steps.push(step);
|
|
260
|
+
}
|
|
261
|
+
// Submit form
|
|
262
|
+
if (form.submitButton) {
|
|
263
|
+
steps.push({
|
|
264
|
+
order: steps.length + 1,
|
|
265
|
+
description: `Submit ${form.purpose} form`,
|
|
266
|
+
action: 'click',
|
|
267
|
+
selector: form.submitButton.selector,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
journeys.push({
|
|
271
|
+
name: `${form.purpose.charAt(0).toUpperCase() + form.purpose.slice(1)} Form Submission`,
|
|
272
|
+
description: `Fill and submit the ${form.purpose} form`,
|
|
273
|
+
type: 'form-submission',
|
|
274
|
+
entryPoint: page.url,
|
|
275
|
+
estimatedDuration: steps.length * 2,
|
|
276
|
+
steps,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return journeys;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Generate search journeys
|
|
284
|
+
*/
|
|
285
|
+
generateSearchJourneys() {
|
|
286
|
+
const journeys = [];
|
|
287
|
+
for (const page of this.siteMap.pages) {
|
|
288
|
+
const searchForm = page.elements.forms.find(f => f.purpose === 'search');
|
|
289
|
+
if (!searchForm)
|
|
290
|
+
continue;
|
|
291
|
+
const steps = [];
|
|
292
|
+
// Navigate to page
|
|
293
|
+
steps.push({
|
|
294
|
+
order: steps.length + 1,
|
|
295
|
+
description: 'Navigate to search page',
|
|
296
|
+
action: 'navigate',
|
|
297
|
+
value: page.url,
|
|
298
|
+
});
|
|
299
|
+
// Fill search field
|
|
300
|
+
const searchField = searchForm.fields.find(f => f.name?.includes('q') || f.name?.includes('search'));
|
|
301
|
+
if (searchField) {
|
|
302
|
+
steps.push({
|
|
303
|
+
order: steps.length + 1,
|
|
304
|
+
description: 'Enter search query',
|
|
305
|
+
action: 'fill',
|
|
306
|
+
selector: searchField.selector,
|
|
307
|
+
value: '${SEARCH_QUERY}',
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
// Submit search
|
|
311
|
+
if (searchForm.submitButton) {
|
|
312
|
+
steps.push({
|
|
313
|
+
order: steps.length + 1,
|
|
314
|
+
description: 'Click search button',
|
|
315
|
+
action: 'click',
|
|
316
|
+
selector: searchForm.submitButton.selector,
|
|
317
|
+
expected: { visible: '.search-results, .results, [role="main"]' },
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
// Press Enter
|
|
322
|
+
steps.push({
|
|
323
|
+
order: steps.length + 1,
|
|
324
|
+
description: 'Submit search (Enter)',
|
|
325
|
+
action: 'press',
|
|
326
|
+
value: 'Enter',
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
journeys.push({
|
|
330
|
+
name: 'Search Query',
|
|
331
|
+
description: 'Perform a search query and verify results',
|
|
332
|
+
type: 'search',
|
|
333
|
+
entryPoint: page.url,
|
|
334
|
+
estimatedDuration: 3,
|
|
335
|
+
steps,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
return journeys;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Generate transaction journeys (checkout, booking, etc.)
|
|
342
|
+
*/
|
|
343
|
+
generateTransactionJourneys() {
|
|
344
|
+
const journeys = [];
|
|
345
|
+
// Find checkout flow
|
|
346
|
+
const checkoutPage = this.siteMap.pages.find(p => p.elements.forms.some(f => f.purpose === 'checkout'));
|
|
347
|
+
if (checkoutPage) {
|
|
348
|
+
const checkoutForm = checkoutPage.elements.forms.find(f => f.purpose === 'checkout');
|
|
349
|
+
if (checkoutForm) {
|
|
350
|
+
const steps = [];
|
|
351
|
+
steps.push({
|
|
352
|
+
order: steps.length + 1,
|
|
353
|
+
description: 'Navigate to checkout',
|
|
354
|
+
action: 'navigate',
|
|
355
|
+
value: checkoutPage.url,
|
|
356
|
+
});
|
|
357
|
+
// Fill checkout fields
|
|
358
|
+
for (const field of checkoutForm.fields) {
|
|
359
|
+
const step = {
|
|
360
|
+
order: steps.length + 1,
|
|
361
|
+
description: `Fill ${field.name || field.inputType}`,
|
|
362
|
+
action: field.inputType === 'select-one' ? 'select' : 'fill',
|
|
363
|
+
selector: field.selector,
|
|
364
|
+
};
|
|
365
|
+
// Set test payment data
|
|
366
|
+
if (field.name?.includes('card')) {
|
|
367
|
+
step.value = '4111111111111111'; // Test card number
|
|
368
|
+
}
|
|
369
|
+
else if (field.name?.includes('cvv') || field.name?.includes('cvc')) {
|
|
370
|
+
step.value = '123';
|
|
371
|
+
}
|
|
372
|
+
else if (field.name?.includes('expiry') || field.name?.includes('exp')) {
|
|
373
|
+
step.value = '12/25';
|
|
374
|
+
}
|
|
375
|
+
else if (field.inputType === 'select-one' && field.options && field.options.length > 0) {
|
|
376
|
+
step.value = field.options[0];
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
step.value = 'Test value';
|
|
380
|
+
}
|
|
381
|
+
steps.push(step);
|
|
382
|
+
}
|
|
383
|
+
// Submit checkout
|
|
384
|
+
if (checkoutForm.submitButton) {
|
|
385
|
+
steps.push({
|
|
386
|
+
order: steps.length + 1,
|
|
387
|
+
description: 'Complete purchase',
|
|
388
|
+
action: 'click',
|
|
389
|
+
selector: checkoutForm.submitButton.selector,
|
|
390
|
+
expected: { visible: '.order-confirmation, .thank-you, [role="status"]' },
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
journeys.push({
|
|
394
|
+
name: 'Checkout Flow',
|
|
395
|
+
description: 'Complete a purchase flow',
|
|
396
|
+
type: 'transaction',
|
|
397
|
+
entryPoint: checkoutPage.url,
|
|
398
|
+
estimatedDuration: 10,
|
|
399
|
+
steps,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return journeys;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Generate journeys from a site map
|
|
408
|
+
*/
|
|
409
|
+
export function generateJourneys(siteMap) {
|
|
410
|
+
const generator = new JourneyGenerator(siteMap);
|
|
411
|
+
return generator.generateJourneys();
|
|
412
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Page Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes web pages to discover elements, forms, and patterns
|
|
5
|
+
*/
|
|
6
|
+
import type { CrawlOptions, PageDefinition } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Page Analyzer class
|
|
9
|
+
*/
|
|
10
|
+
export declare class PageAnalyzer {
|
|
11
|
+
private browser?;
|
|
12
|
+
private context?;
|
|
13
|
+
private page?;
|
|
14
|
+
private options;
|
|
15
|
+
constructor(options: CrawlOptions);
|
|
16
|
+
/**
|
|
17
|
+
* Initialize browser
|
|
18
|
+
*/
|
|
19
|
+
private initBrowser;
|
|
20
|
+
/**
|
|
21
|
+
* Perform authentication if configured
|
|
22
|
+
*/
|
|
23
|
+
private performAuth;
|
|
24
|
+
/**
|
|
25
|
+
* Analyze a single page
|
|
26
|
+
*/
|
|
27
|
+
analyze(url: string, depth: number): Promise<PageDefinition>;
|
|
28
|
+
/**
|
|
29
|
+
* Discover all interactive elements on the page
|
|
30
|
+
*/
|
|
31
|
+
private discoverElements;
|
|
32
|
+
/**
|
|
33
|
+
* Discover buttons
|
|
34
|
+
*/
|
|
35
|
+
private discoverButtons;
|
|
36
|
+
/**
|
|
37
|
+
* Discover links
|
|
38
|
+
*/
|
|
39
|
+
private discoverLinks;
|
|
40
|
+
/**
|
|
41
|
+
* Discover forms
|
|
42
|
+
*/
|
|
43
|
+
private discoverForms;
|
|
44
|
+
/**
|
|
45
|
+
* Discover fields within a form
|
|
46
|
+
*/
|
|
47
|
+
private discoverFormFields;
|
|
48
|
+
/**
|
|
49
|
+
* Detect form purpose based on fields
|
|
50
|
+
*/
|
|
51
|
+
private detectFormPurpose;
|
|
52
|
+
/**
|
|
53
|
+
* Calculate confidence for form purpose detection
|
|
54
|
+
*/
|
|
55
|
+
private calculateFormPurposeConfidence;
|
|
56
|
+
/**
|
|
57
|
+
* Discover standalone input fields (not in forms)
|
|
58
|
+
*/
|
|
59
|
+
private discoverInputs;
|
|
60
|
+
/**
|
|
61
|
+
* Discover select elements
|
|
62
|
+
*/
|
|
63
|
+
private discoverSelects;
|
|
64
|
+
/**
|
|
65
|
+
* Discover checkboxes
|
|
66
|
+
*/
|
|
67
|
+
private discoverCheckboxes;
|
|
68
|
+
/**
|
|
69
|
+
* Discover radio buttons
|
|
70
|
+
*/
|
|
71
|
+
private discoverRadios;
|
|
72
|
+
/**
|
|
73
|
+
* Analyze page navigation structure
|
|
74
|
+
*/
|
|
75
|
+
private analyzeNavigation;
|
|
76
|
+
/**
|
|
77
|
+
* Run accessibility scan using axe-core
|
|
78
|
+
*/
|
|
79
|
+
private runAccessibilityScan;
|
|
80
|
+
/**
|
|
81
|
+
* Detect page type
|
|
82
|
+
*/
|
|
83
|
+
private detectPageType;
|
|
84
|
+
/**
|
|
85
|
+
* Clean up resources
|
|
86
|
+
*/
|
|
87
|
+
cleanup(): Promise<void>;
|
|
88
|
+
}
|