@lullabot/playwright-testing 1.0.1 → 1.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/lib/accessible-screenshot.js +24 -14
- package/lib/github/failure-summary.js +1 -1
- package/package.json +1 -1
- package/src/accessibility-baseline.test.ts +236 -0
- package/src/accessible-screenshot.ts +29 -11
- package/src/github/failure-summary.test.ts +2 -1
- package/src/github/failure-summary.ts +1 -1
|
@@ -38,6 +38,9 @@ async function checkAccessibility(page, testInfo, options) {
|
|
|
38
38
|
if (!testInfo.annotations.some((a) => a.type === "@a11y")) {
|
|
39
39
|
testInfo.annotations.push({ type: "@a11y" });
|
|
40
40
|
}
|
|
41
|
+
// An in-code baseline is shared by both scans. Track matches across this
|
|
42
|
+
// check so that a rule detected by either scan is not reported as stale.
|
|
43
|
+
const sharedBaselineMatches = new Set();
|
|
41
44
|
if (bestPracticeMode !== "off") {
|
|
42
45
|
const bpResults = await runBestPracticeScan(page, testInfo, {
|
|
43
46
|
exclude: [...exclude, ...bestPracticeExclude],
|
|
@@ -52,7 +55,7 @@ async function checkAccessibility(page, testInfo, options) {
|
|
|
52
55
|
scan: "best-practice",
|
|
53
56
|
expectFn: test_1.expect.soft,
|
|
54
57
|
scanLabel: "Best-practice scan",
|
|
55
|
-
}, baseline);
|
|
58
|
+
}, baseline, sharedBaselineMatches);
|
|
56
59
|
}
|
|
57
60
|
const wcagScanResults = await runWcagScan(page, testInfo, {
|
|
58
61
|
wcagTags,
|
|
@@ -68,7 +71,7 @@ async function checkAccessibility(page, testInfo, options) {
|
|
|
68
71
|
scan: "wcag",
|
|
69
72
|
expectFn: test_1.expect,
|
|
70
73
|
scanLabel: "WCAG scan",
|
|
71
|
-
}, baseline);
|
|
74
|
+
}, baseline, sharedBaselineMatches);
|
|
72
75
|
}
|
|
73
76
|
/**
|
|
74
77
|
* Dispatch the assertion for one scan to the correct mode:
|
|
@@ -85,9 +88,9 @@ async function checkAccessibility(page, testInfo, options) {
|
|
|
85
88
|
* test (matching Playwright's missing-snapshot behaviour); locally,
|
|
86
89
|
* seeding passes so the first run is green.
|
|
87
90
|
*/
|
|
88
|
-
async function dispatchAssertion(ctx, inCodeBaseline) {
|
|
91
|
+
async function dispatchAssertion(ctx, inCodeBaseline, sharedBaselineMatches) {
|
|
89
92
|
if (inCodeBaseline) {
|
|
90
|
-
return assertBaseline(ctx, inCodeBaseline);
|
|
93
|
+
return assertBaseline(ctx, inCodeBaseline, sharedBaselineMatches);
|
|
91
94
|
}
|
|
92
95
|
if (await (0, accessibility_baseline_file_js_1.snapshotExists)(ctx.testInfo)) {
|
|
93
96
|
return assertSnapshot(ctx);
|
|
@@ -214,7 +217,7 @@ async function screenshotViolatingElements(page, testInfo, results) {
|
|
|
214
217
|
/**
|
|
215
218
|
* Assert violations against a baseline allowlist (in-code or on-disk).
|
|
216
219
|
*/
|
|
217
|
-
function assertBaseline(ctx, baseline) {
|
|
220
|
+
function assertBaseline(ctx, baseline, sharedBaselineMatches) {
|
|
218
221
|
(0, accessibility_baseline_js_1.validateAccessibilityBaseline)(baseline);
|
|
219
222
|
const { testInfo, results, scanLabel, expectFn } = ctx;
|
|
220
223
|
const allViolations = extractNormalizedViolations(results);
|
|
@@ -229,6 +232,7 @@ function assertBaseline(ctx, baseline) {
|
|
|
229
232
|
});
|
|
230
233
|
if (baselineIndex >= 0) {
|
|
231
234
|
matchedBaselineIndices.add(baselineIndex);
|
|
235
|
+
sharedBaselineMatches?.add(baselineIndex);
|
|
232
236
|
const entry = baseline[baselineIndex];
|
|
233
237
|
testInfo.annotations.push({
|
|
234
238
|
type: "Baselined a11y violation",
|
|
@@ -239,15 +243,21 @@ function assertBaseline(ctx, baseline) {
|
|
|
239
243
|
unmatchedViolations.push(violation);
|
|
240
244
|
}
|
|
241
245
|
}
|
|
242
|
-
//
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
// The WCAG scan always runs last, even when best-practice is disabled.
|
|
247
|
+
// Report shared entries once after both scans have contributed matches,
|
|
248
|
+
// before the hard assertion can throw. On-disk baselines belong to one
|
|
249
|
+
// scan and continue to report stale entries independently.
|
|
250
|
+
if (!sharedBaselineMatches || ctx.scan === "wcag") {
|
|
251
|
+
const matches = sharedBaselineMatches ?? matchedBaselineIndices;
|
|
252
|
+
baseline.forEach((entry, idx) => {
|
|
253
|
+
if (!matches.has(idx)) {
|
|
254
|
+
testInfo.annotations.push({
|
|
255
|
+
type: "Stale a11y baseline entry",
|
|
256
|
+
description: `${entry.rule} on ${entry.targets.join(", ")} — no longer detected`,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
251
261
|
// Summary annotation for baseline mode.
|
|
252
262
|
const baselinedCount = matchedBaselineIndices.size;
|
|
253
263
|
testInfo.annotations.push({
|
|
@@ -292,7 +292,7 @@ function headline(report) {
|
|
|
292
292
|
// seeing — a baselined violation passes and is still screenshotted.
|
|
293
293
|
const parts = report.totalFailed === 0
|
|
294
294
|
? [":white_check_mark: No failing tests"]
|
|
295
|
-
: [
|
|
295
|
+
: [`:x: **${report.totalFailed}** failing test(s)`];
|
|
296
296
|
if (report.totalFlaky > 0)
|
|
297
297
|
parts.push(`:warning: **${report.totalFlaky}** flaky test(s)`);
|
|
298
298
|
if (report.totalImages > 0)
|
package/package.json
CHANGED
|
@@ -305,6 +305,175 @@ describe("accessibility baseline", () => {
|
|
|
305
305
|
expect(staleAnnotations[0].description).toContain("#old-element");
|
|
306
306
|
expect(staleAnnotations[0].description).toContain("no longer detected");
|
|
307
307
|
});
|
|
308
|
+
|
|
309
|
+
it.each([
|
|
310
|
+
{ name: "best-practice", bp: ["page-has-heading-one"], wcag: [] },
|
|
311
|
+
{ name: "WCAG", bp: [], wcag: ["image-alt"] },
|
|
312
|
+
{ name: "both scans", bp: ["page-has-heading-one"], wcag: ["image-alt"] },
|
|
313
|
+
])(
|
|
314
|
+
"does not mark entries matched by $name as stale",
|
|
315
|
+
async ({ bp, wcag }) => {
|
|
316
|
+
const rules = [...bp, ...wcag];
|
|
317
|
+
const baseline: AccessibilityBaseline = rules.map((rule) => ({
|
|
318
|
+
rule,
|
|
319
|
+
targets: ["#known"],
|
|
320
|
+
reason: "Known issue",
|
|
321
|
+
willBeFixedIn: "PROJ-353",
|
|
322
|
+
}));
|
|
323
|
+
mockAnalyze
|
|
324
|
+
.mockResolvedValueOnce(
|
|
325
|
+
makeAxeResults({
|
|
326
|
+
violations: bp.map((rule) => makeViolation(rule, [["#known"]])),
|
|
327
|
+
}),
|
|
328
|
+
)
|
|
329
|
+
.mockResolvedValueOnce(
|
|
330
|
+
makeAxeResults({
|
|
331
|
+
violations: wcag.map((rule) => makeViolation(rule, [["#known"]])),
|
|
332
|
+
}),
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
const testInfo = makeTestInfo();
|
|
336
|
+
await checkAccessibility(makePage() as any, testInfo as any, {
|
|
337
|
+
baseline,
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
expect(
|
|
341
|
+
testInfo.annotations.filter(
|
|
342
|
+
(a) => a.type === "Stale a11y baseline entry",
|
|
343
|
+
),
|
|
344
|
+
).toEqual([]);
|
|
345
|
+
expect(
|
|
346
|
+
testInfo.annotations.filter(
|
|
347
|
+
(a) => a.type === "Baselined a11y violation",
|
|
348
|
+
),
|
|
349
|
+
).toHaveLength(rules.length);
|
|
350
|
+
expect(
|
|
351
|
+
testInfo.annotations.filter((a) =>
|
|
352
|
+
a.description?.includes("new violations"),
|
|
353
|
+
),
|
|
354
|
+
).toEqual([
|
|
355
|
+
{
|
|
356
|
+
type: "Accessibility",
|
|
357
|
+
description: `Best-practice scan: 0 new violations (${bp.length} baselined)`,
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
type: "Accessibility",
|
|
361
|
+
description: `WCAG scan: 0 new violations (${wcag.length} baselined)`,
|
|
362
|
+
},
|
|
363
|
+
]);
|
|
364
|
+
expect(mockToBe).not.toHaveBeenCalled();
|
|
365
|
+
},
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
it("reports entries unmatched by either scan only once", async () => {
|
|
369
|
+
const testInfo = makeTestInfo();
|
|
370
|
+
await checkAccessibility(makePage() as any, testInfo as any, {
|
|
371
|
+
baseline: [
|
|
372
|
+
{
|
|
373
|
+
rule: "page-has-heading-one",
|
|
374
|
+
targets: ["html"],
|
|
375
|
+
reason: "Was broken",
|
|
376
|
+
willBeFixedIn: "PROJ-353",
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
expect(
|
|
382
|
+
testInfo.annotations.filter(
|
|
383
|
+
(a) => a.type === "Stale a11y baseline entry",
|
|
384
|
+
),
|
|
385
|
+
).toEqual([
|
|
386
|
+
{
|
|
387
|
+
type: "Stale a11y baseline entry",
|
|
388
|
+
description: "page-has-heading-one on html — no longer detected",
|
|
389
|
+
},
|
|
390
|
+
]);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it("does not carry baseline matches into later checks in the same test", async () => {
|
|
394
|
+
const baseline: AccessibilityBaseline = [
|
|
395
|
+
{
|
|
396
|
+
rule: "page-has-heading-one",
|
|
397
|
+
targets: ["html"],
|
|
398
|
+
reason: "Known issue",
|
|
399
|
+
willBeFixedIn: "PROJ-353",
|
|
400
|
+
},
|
|
401
|
+
];
|
|
402
|
+
mockAnalyze.mockResolvedValueOnce(
|
|
403
|
+
makeAxeResults({
|
|
404
|
+
violations: [makeViolation("page-has-heading-one", [["html"]])],
|
|
405
|
+
}),
|
|
406
|
+
);
|
|
407
|
+
const testInfo = makeTestInfo();
|
|
408
|
+
await checkAccessibility(makePage() as any, testInfo as any, {
|
|
409
|
+
baseline,
|
|
410
|
+
});
|
|
411
|
+
expect(
|
|
412
|
+
testInfo.annotations.filter(
|
|
413
|
+
(a) => a.type === "Stale a11y baseline entry",
|
|
414
|
+
),
|
|
415
|
+
).toEqual([]);
|
|
416
|
+
|
|
417
|
+
await checkAccessibility(makePage() as any, testInfo as any, {
|
|
418
|
+
baseline,
|
|
419
|
+
});
|
|
420
|
+
expect(
|
|
421
|
+
testInfo.annotations.filter(
|
|
422
|
+
(a) => a.type === "Stale a11y baseline entry",
|
|
423
|
+
),
|
|
424
|
+
).toHaveLength(1);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("reports stale entries before an unmatched WCAG violation throws", async () => {
|
|
428
|
+
const baseline: AccessibilityBaseline = [
|
|
429
|
+
{
|
|
430
|
+
rule: "page-has-heading-one",
|
|
431
|
+
targets: ["html"],
|
|
432
|
+
reason: "Known issue",
|
|
433
|
+
willBeFixedIn: "PROJ-353",
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
rule: "heading-order",
|
|
437
|
+
targets: ["h3"],
|
|
438
|
+
reason: "Was broken",
|
|
439
|
+
willBeFixedIn: "PROJ-354",
|
|
440
|
+
},
|
|
441
|
+
];
|
|
442
|
+
mockAnalyze
|
|
443
|
+
.mockResolvedValueOnce(
|
|
444
|
+
makeAxeResults({
|
|
445
|
+
violations: [makeViolation("page-has-heading-one", [["html"]])],
|
|
446
|
+
}),
|
|
447
|
+
)
|
|
448
|
+
.mockResolvedValueOnce(
|
|
449
|
+
makeAxeResults({
|
|
450
|
+
violations: [makeViolation("image-alt", [["img"]])],
|
|
451
|
+
}),
|
|
452
|
+
);
|
|
453
|
+
mockToBe.mockImplementationOnce(() => {
|
|
454
|
+
throw new Error("new WCAG violation");
|
|
455
|
+
});
|
|
456
|
+
const testInfo = makeTestInfo();
|
|
457
|
+
|
|
458
|
+
await expect(
|
|
459
|
+
checkAccessibility(makePage() as any, testInfo as any, { baseline }),
|
|
460
|
+
).rejects.toThrow("new WCAG violation");
|
|
461
|
+
|
|
462
|
+
expect(
|
|
463
|
+
testInfo.annotations.filter(
|
|
464
|
+
(a) => a.type === "Stale a11y baseline entry",
|
|
465
|
+
),
|
|
466
|
+
).toEqual([
|
|
467
|
+
{
|
|
468
|
+
type: "Stale a11y baseline entry",
|
|
469
|
+
description: "heading-order on h3 — no longer detected",
|
|
470
|
+
},
|
|
471
|
+
]);
|
|
472
|
+
expect(mockExpectHard).toHaveBeenCalledWith(
|
|
473
|
+
null,
|
|
474
|
+
expect.stringContaining("image-alt"),
|
|
475
|
+
);
|
|
476
|
+
});
|
|
308
477
|
});
|
|
309
478
|
|
|
310
479
|
describe("snapshot bypass", () => {
|
|
@@ -699,6 +868,73 @@ describe("accessibility baseline", () => {
|
|
|
699
868
|
).rejects.toThrow("requires a reason");
|
|
700
869
|
});
|
|
701
870
|
|
|
871
|
+
it("keeps stale detection independent for each scan-specific on-disk baseline", async () => {
|
|
872
|
+
const fs = await import("fs");
|
|
873
|
+
const path = await import("path");
|
|
874
|
+
await fs.promises.writeFile(
|
|
875
|
+
path.join(tmpDir, "separate-scans-1.a11y-baseline-best-practice.json"),
|
|
876
|
+
JSON.stringify({
|
|
877
|
+
note: "Known best-practice violation.",
|
|
878
|
+
violations: [
|
|
879
|
+
{
|
|
880
|
+
rule: "page-has-heading-one",
|
|
881
|
+
targets: ["html"],
|
|
882
|
+
reason: "Known",
|
|
883
|
+
willBeFixedIn: "PROJ-353",
|
|
884
|
+
},
|
|
885
|
+
],
|
|
886
|
+
}),
|
|
887
|
+
);
|
|
888
|
+
await fs.promises.writeFile(
|
|
889
|
+
path.join(tmpDir, "separate-scans-1.a11y-baseline.json"),
|
|
890
|
+
JSON.stringify({
|
|
891
|
+
note: "Previously broken image.",
|
|
892
|
+
violations: [
|
|
893
|
+
{
|
|
894
|
+
rule: "image-alt",
|
|
895
|
+
targets: ["img"],
|
|
896
|
+
reason: "Was broken",
|
|
897
|
+
willBeFixedIn: "PROJ-354",
|
|
898
|
+
},
|
|
899
|
+
],
|
|
900
|
+
}),
|
|
901
|
+
);
|
|
902
|
+
mockAnalyze.mockResolvedValueOnce(
|
|
903
|
+
makeAxeResults({
|
|
904
|
+
violations: [makeViolation("page-has-heading-one", [["html"]])],
|
|
905
|
+
}),
|
|
906
|
+
);
|
|
907
|
+
|
|
908
|
+
const testInfo = makeTestInfo({
|
|
909
|
+
updateSnapshots: "none",
|
|
910
|
+
snapshotsDir: tmpDir,
|
|
911
|
+
title: "separate scans",
|
|
912
|
+
});
|
|
913
|
+
await checkAccessibility(makePage() as any, testInfo as any);
|
|
914
|
+
|
|
915
|
+
expect(
|
|
916
|
+
testInfo.annotations.filter(
|
|
917
|
+
(a) => a.type === "Baselined a11y violation",
|
|
918
|
+
),
|
|
919
|
+
).toEqual([
|
|
920
|
+
{
|
|
921
|
+
type: "Baselined a11y violation",
|
|
922
|
+
description: "page-has-heading-one: Known — PROJ-353",
|
|
923
|
+
},
|
|
924
|
+
]);
|
|
925
|
+
expect(
|
|
926
|
+
testInfo.annotations.filter(
|
|
927
|
+
(a) => a.type === "Stale a11y baseline entry",
|
|
928
|
+
),
|
|
929
|
+
).toEqual([
|
|
930
|
+
{
|
|
931
|
+
type: "Stale a11y baseline entry",
|
|
932
|
+
description: "image-alt on img — no longer detected",
|
|
933
|
+
},
|
|
934
|
+
]);
|
|
935
|
+
expect(mockToBe).not.toHaveBeenCalled();
|
|
936
|
+
});
|
|
937
|
+
|
|
702
938
|
it("produces per-call counter files for multi-call tests", async () => {
|
|
703
939
|
mockAnalyze.mockResolvedValue(makeAxeResults({ violations: [] }));
|
|
704
940
|
|
|
@@ -253,6 +253,10 @@ export async function checkAccessibility(
|
|
|
253
253
|
testInfo.annotations.push({ type: "@a11y" });
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
// An in-code baseline is shared by both scans. Track matches across this
|
|
257
|
+
// check so that a rule detected by either scan is not reported as stale.
|
|
258
|
+
const sharedBaselineMatches = new Set<number>();
|
|
259
|
+
|
|
256
260
|
if (bestPracticeMode !== "off") {
|
|
257
261
|
const bpResults = await runBestPracticeScan(page, testInfo, {
|
|
258
262
|
exclude: [...exclude, ...bestPracticeExclude],
|
|
@@ -270,6 +274,7 @@ export async function checkAccessibility(
|
|
|
270
274
|
scanLabel: "Best-practice scan",
|
|
271
275
|
},
|
|
272
276
|
baseline,
|
|
277
|
+
sharedBaselineMatches,
|
|
273
278
|
);
|
|
274
279
|
}
|
|
275
280
|
|
|
@@ -292,6 +297,7 @@ export async function checkAccessibility(
|
|
|
292
297
|
scanLabel: "WCAG scan",
|
|
293
298
|
},
|
|
294
299
|
baseline,
|
|
300
|
+
sharedBaselineMatches,
|
|
295
301
|
);
|
|
296
302
|
}
|
|
297
303
|
|
|
@@ -321,9 +327,10 @@ interface ScanContext {
|
|
|
321
327
|
async function dispatchAssertion(
|
|
322
328
|
ctx: ScanContext,
|
|
323
329
|
inCodeBaseline?: AccessibilityBaseline,
|
|
330
|
+
sharedBaselineMatches?: Set<number>,
|
|
324
331
|
): Promise<void> {
|
|
325
332
|
if (inCodeBaseline) {
|
|
326
|
-
return assertBaseline(ctx, inCodeBaseline);
|
|
333
|
+
return assertBaseline(ctx, inCodeBaseline, sharedBaselineMatches);
|
|
327
334
|
}
|
|
328
335
|
|
|
329
336
|
if (await snapshotExists(ctx.testInfo)) {
|
|
@@ -497,7 +504,11 @@ async function screenshotViolatingElements(
|
|
|
497
504
|
/**
|
|
498
505
|
* Assert violations against a baseline allowlist (in-code or on-disk).
|
|
499
506
|
*/
|
|
500
|
-
function assertBaseline(
|
|
507
|
+
function assertBaseline(
|
|
508
|
+
ctx: ScanContext,
|
|
509
|
+
baseline: AccessibilityBaseline,
|
|
510
|
+
sharedBaselineMatches?: Set<number>,
|
|
511
|
+
) {
|
|
501
512
|
validateAccessibilityBaseline(baseline);
|
|
502
513
|
const { testInfo, results, scanLabel, expectFn } = ctx;
|
|
503
514
|
const allViolations = extractNormalizedViolations(results);
|
|
@@ -517,6 +528,7 @@ function assertBaseline(ctx: ScanContext, baseline: AccessibilityBaseline) {
|
|
|
517
528
|
|
|
518
529
|
if (baselineIndex >= 0) {
|
|
519
530
|
matchedBaselineIndices.add(baselineIndex);
|
|
531
|
+
sharedBaselineMatches?.add(baselineIndex);
|
|
520
532
|
const entry = baseline[baselineIndex];
|
|
521
533
|
testInfo.annotations.push({
|
|
522
534
|
type: "Baselined a11y violation",
|
|
@@ -527,15 +539,21 @@ function assertBaseline(ctx: ScanContext, baseline: AccessibilityBaseline) {
|
|
|
527
539
|
}
|
|
528
540
|
}
|
|
529
541
|
|
|
530
|
-
//
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
542
|
+
// The WCAG scan always runs last, even when best-practice is disabled.
|
|
543
|
+
// Report shared entries once after both scans have contributed matches,
|
|
544
|
+
// before the hard assertion can throw. On-disk baselines belong to one
|
|
545
|
+
// scan and continue to report stale entries independently.
|
|
546
|
+
if (!sharedBaselineMatches || ctx.scan === "wcag") {
|
|
547
|
+
const matches = sharedBaselineMatches ?? matchedBaselineIndices;
|
|
548
|
+
baseline.forEach((entry, idx) => {
|
|
549
|
+
if (!matches.has(idx)) {
|
|
550
|
+
testInfo.annotations.push({
|
|
551
|
+
type: "Stale a11y baseline entry",
|
|
552
|
+
description: `${entry.rule} on ${entry.targets.join(", ")} — no longer detected`,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
539
557
|
|
|
540
558
|
// Summary annotation for baseline mode.
|
|
541
559
|
const baselinedCount = matchedBaselineIndices.size;
|
|
@@ -245,6 +245,7 @@ describe("generateSummary", () => {
|
|
|
245
245
|
// attachment resolves, and that dead render is cached for good.
|
|
246
246
|
const summary = generateSummary(reportWith("https://example.test/a"));
|
|
247
247
|
|
|
248
|
+
expect(summary).toContain(":x: **1** failing test(s)");
|
|
248
249
|
expect(summary).not.toContain("<img");
|
|
249
250
|
expect(summary).toContain(
|
|
250
251
|
"1 screenshot(s) uploaded — see the pull request comment.",
|
|
@@ -396,7 +397,7 @@ describe("generateComment", () => {
|
|
|
396
397
|
summaryUrl: "https://example.test/run/1",
|
|
397
398
|
});
|
|
398
399
|
|
|
399
|
-
expect(comment).toContain("**1** failing test(s)");
|
|
400
|
+
expect(comment).toContain(":x: **1** failing test(s)");
|
|
400
401
|
expect(comment).toContain("homepage matches");
|
|
401
402
|
expect(comment).toContain("(https://example.test/run/1)");
|
|
402
403
|
});
|
|
@@ -382,7 +382,7 @@ function headline(report: FailureReport): string {
|
|
|
382
382
|
const parts =
|
|
383
383
|
report.totalFailed === 0
|
|
384
384
|
? [":white_check_mark: No failing tests"]
|
|
385
|
-
: [
|
|
385
|
+
: [`:x: **${report.totalFailed}** failing test(s)`];
|
|
386
386
|
|
|
387
387
|
if (report.totalFlaky > 0)
|
|
388
388
|
parts.push(`:warning: **${report.totalFlaky}** flaky test(s)`);
|