@sudobility/testomniac_runner_service 0.1.172 → 0.1.173
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/browser/performance-snapshot.d.ts +27 -0
- package/dist/browser/performance-snapshot.d.ts.map +1 -0
- package/dist/browser/performance-snapshot.js +75 -0
- package/dist/browser/performance-snapshot.js.map +1 -0
- package/dist/expertise/accessibility-expertise.d.ts +4 -0
- package/dist/expertise/accessibility-expertise.d.ts.map +1 -1
- package/dist/expertise/accessibility-expertise.js +132 -1
- package/dist/expertise/accessibility-expertise.js.map +1 -1
- package/dist/expertise/content-expertise.d.ts +5 -0
- package/dist/expertise/content-expertise.d.ts.map +1 -1
- package/dist/expertise/content-expertise.js +193 -1
- package/dist/expertise/content-expertise.js.map +1 -1
- package/dist/expertise/page-health-outcomes.d.ts +5 -0
- package/dist/expertise/page-health-outcomes.d.ts.map +1 -0
- package/dist/expertise/page-health-outcomes.js +16 -0
- package/dist/expertise/page-health-outcomes.js.map +1 -0
- package/dist/expertise/performance-expertise.d.ts +10 -0
- package/dist/expertise/performance-expertise.d.ts.map +1 -1
- package/dist/expertise/performance-expertise.js +245 -1
- package/dist/expertise/performance-expertise.js.map +1 -1
- package/dist/expertise/rule-id.d.ts +4 -0
- package/dist/expertise/rule-id.d.ts.map +1 -0
- package/dist/expertise/rule-id.js +7 -0
- package/dist/expertise/rule-id.js.map +1 -0
- package/dist/expertise/security-expertise.d.ts +4 -0
- package/dist/expertise/security-expertise.d.ts.map +1 -1
- package/dist/expertise/security-expertise.js +116 -1
- package/dist/expertise/security-expertise.js.map +1 -1
- package/dist/expertise/seo-expertise.d.ts +7 -0
- package/dist/expertise/seo-expertise.d.ts.map +1 -1
- package/dist/expertise/seo-expertise.js +169 -6
- package/dist/expertise/seo-expertise.js.map +1 -1
- package/dist/expertise/tester-expertise.d.ts.map +1 -1
- package/dist/expertise/tester-expertise.js +96 -41
- package/dist/expertise/tester-expertise.js.map +1 -1
- package/dist/expertise/types.d.ts +6 -1
- package/dist/expertise/types.d.ts.map +1 -1
- package/dist/expertise/ui-expertise.d.ts +4 -0
- package/dist/expertise/ui-expertise.d.ts.map +1 -1
- package/dist/expertise/ui-expertise.js +103 -1
- package/dist/expertise/ui-expertise.js.map +1 -1
- package/dist/orchestrator/test-interaction-executor.d.ts.map +1 -1
- package/dist/orchestrator/test-interaction-executor.js +60 -70
- package/dist/orchestrator/test-interaction-executor.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
import { ExpertiseRuleId } from "@sudobility/testomniac_types";
|
|
2
|
+
import { applyRuleIds } from "./rule-id";
|
|
3
|
+
const SEO_RULE_IDS = {
|
|
4
|
+
"Page should have a <title> tag": ExpertiseRuleId.SeoTitlePresent,
|
|
5
|
+
"Page title should be a useful search snippet length": ExpertiseRuleId.SeoTitleLength,
|
|
6
|
+
"Page should have meta description": ExpertiseRuleId.SeoMetaDescriptionPresent,
|
|
7
|
+
"Meta description should be a useful search snippet length": ExpertiseRuleId.SeoMetaDescriptionLength,
|
|
8
|
+
"Page should have meta keywords": ExpertiseRuleId.SeoMetaKeywordsPresent,
|
|
9
|
+
'Page should have <link rel="canonical">': ExpertiseRuleId.SeoCanonicalPresent,
|
|
10
|
+
"Canonical URL should point at this page or a valid parent": ExpertiseRuleId.SeoCanonicalTarget,
|
|
11
|
+
"Canonical URL should be parseable": ExpertiseRuleId.SeoCanonicalTarget,
|
|
12
|
+
"Robots meta should not accidentally block indexing": ExpertiseRuleId.SeoRobotsIndexability,
|
|
13
|
+
"Page should have meta property og:title": ExpertiseRuleId.SeoOpenGraphTitle,
|
|
14
|
+
"Page should have meta property og:description": ExpertiseRuleId.SeoOpenGraphDescription,
|
|
15
|
+
"Link text should describe the destination": ExpertiseRuleId.SeoAnchorTextDescriptive,
|
|
16
|
+
};
|
|
1
17
|
/**
|
|
2
18
|
* Checks if essential SEO meta tags are present in the page HTML.
|
|
3
19
|
* Creates warning outcomes for missing tags.
|
|
@@ -8,12 +24,25 @@ export class SeoExpertise {
|
|
|
8
24
|
const outcomes = [];
|
|
9
25
|
const html = context.html;
|
|
10
26
|
outcomes.push(this.checkTag(html, /<title[^>]*>([^<]+)<\/title>/i, "title"));
|
|
27
|
+
outcomes.push(this.checkTitleLength(html));
|
|
11
28
|
outcomes.push(this.checkMetaTag(html, "description"));
|
|
29
|
+
outcomes.push(this.checkMetaDescriptionLength(html));
|
|
12
30
|
outcomes.push(this.checkMetaTag(html, "keywords"));
|
|
13
31
|
outcomes.push(this.checkLinkTag(html, "canonical"));
|
|
32
|
+
outcomes.push(this.checkCanonicalTarget(html, context.currentUrl));
|
|
33
|
+
outcomes.push(this.checkRobotsIndexability(html));
|
|
14
34
|
outcomes.push(this.checkMetaProperty(html, "og:title"));
|
|
15
35
|
outcomes.push(this.checkMetaProperty(html, "og:description"));
|
|
16
|
-
|
|
36
|
+
outcomes.push(this.checkDescriptiveAnchors(html));
|
|
37
|
+
return applyRuleIds(outcomes, SEO_RULE_IDS);
|
|
38
|
+
}
|
|
39
|
+
extractTitle(html) {
|
|
40
|
+
return html.match(/<title[^>]*>([^<]+)<\/title>/i)?.[1]?.trim() ?? null;
|
|
41
|
+
}
|
|
42
|
+
extractMetaContent(html, name) {
|
|
43
|
+
const regex = new RegExp(`<meta[^>]+name=["']${name}["'][^>]+content=["']([^"']+)["']`, "i");
|
|
44
|
+
const regexAlt = new RegExp(`<meta[^>]+content=["']([^"']+)["'][^>]+name=["']${name}["']`, "i");
|
|
45
|
+
return (regex.exec(html) || regexAlt.exec(html))?.[1]?.trim() ?? null;
|
|
17
46
|
}
|
|
18
47
|
checkTag(html, regex, tagName) {
|
|
19
48
|
const match = regex.exec(html);
|
|
@@ -33,13 +62,11 @@ export class SeoExpertise {
|
|
|
33
62
|
};
|
|
34
63
|
}
|
|
35
64
|
checkMetaTag(html, name) {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const match = regex.exec(html) || regexAlt.exec(html);
|
|
39
|
-
if (match && match[1]?.trim()) {
|
|
65
|
+
const content = this.extractMetaContent(html, name);
|
|
66
|
+
if (content) {
|
|
40
67
|
return {
|
|
41
68
|
expected: `Page should have meta ${name}`,
|
|
42
|
-
observed: `Found meta ${name}: "${
|
|
69
|
+
observed: `Found meta ${name}: "${content.slice(0, 80)}"`,
|
|
43
70
|
result: "pass",
|
|
44
71
|
priority: 4,
|
|
45
72
|
};
|
|
@@ -88,5 +115,141 @@ export class SeoExpertise {
|
|
|
88
115
|
priority: 4,
|
|
89
116
|
};
|
|
90
117
|
}
|
|
118
|
+
checkTitleLength(html) {
|
|
119
|
+
const title = this.extractTitle(html);
|
|
120
|
+
if (!title) {
|
|
121
|
+
return {
|
|
122
|
+
expected: "Page title should be a useful search snippet length",
|
|
123
|
+
observed: "No title text available to measure",
|
|
124
|
+
result: "pass",
|
|
125
|
+
priority: 4,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (title.length < 15 || title.length > 70) {
|
|
129
|
+
return {
|
|
130
|
+
expected: "Page title should be a useful search snippet length",
|
|
131
|
+
observed: `Title is ${title.length} characters; target range is 15-70`,
|
|
132
|
+
result: "warning",
|
|
133
|
+
priority: 4,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
expected: "Page title should be a useful search snippet length",
|
|
138
|
+
observed: `Title length is ${title.length} characters`,
|
|
139
|
+
result: "pass",
|
|
140
|
+
priority: 4,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
checkMetaDescriptionLength(html) {
|
|
144
|
+
const description = this.extractMetaContent(html, "description");
|
|
145
|
+
if (!description) {
|
|
146
|
+
return {
|
|
147
|
+
expected: "Meta description should be a useful search snippet length",
|
|
148
|
+
observed: "No meta description available to measure",
|
|
149
|
+
result: "pass",
|
|
150
|
+
priority: 4,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (description.length < 50 || description.length > 170) {
|
|
154
|
+
return {
|
|
155
|
+
expected: "Meta description should be a useful search snippet length",
|
|
156
|
+
observed: `Meta description is ${description.length} characters; target range is 50-170`,
|
|
157
|
+
result: "warning",
|
|
158
|
+
priority: 4,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
expected: "Meta description should be a useful search snippet length",
|
|
163
|
+
observed: `Meta description length is ${description.length} characters`,
|
|
164
|
+
result: "pass",
|
|
165
|
+
priority: 4,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
checkCanonicalTarget(html, currentUrl) {
|
|
169
|
+
const href = html.match(/<link[^>]+rel=["']canonical["'][^>]+href=["']([^"']+)["']/i)?.[1] ?? null;
|
|
170
|
+
if (!href || !currentUrl) {
|
|
171
|
+
return {
|
|
172
|
+
expected: "Canonical URL should point at this page or a valid parent",
|
|
173
|
+
observed: href ? "Canonical URL present" : "No canonical URL present",
|
|
174
|
+
result: "pass",
|
|
175
|
+
priority: 4,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const canonical = new URL(href, currentUrl);
|
|
180
|
+
const current = new URL(currentUrl);
|
|
181
|
+
if (canonical.origin !== current.origin) {
|
|
182
|
+
return {
|
|
183
|
+
expected: "Canonical URL should point at this page or a valid parent",
|
|
184
|
+
observed: `Canonical points to a different origin: ${canonical.origin}`,
|
|
185
|
+
result: "warning",
|
|
186
|
+
priority: 3,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return {
|
|
192
|
+
expected: "Canonical URL should be parseable",
|
|
193
|
+
observed: `Canonical href is not a valid URL: ${href}`,
|
|
194
|
+
result: "warning",
|
|
195
|
+
priority: 3,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
expected: "Canonical URL should point at this page or a valid parent",
|
|
200
|
+
observed: "Canonical URL is same-origin and parseable",
|
|
201
|
+
result: "pass",
|
|
202
|
+
priority: 4,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
checkRobotsIndexability(html) {
|
|
206
|
+
const robots = this.extractMetaContent(html, "robots");
|
|
207
|
+
if (!robots) {
|
|
208
|
+
return {
|
|
209
|
+
expected: "Robots meta should not accidentally block indexing",
|
|
210
|
+
observed: "No robots meta tag detected",
|
|
211
|
+
result: "pass",
|
|
212
|
+
priority: 4,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (/\b(noindex|none)\b/i.test(robots)) {
|
|
216
|
+
return {
|
|
217
|
+
expected: "Robots meta should not accidentally block indexing",
|
|
218
|
+
observed: `Robots meta contains "${robots}"`,
|
|
219
|
+
result: "warning",
|
|
220
|
+
priority: 2,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
expected: "Robots meta should not accidentally block indexing",
|
|
225
|
+
observed: `Robots meta is "${robots}"`,
|
|
226
|
+
result: "pass",
|
|
227
|
+
priority: 4,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
checkDescriptiveAnchors(html) {
|
|
231
|
+
const anchors = html.match(/<a\b[^>]*href=["'][^"']+["'][^>]*>[\s\S]*?<\/a>/gi) ?? [];
|
|
232
|
+
const weak = anchors.filter(anchor => {
|
|
233
|
+
const text = anchor
|
|
234
|
+
.replace(/<[^>]+>/g, " ")
|
|
235
|
+
.replace(/\s+/g, " ")
|
|
236
|
+
.trim();
|
|
237
|
+
return /^(click here|learn more|read more|more|here)$/i.test(text);
|
|
238
|
+
});
|
|
239
|
+
if (weak.length > 0) {
|
|
240
|
+
return {
|
|
241
|
+
expected: "Link text should describe the destination",
|
|
242
|
+
observed: `${weak.length} link(s) use generic anchor text such as "learn more"`,
|
|
243
|
+
result: "warning",
|
|
244
|
+
priority: 4,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
expected: "Link text should describe the destination",
|
|
249
|
+
observed: "No generic anchor text detected",
|
|
250
|
+
result: "pass",
|
|
251
|
+
priority: 4,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
91
254
|
}
|
|
92
255
|
//# sourceMappingURL=seo-expertise.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seo-expertise.js","sourceRoot":"","sources":["../../src/expertise/seo-expertise.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"seo-expertise.js","sourceRoot":"","sources":["../../src/expertise/seo-expertise.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tester-expertise.d.ts","sourceRoot":"","sources":["../../src/expertise/tester-expertise.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tester-expertise.d.ts","sourceRoot":"","sources":["../../src/expertise/tester-expertise.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AA+DpE;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAC/C,IAAI,SAAY;IAEhB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,EAAE;IAe9C,OAAO,CAAC,gBAAgB;CA+HzB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ExpectationSeverity } from "@sudobility/testomniac_types";
|
|
2
|
-
import { checkNoConsoleErrors, checkNoNetworkErrors, checkPageLoaded, } from "./tester/core-checks";
|
|
1
|
+
import { ExpectationSeverity, ExpertiseRuleId, } from "@sudobility/testomniac_types";
|
|
2
|
+
import { checkNoConsoleErrors, checkMixedContent, checkNoNetworkErrors, checkPageLoaded, checkSlowResponses, } from "./tester/core-checks";
|
|
3
3
|
import { checkInputValue } from "./tester/text-input-checks";
|
|
4
4
|
import { checkSelectionState } from "./tester/selection-control-checks";
|
|
5
5
|
import { checkErrorStateCleared, checkErrorStateVisible, checkFormSubmittedSuccessfully, checkValidationMessageVisible, } from "./tester/form-checks";
|
|
@@ -34,91 +34,146 @@ export class TesterExpertise {
|
|
|
34
34
|
return outcomes;
|
|
35
35
|
}
|
|
36
36
|
checkExpectation(expectation, context) {
|
|
37
|
+
const withRuleId = (outcome) => ({
|
|
38
|
+
...outcome,
|
|
39
|
+
ruleId: outcome.ruleId ??
|
|
40
|
+
TESTER_RULE_IDS_BY_EXPECTATION_TYPE[expectation.expectationType] ??
|
|
41
|
+
ExpertiseRuleId.TesterUnknownExpectation,
|
|
42
|
+
});
|
|
37
43
|
switch (expectation.expectationType) {
|
|
38
44
|
case "page_loaded":
|
|
39
|
-
return checkPageLoaded(context, expectation.description);
|
|
45
|
+
return withRuleId(checkPageLoaded(context, expectation.description));
|
|
40
46
|
case "no_console_errors":
|
|
41
|
-
return checkNoConsoleErrors(context, expectation.description);
|
|
47
|
+
return withRuleId(checkNoConsoleErrors(context, expectation.description));
|
|
42
48
|
case "no_network_errors":
|
|
43
|
-
return checkNoNetworkErrors(context, expectation.description);
|
|
49
|
+
return withRuleId(checkNoNetworkErrors(context, expectation.description));
|
|
50
|
+
case "no_server_errors":
|
|
51
|
+
return withRuleId(checkNoNetworkErrors(context, expectation.description));
|
|
52
|
+
case "no_mixed_content":
|
|
53
|
+
return withRuleId(checkMixedContent(context, expectation.description));
|
|
54
|
+
case "load_time_within":
|
|
55
|
+
return withRuleId(checkSlowResponses(context, expectation.description));
|
|
44
56
|
case "input_value":
|
|
45
|
-
return checkInputValue(expectation, context);
|
|
57
|
+
return withRuleId(checkInputValue(expectation, context));
|
|
46
58
|
case "element_checked":
|
|
47
59
|
case "element_unchecked":
|
|
48
|
-
return checkSelectionState(expectation, context);
|
|
60
|
+
return withRuleId(checkSelectionState(expectation, context));
|
|
49
61
|
case "validation_message_visible":
|
|
50
|
-
return checkValidationMessageVisible(expectation, context);
|
|
62
|
+
return withRuleId(checkValidationMessageVisible(expectation, context));
|
|
51
63
|
case "error_state_visible":
|
|
52
|
-
return checkErrorStateVisible(expectation, context);
|
|
64
|
+
return withRuleId(checkErrorStateVisible(expectation, context));
|
|
53
65
|
case "error_state_cleared":
|
|
54
|
-
return checkErrorStateCleared(expectation, context);
|
|
66
|
+
return withRuleId(checkErrorStateCleared(expectation, context));
|
|
55
67
|
case "form_submitted_successfully":
|
|
56
|
-
return checkFormSubmittedSuccessfully(expectation, context);
|
|
68
|
+
return withRuleId(checkFormSubmittedSuccessfully(expectation, context));
|
|
57
69
|
case "required_error_shown_for_field":
|
|
58
|
-
return checkRequiredErrorShownForField(expectation, context);
|
|
70
|
+
return withRuleId(checkRequiredErrorShownForField(expectation, context));
|
|
59
71
|
case "field_error_clears_after_fix":
|
|
60
|
-
return checkFieldErrorClearsAfterFix(expectation, context);
|
|
72
|
+
return withRuleId(checkFieldErrorClearsAfterFix(expectation, context));
|
|
61
73
|
case "cart_summary_changed":
|
|
62
|
-
return checkCartSummaryChanged(expectation, context);
|
|
74
|
+
return withRuleId(checkCartSummaryChanged(expectation, context));
|
|
63
75
|
case "count_changed":
|
|
64
|
-
return checkCountChanged(expectation, context);
|
|
76
|
+
return withRuleId(checkCountChanged(expectation, context));
|
|
65
77
|
case "row_count_changed":
|
|
66
|
-
return checkRowCountChanged(expectation, context);
|
|
78
|
+
return withRuleId(checkRowCountChanged(expectation, context));
|
|
67
79
|
case "network_request_made":
|
|
68
|
-
return checkNetworkRequestMade(expectation, context);
|
|
80
|
+
return withRuleId(checkNetworkRequestMade(expectation, context));
|
|
69
81
|
case "no_duplicate_mutation_requests":
|
|
70
|
-
return checkNoDuplicateMutationRequests(expectation, context);
|
|
82
|
+
return withRuleId(checkNoDuplicateMutationRequests(expectation, context));
|
|
71
83
|
case "dialog_closed":
|
|
72
|
-
return checkDialogClosed(expectation, context);
|
|
84
|
+
return withRuleId(checkDialogClosed(expectation, context));
|
|
73
85
|
case "focus_returned":
|
|
74
|
-
return checkFocusReturned(expectation, context);
|
|
86
|
+
return withRuleId(checkFocusReturned(expectation, context));
|
|
75
87
|
case "feedback_visible":
|
|
76
|
-
return checkFeedbackVisible(expectation, context);
|
|
88
|
+
return withRuleId(checkFeedbackVisible(expectation, context));
|
|
77
89
|
case "feedback_not_duplicated":
|
|
78
|
-
return checkFeedbackNotDuplicated(expectation, context);
|
|
90
|
+
return withRuleId(checkFeedbackNotDuplicated(expectation, context));
|
|
79
91
|
case "state_persists_after_reload":
|
|
80
|
-
return checkStatePersistsAfterReload(expectation, context);
|
|
92
|
+
return withRuleId(checkStatePersistsAfterReload(expectation, context));
|
|
81
93
|
case "back_navigation_restores_state":
|
|
82
|
-
return checkBackNavigationRestoresState(expectation, context);
|
|
94
|
+
return withRuleId(checkBackNavigationRestoresState(expectation, context));
|
|
83
95
|
case "forward_navigation_reapplies_state":
|
|
84
|
-
return checkForwardNavigationReappliesState(expectation, context);
|
|
96
|
+
return withRuleId(checkForwardNavigationReappliesState(expectation, context));
|
|
85
97
|
case "expanded_state_changed":
|
|
86
|
-
return checkExpandedStateChanged(expectation, context);
|
|
98
|
+
return withRuleId(checkExpandedStateChanged(expectation, context));
|
|
87
99
|
case "element_focused":
|
|
88
|
-
return checkElementFocused(expectation, context);
|
|
100
|
+
return withRuleId(checkElementFocused(expectation, context));
|
|
89
101
|
case "results_changed":
|
|
90
|
-
return checkResultsChanged(expectation, context);
|
|
102
|
+
return withRuleId(checkResultsChanged(expectation, context));
|
|
91
103
|
case "results_restored":
|
|
92
|
-
return checkResultsRestored(expectation, context);
|
|
104
|
+
return withRuleId(checkResultsRestored(expectation, context));
|
|
93
105
|
case "collection_order_changed":
|
|
94
|
-
return checkCollectionOrderChanged(expectation, context);
|
|
106
|
+
return withRuleId(checkCollectionOrderChanged(expectation, context));
|
|
95
107
|
case "variant_state_changed":
|
|
96
|
-
return checkVariantStateChanged(expectation, context);
|
|
108
|
+
return withRuleId(checkVariantStateChanged(expectation, context));
|
|
97
109
|
case "empty_state_visible":
|
|
98
|
-
return checkEmptyStateVisible(expectation, context);
|
|
110
|
+
return withRuleId(checkEmptyStateVisible(expectation, context));
|
|
99
111
|
case "url_unchanged":
|
|
100
|
-
return checkUrlUnchanged(expectation, context);
|
|
112
|
+
return withRuleId(checkUrlUnchanged(expectation, context));
|
|
101
113
|
case "navigation_or_state_changed":
|
|
102
|
-
return checkNavigationOrStateChanged(expectation, context);
|
|
114
|
+
return withRuleId(checkNavigationOrStateChanged(expectation, context));
|
|
103
115
|
case "page_responsive":
|
|
104
|
-
return checkPageResponsive(expectation, context);
|
|
116
|
+
return withRuleId(checkPageResponsive(expectation, context));
|
|
105
117
|
case "loading_completes":
|
|
106
|
-
return checkLoadingCompletes(expectation, context);
|
|
118
|
+
return withRuleId(checkLoadingCompletes(expectation, context));
|
|
107
119
|
case "modal_opened":
|
|
108
|
-
return checkModalOpened(expectation, context);
|
|
120
|
+
return withRuleId(checkModalOpened(expectation, context));
|
|
109
121
|
case "media_loaded":
|
|
110
|
-
return checkMediaLoaded(expectation, context);
|
|
122
|
+
return withRuleId(checkMediaLoaded(expectation, context));
|
|
111
123
|
case "video_playable":
|
|
112
|
-
return checkVideoPlayable(expectation, context);
|
|
124
|
+
return withRuleId(checkVideoPlayable(expectation, context));
|
|
113
125
|
default:
|
|
114
126
|
// For expectations we don't have specific logic for yet, pass them
|
|
115
|
-
return {
|
|
127
|
+
return withRuleId({
|
|
116
128
|
expected: expectation.description,
|
|
117
129
|
observed: "Check not implemented — assumed pass",
|
|
118
130
|
result: "pass",
|
|
119
131
|
severity: ExpectationSeverity.Info,
|
|
120
|
-
};
|
|
132
|
+
});
|
|
121
133
|
}
|
|
122
134
|
}
|
|
123
135
|
}
|
|
136
|
+
const TESTER_RULE_IDS_BY_EXPECTATION_TYPE = {
|
|
137
|
+
page_loaded: ExpertiseRuleId.TesterPageLoaded,
|
|
138
|
+
no_console_errors: ExpertiseRuleId.TesterConsoleNoErrors,
|
|
139
|
+
no_network_errors: ExpertiseRuleId.TesterNetworkNoErrors,
|
|
140
|
+
no_server_errors: ExpertiseRuleId.TesterNetworkNoServerErrors,
|
|
141
|
+
no_mixed_content: ExpertiseRuleId.TesterNetworkNoMixedContent,
|
|
142
|
+
load_time_within: ExpertiseRuleId.TesterPerformanceLoadTimeWithin,
|
|
143
|
+
input_value: ExpertiseRuleId.TesterInputValue,
|
|
144
|
+
element_checked: ExpertiseRuleId.TesterSelectionState,
|
|
145
|
+
element_unchecked: ExpertiseRuleId.TesterSelectionState,
|
|
146
|
+
validation_message_visible: ExpertiseRuleId.TesterValidationMessageVisible,
|
|
147
|
+
error_state_visible: ExpertiseRuleId.TesterErrorStateVisible,
|
|
148
|
+
error_state_cleared: ExpertiseRuleId.TesterErrorStateCleared,
|
|
149
|
+
form_submitted_successfully: ExpertiseRuleId.TesterFormSubmittedSuccessfully,
|
|
150
|
+
required_error_shown_for_field: ExpertiseRuleId.TesterRequiredErrorShownForField,
|
|
151
|
+
field_error_clears_after_fix: ExpertiseRuleId.TesterFieldErrorClearsAfterFix,
|
|
152
|
+
cart_summary_changed: ExpertiseRuleId.TesterCartSummaryChanged,
|
|
153
|
+
count_changed: ExpertiseRuleId.TesterCountChanged,
|
|
154
|
+
row_count_changed: ExpertiseRuleId.TesterRowCountChanged,
|
|
155
|
+
network_request_made: ExpertiseRuleId.TesterNetworkRequestMade,
|
|
156
|
+
no_duplicate_mutation_requests: ExpertiseRuleId.TesterNoDuplicateMutationRequests,
|
|
157
|
+
dialog_closed: ExpertiseRuleId.TesterDialogClosed,
|
|
158
|
+
focus_returned: ExpertiseRuleId.TesterFocusReturned,
|
|
159
|
+
feedback_visible: ExpertiseRuleId.TesterFeedbackVisible,
|
|
160
|
+
feedback_not_duplicated: ExpertiseRuleId.TesterFeedbackNotDuplicated,
|
|
161
|
+
state_persists_after_reload: ExpertiseRuleId.TesterStatePersistsAfterReload,
|
|
162
|
+
back_navigation_restores_state: ExpertiseRuleId.TesterBackNavigationRestoresState,
|
|
163
|
+
forward_navigation_reapplies_state: ExpertiseRuleId.TesterForwardNavigationReappliesState,
|
|
164
|
+
expanded_state_changed: ExpertiseRuleId.TesterExpandedStateChanged,
|
|
165
|
+
element_focused: ExpertiseRuleId.TesterElementFocused,
|
|
166
|
+
results_changed: ExpertiseRuleId.TesterResultsChanged,
|
|
167
|
+
results_restored: ExpertiseRuleId.TesterResultsRestored,
|
|
168
|
+
collection_order_changed: ExpertiseRuleId.TesterCollectionOrderChanged,
|
|
169
|
+
variant_state_changed: ExpertiseRuleId.TesterVariantStateChanged,
|
|
170
|
+
empty_state_visible: ExpertiseRuleId.TesterEmptyStateVisible,
|
|
171
|
+
url_unchanged: ExpertiseRuleId.TesterUrlUnchanged,
|
|
172
|
+
navigation_or_state_changed: ExpertiseRuleId.TesterNavigationOrStateChanged,
|
|
173
|
+
page_responsive: ExpertiseRuleId.TesterPageResponsive,
|
|
174
|
+
loading_completes: ExpertiseRuleId.TesterLoadingCompletes,
|
|
175
|
+
modal_opened: ExpertiseRuleId.TesterModalOpened,
|
|
176
|
+
media_loaded: ExpertiseRuleId.TesterMediaLoaded,
|
|
177
|
+
video_playable: ExpertiseRuleId.TesterVideoPlayable,
|
|
178
|
+
};
|
|
124
179
|
//# sourceMappingURL=tester-expertise.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tester-expertise.js","sourceRoot":"","sources":["../../src/expertise/tester-expertise.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"tester-expertise.js","sourceRoot":"","sources":["../../src/expertise/tester-expertise.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import type { Expectation, ExpectationSeverity, NetworkLogEntry } from "@sudobility/testomniac_types";
|
|
1
|
+
import type { Expectation, ExpectationSeverity, ExpertiseRuleId, NetworkLogEntry } from "@sudobility/testomniac_types";
|
|
2
2
|
import type { DetectedScaffoldRegion } from "../scanner/component-detector";
|
|
3
3
|
import type { DetectedPatternWithInstances } from "../scanner/pattern-detector";
|
|
4
4
|
import type { ControlState } from "./tester/control-state";
|
|
5
5
|
import type { UiSnapshot } from "../browser/ui-snapshot";
|
|
6
|
+
import type { PageHealthIssue } from "../scanner/page-health-evaluator";
|
|
7
|
+
import type { PerformanceSnapshot } from "../browser/performance-snapshot";
|
|
6
8
|
export interface Outcome {
|
|
9
|
+
ruleId?: ExpertiseRuleId;
|
|
7
10
|
expected: string;
|
|
8
11
|
observed: string;
|
|
9
12
|
result: "pass" | "warning" | "error";
|
|
@@ -25,6 +28,8 @@ export interface ExpertiseContext {
|
|
|
25
28
|
finalUiSnapshot: UiSnapshot;
|
|
26
29
|
initialControlStates: ControlState[];
|
|
27
30
|
finalControlStates: ControlState[];
|
|
31
|
+
pageHealthIssues?: PageHealthIssue[];
|
|
32
|
+
performanceSnapshot?: PerformanceSnapshot | null;
|
|
28
33
|
}
|
|
29
34
|
export interface Expertise {
|
|
30
35
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/expertise/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EACnB,eAAe,EAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/expertise/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,eAAe,EAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACrC,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,sBAAsB,EAAE,CAAC;IACpC,QAAQ,EAAE,4BAA4B,EAAE,CAAC;IACzC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,UAAU,CAAC;IAC9B,eAAe,EAAE,UAAU,CAAC;IAC5B,oBAAoB,EAAE,YAAY,EAAE,CAAC;IACrC,kBAAkB,EAAE,YAAY,EAAE,CAAC;IACnC,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;CAClD;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,EAAE,CAAC;CAChD"}
|
|
@@ -8,5 +8,9 @@ export declare class UiExpertise implements Expertise {
|
|
|
8
8
|
private checkSocialButtonIntegrity;
|
|
9
9
|
private checkBreadcrumbConsistency;
|
|
10
10
|
private checkInteractiveDensity;
|
|
11
|
+
private checkStuckLoadingIndicators;
|
|
12
|
+
private checkDialogCloseAffordance;
|
|
13
|
+
private checkDisabledPrimaryActions;
|
|
14
|
+
private checkNavigationButtons;
|
|
11
15
|
}
|
|
12
16
|
//# sourceMappingURL=ui-expertise.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-expertise.d.ts","sourceRoot":"","sources":["../../src/expertise/ui-expertise.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ui-expertise.d.ts","sourceRoot":"","sources":["../../src/expertise/ui-expertise.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAmCpE,qBAAa,WAAY,YAAW,SAAS;IAC3C,IAAI,SAAQ;IAEZ,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,EAAE;IAuB9C,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,wBAAwB;IAqBhC,OAAO,CAAC,wBAAwB;IAsBhC,OAAO,CAAC,0BAA0B;IA2DlC,OAAO,CAAC,0BAA0B;IAwClC,OAAO,CAAC,uBAAuB;IAwB/B,OAAO,CAAC,2BAA2B;IAqBnC,OAAO,CAAC,0BAA0B;IA0BlC,OAAO,CAAC,2BAA2B;IAyBnC,OAAO,CAAC,sBAAsB;CAsB/B"}
|
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
import { ExpertiseRuleId } from "@sudobility/testomniac_types";
|
|
2
|
+
import { outcomesFromPageHealth } from "./page-health-outcomes";
|
|
3
|
+
import { applyRuleIds } from "./rule-id";
|
|
4
|
+
const UI_PAGE_HEALTH_RULE_IDS = {
|
|
5
|
+
element_overlap: ExpertiseRuleId.UiPageHealthOverlap,
|
|
6
|
+
dead_social_button: ExpertiseRuleId.UiPageHealthDeadSocialButton,
|
|
7
|
+
inconsistent_grid: ExpertiseRuleId.UiPageHealthInconsistentGrid,
|
|
8
|
+
horizontal_overflow: ExpertiseRuleId.UiPageHealthHorizontalOverflow,
|
|
9
|
+
truncated_text: ExpertiseRuleId.UiPageHealthTruncatedText,
|
|
10
|
+
duplicate_element: ExpertiseRuleId.UiPageHealthDuplicateElement,
|
|
11
|
+
error_message_visible: ExpertiseRuleId.UiPageHealthErrorMessageVisible,
|
|
12
|
+
};
|
|
13
|
+
const UI_RULE_IDS = {
|
|
14
|
+
"Page should expose a main content landmark": ExpertiseRuleId.UiMainLandmark,
|
|
15
|
+
"Shared page scaffolds should not be duplicated": ExpertiseRuleId.UiScaffoldConsistency,
|
|
16
|
+
"Page should not load with visible error UI": ExpertiseRuleId.UiActiveErrorPatterns,
|
|
17
|
+
"Social share buttons should be functional (links or have click handlers)": ExpertiseRuleId.UiSocialButtonIntegrity,
|
|
18
|
+
"Breadcrumb navigation should be consistent": ExpertiseRuleId.UiBreadcrumbConsistency,
|
|
19
|
+
"Page should expose at least one interactive control": ExpertiseRuleId.UiInteractiveDensity,
|
|
20
|
+
"Page should not remain in a loading state after settling": ExpertiseRuleId.UiStuckLoadingIndicators,
|
|
21
|
+
"Dialogs should provide an obvious close or cancel control": ExpertiseRuleId.UiDialogCloseAffordance,
|
|
22
|
+
"Primary actions should not be disabled without clear context": ExpertiseRuleId.UiDisabledPrimaryActions,
|
|
23
|
+
"Navigation-style actions should use links or clearly change state": ExpertiseRuleId.UiNavigationButtons,
|
|
24
|
+
};
|
|
1
25
|
export class UiExpertise {
|
|
2
26
|
name = "ui";
|
|
3
27
|
evaluate(context) {
|
|
@@ -8,7 +32,12 @@ export class UiExpertise {
|
|
|
8
32
|
outcomes.push(this.checkInteractiveDensity(context.html));
|
|
9
33
|
outcomes.push(this.checkSocialButtonIntegrity(context.html));
|
|
10
34
|
outcomes.push(this.checkBreadcrumbConsistency(context.html));
|
|
11
|
-
|
|
35
|
+
outcomes.push(this.checkStuckLoadingIndicators(context.html));
|
|
36
|
+
outcomes.push(this.checkDialogCloseAffordance(context.html));
|
|
37
|
+
outcomes.push(this.checkDisabledPrimaryActions(context.html));
|
|
38
|
+
outcomes.push(this.checkNavigationButtons(context.html));
|
|
39
|
+
outcomes.push(...outcomesFromPageHealth(context.pageHealthIssues, UI_PAGE_HEALTH_RULE_IDS));
|
|
40
|
+
return applyRuleIds(outcomes, UI_RULE_IDS);
|
|
12
41
|
}
|
|
13
42
|
checkMainLandmark(html) {
|
|
14
43
|
const hasMain = /<main\b/i.test(html) || /\brole=["']main["']/i.test(html);
|
|
@@ -163,5 +192,78 @@ export class UiExpertise {
|
|
|
163
192
|
priority: 3,
|
|
164
193
|
};
|
|
165
194
|
}
|
|
195
|
+
checkStuckLoadingIndicators(html) {
|
|
196
|
+
const text = html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ");
|
|
197
|
+
const loadingTokens = text.match(/\b(loading|please wait|spinner|skeleton|fetching)\b/gi) ?? [];
|
|
198
|
+
const busyElements = html.match(/\baria-busy=["']true["']/gi) ?? [];
|
|
199
|
+
if (loadingTokens.length + busyElements.length > 3) {
|
|
200
|
+
return {
|
|
201
|
+
expected: "Page should not remain in a loading state after settling",
|
|
202
|
+
observed: `${loadingTokens.length} loading token(s) and ${busyElements.length} aria-busy element(s) detected`,
|
|
203
|
+
result: "warning",
|
|
204
|
+
priority: 3,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
expected: "Page should not remain in a loading state after settling",
|
|
209
|
+
observed: "No excessive loading indicators detected",
|
|
210
|
+
result: "pass",
|
|
211
|
+
priority: 3,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
checkDialogCloseAffordance(html) {
|
|
215
|
+
const dialogs = html.match(/<(dialog|[^>]+\brole=["']dialog["'])[\s\S]*?<\/[^>]+>/gi) ??
|
|
216
|
+
[];
|
|
217
|
+
const withoutClose = dialogs.filter(dialog => !/\b(aria-label|title)=["'][^"']*(close|dismiss|cancel)/i.test(dialog) && !/>\s*(close|dismiss|cancel|×|x)\s*</i.test(dialog));
|
|
218
|
+
if (withoutClose.length > 0) {
|
|
219
|
+
return {
|
|
220
|
+
expected: "Dialogs should provide an obvious close or cancel control",
|
|
221
|
+
observed: `${withoutClose.length} dialog(s) lack an obvious close affordance`,
|
|
222
|
+
result: "warning",
|
|
223
|
+
priority: 3,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
expected: "Dialogs should provide an obvious close or cancel control",
|
|
228
|
+
observed: "All detected dialogs include a close/cancel affordance",
|
|
229
|
+
result: "pass",
|
|
230
|
+
priority: 3,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
checkDisabledPrimaryActions(html) {
|
|
234
|
+
const primaryButtons = html.match(/<button\b[^>]*(?:class=["'][^"']*(primary|submit|checkout|buy|cart)[^"']*["']|type=["']submit["'])[^>]*>/gi) ?? [];
|
|
235
|
+
const disabled = primaryButtons.filter(button => /\bdisabled\b/i.test(button));
|
|
236
|
+
if (disabled.length > 0) {
|
|
237
|
+
return {
|
|
238
|
+
expected: "Primary actions should not be disabled without clear context",
|
|
239
|
+
observed: `${disabled.length} primary-looking button(s) are disabled`,
|
|
240
|
+
result: "warning",
|
|
241
|
+
priority: 3,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
expected: "Primary actions should not be disabled without clear context",
|
|
246
|
+
observed: "No disabled primary-looking actions detected",
|
|
247
|
+
result: "pass",
|
|
248
|
+
priority: 3,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
checkNavigationButtons(html) {
|
|
252
|
+
const suspicious = html.match(/<button\b(?![^>]*type=["']submit["'])[^>]*>[\s\S]*?\b(view|details|learn more|read more|shop now|continue)\b[\s\S]*?<\/button>/gi) ?? [];
|
|
253
|
+
if (suspicious.length > 0) {
|
|
254
|
+
return {
|
|
255
|
+
expected: "Navigation-style actions should use links or clearly change state",
|
|
256
|
+
observed: `${suspicious.length} button(s) look like navigation actions without link semantics`,
|
|
257
|
+
result: "warning",
|
|
258
|
+
priority: 4,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
expected: "Navigation-style actions should use links or clearly change state",
|
|
263
|
+
observed: "No navigation-looking buttons without link semantics detected",
|
|
264
|
+
result: "pass",
|
|
265
|
+
priority: 4,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
166
268
|
}
|
|
167
269
|
//# sourceMappingURL=ui-expertise.js.map
|