@crvy/rprtr 0.1.4 → 0.2.2
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/CHANGELOG.md +61 -0
- package/README.md +8 -7
- package/dist/{chunk-WRMHUY5A.js → chunk-HAFWYUNO.js} +181 -130
- package/dist/{chunk-KLGJSEKV.js → chunk-RFZGJSL3.js} +439 -127
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -4
- package/dist/index.css +26 -0
- package/dist/index.js +297 -76
- package/dist/offline-reports.d.ts +4 -0
- package/dist/offline-reports.d.ts.map +1 -1
- package/dist/report-state.d.ts +3 -1
- package/dist/report-state.d.ts.map +1 -1
- package/dist/reporter-utils.d.ts +0 -1
- package/dist/reporter-utils.d.ts.map +1 -1
- package/dist/reporter.cjs +186 -134
- package/dist/reporter.d.ts +1 -1
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +8 -6
- package/dist/schemas/http.d.ts +44 -0
- package/dist/schemas/http.d.ts.map +1 -0
- package/dist/schemas.d.ts +25 -6
- package/dist/schemas.d.ts.map +1 -1
- package/dist/server/app.d.ts +6 -0
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/handlers.d.ts +3 -0
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server/playwright-config.d.ts +2 -0
- package/dist/server/playwright-config.d.ts.map +1 -0
- package/dist/server/routes-context.d.ts +13 -0
- package/dist/server/routes-context.d.ts.map +1 -0
- package/dist/server/routes.d.ts +6 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/run-controller.d.ts +85 -0
- package/dist/server/run-controller.d.ts.map +1 -0
- package/dist/server/run-routes.d.ts +3 -0
- package/dist/server/run-routes.d.ts.map +1 -0
- package/dist/server.cjs +633 -273
- package/dist/server.js +2 -2
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -19322,10 +19322,42 @@ function date4(params) {
|
|
|
19322
19322
|
// node_modules/zod/v4/classic/external.js
|
|
19323
19323
|
config(en_default());
|
|
19324
19324
|
|
|
19325
|
+
// src/schemas/http.ts
|
|
19326
|
+
var ApproveRequestBodySchema = external_exports.object({
|
|
19327
|
+
id: external_exports.string(),
|
|
19328
|
+
retry: external_exports.number(),
|
|
19329
|
+
image: external_exports.string()
|
|
19330
|
+
});
|
|
19331
|
+
var RunTestDescriptorSchema = external_exports.object({
|
|
19332
|
+
file: external_exports.string(),
|
|
19333
|
+
line: external_exports.number(),
|
|
19334
|
+
column: external_exports.number().optional(),
|
|
19335
|
+
projectName: external_exports.string().optional(),
|
|
19336
|
+
titlePath: external_exports.array(external_exports.string())
|
|
19337
|
+
});
|
|
19338
|
+
var RunRequestBodySchema = external_exports.object({
|
|
19339
|
+
tests: external_exports.array(RunTestDescriptorSchema).optional()
|
|
19340
|
+
});
|
|
19341
|
+
var RunResponseSchema = external_exports.discriminatedUnion("ok", [
|
|
19342
|
+
external_exports.object({ ok: external_exports.literal(true) }),
|
|
19343
|
+
external_exports.object({
|
|
19344
|
+
ok: external_exports.literal(false),
|
|
19345
|
+
reason: external_exports.enum(["no-config", "already-running", "no-tests"])
|
|
19346
|
+
})
|
|
19347
|
+
]);
|
|
19348
|
+
var StopResponseSchema = external_exports.discriminatedUnion("ok", [
|
|
19349
|
+
external_exports.object({ ok: external_exports.literal(true) }),
|
|
19350
|
+
external_exports.object({
|
|
19351
|
+
ok: external_exports.literal(false),
|
|
19352
|
+
reason: external_exports.literal("not-running")
|
|
19353
|
+
})
|
|
19354
|
+
]);
|
|
19355
|
+
|
|
19325
19356
|
// src/schemas.ts
|
|
19326
19357
|
var LocationSchema = external_exports.object({
|
|
19327
19358
|
file: external_exports.string(),
|
|
19328
|
-
line: external_exports.number()
|
|
19359
|
+
line: external_exports.number(),
|
|
19360
|
+
column: external_exports.number().optional()
|
|
19329
19361
|
});
|
|
19330
19362
|
var VisualSourceSchema = external_exports.enum(["comparison", "baseline-only", "declared-only"]);
|
|
19331
19363
|
var ImagesSchema = external_exports.object({
|
|
@@ -19413,7 +19445,13 @@ var WebSocketMessageSchema = external_exports.discriminatedUnion("type", [
|
|
|
19413
19445
|
isUpdateMode: external_exports.boolean().optional()
|
|
19414
19446
|
})
|
|
19415
19447
|
}),
|
|
19416
|
-
external_exports.object({ type: external_exports.literal("approve"), data: external_exports.unknown() })
|
|
19448
|
+
external_exports.object({ type: external_exports.literal("approve"), data: external_exports.unknown() }),
|
|
19449
|
+
external_exports.object({
|
|
19450
|
+
type: external_exports.literal("run-status"),
|
|
19451
|
+
data: external_exports.object({
|
|
19452
|
+
running: external_exports.boolean()
|
|
19453
|
+
})
|
|
19454
|
+
})
|
|
19417
19455
|
]);
|
|
19418
19456
|
var TestBeginDataSchema = external_exports.object({
|
|
19419
19457
|
id: external_exports.string(),
|
|
@@ -19442,7 +19480,9 @@ var RegisterDataSchema = external_exports.object({
|
|
|
19442
19480
|
playwrightSnapshotDir: external_exports.string().optional(),
|
|
19443
19481
|
playwrightTestDir: external_exports.string().optional(),
|
|
19444
19482
|
playwrightSnapshotPathTemplate: external_exports.string().optional(),
|
|
19445
|
-
playwrightToHaveScreenshotPathTemplate: external_exports.string().optional()
|
|
19483
|
+
playwrightToHaveScreenshotPathTemplate: external_exports.string().optional(),
|
|
19484
|
+
configFile: external_exports.string().optional(),
|
|
19485
|
+
cwd: external_exports.string().optional()
|
|
19446
19486
|
});
|
|
19447
19487
|
var ReportDataSchema = external_exports.object({
|
|
19448
19488
|
isRunning: external_exports.boolean(),
|
|
@@ -19467,14 +19507,11 @@ var OfflineReportSchema = external_exports.object({
|
|
|
19467
19507
|
workers: external_exports.number(),
|
|
19468
19508
|
events: external_exports.array(OfflineEventSchema)
|
|
19469
19509
|
});
|
|
19470
|
-
var ApproveRequestBodySchema = external_exports.object({
|
|
19471
|
-
id: external_exports.string(),
|
|
19472
|
-
retry: external_exports.number(),
|
|
19473
|
-
image: external_exports.string()
|
|
19474
|
-
});
|
|
19475
19510
|
var ReportApiResponseSchema = external_exports.object({
|
|
19476
19511
|
tests: external_exports.record(external_exports.string(), TestDataSchema),
|
|
19477
|
-
isUpdateMode: external_exports.boolean().optional()
|
|
19512
|
+
isUpdateMode: external_exports.boolean().optional(),
|
|
19513
|
+
isRunning: external_exports.boolean().optional(),
|
|
19514
|
+
runEnabled: external_exports.boolean().optional()
|
|
19478
19515
|
});
|
|
19479
19516
|
var ClientBootstrapDataSchema = external_exports.object({
|
|
19480
19517
|
report: ReportApiResponseSchema.extend({
|
|
@@ -19482,7 +19519,9 @@ var ClientBootstrapDataSchema = external_exports.object({
|
|
|
19482
19519
|
}),
|
|
19483
19520
|
liveUpdates: external_exports.boolean(),
|
|
19484
19521
|
approvalEnabled: external_exports.boolean(),
|
|
19485
|
-
approvalMessage: external_exports.string().optional()
|
|
19522
|
+
approvalMessage: external_exports.string().optional(),
|
|
19523
|
+
runEnabled: external_exports.boolean().optional(),
|
|
19524
|
+
isRunning: external_exports.boolean().optional()
|
|
19486
19525
|
});
|
|
19487
19526
|
var ImagesViewModeSchema = external_exports.enum(["side-by-side", "swap", "slide", "blend"]);
|
|
19488
19527
|
function safeParse3(schema, data) {
|
|
@@ -20066,6 +20105,42 @@ function recalcAllSuiteStatuses(suite) {
|
|
|
20066
20105
|
}
|
|
20067
20106
|
suite.status = getChildrenArray(suite.children).map(({ status }) => status).reduce(calcStatus);
|
|
20068
20107
|
}
|
|
20108
|
+
function markTestsPending(node) {
|
|
20109
|
+
if (isTest(node)) {
|
|
20110
|
+
node.status = "pending";
|
|
20111
|
+
return;
|
|
20112
|
+
}
|
|
20113
|
+
getChildrenArray(node.children).forEach(markTestsPending);
|
|
20114
|
+
}
|
|
20115
|
+
|
|
20116
|
+
// src/client/helpers/run-snapshot.ts
|
|
20117
|
+
function captureTestStatuses(node) {
|
|
20118
|
+
const snapshot2 = /* @__PURE__ */ new Map();
|
|
20119
|
+
const walk = (n) => {
|
|
20120
|
+
if (isTest(n)) {
|
|
20121
|
+
snapshot2.set(n.id, n.status);
|
|
20122
|
+
return;
|
|
20123
|
+
}
|
|
20124
|
+
for (const child2 of getChildrenArray(n.children)) walk(child2);
|
|
20125
|
+
};
|
|
20126
|
+
walk(node);
|
|
20127
|
+
return snapshot2;
|
|
20128
|
+
}
|
|
20129
|
+
function restoreTestStatuses(root10, snapshot2, receivedIds) {
|
|
20130
|
+
let restored = 0;
|
|
20131
|
+
const walk = (n) => {
|
|
20132
|
+
if (isTest(n)) {
|
|
20133
|
+
if (snapshot2.has(n.id) && !receivedIds.has(n.id)) {
|
|
20134
|
+
n.status = snapshot2.get(n.id);
|
|
20135
|
+
restored++;
|
|
20136
|
+
}
|
|
20137
|
+
return;
|
|
20138
|
+
}
|
|
20139
|
+
for (const child2 of getChildrenArray(n.children)) walk(child2);
|
|
20140
|
+
};
|
|
20141
|
+
walk(root10);
|
|
20142
|
+
return restored;
|
|
20143
|
+
}
|
|
20069
20144
|
|
|
20070
20145
|
// src/client/viewMode.ts
|
|
20071
20146
|
var VIEW_MODE_KEY = "crvy-rprtr-view-mode";
|
|
@@ -20105,8 +20180,9 @@ function statusDotClass(status) {
|
|
|
20105
20180
|
|
|
20106
20181
|
// src/client/components/TreeItem.svelte
|
|
20107
20182
|
var root_1 = from_html(`<span aria-hidden="true">\u25B6</span>`);
|
|
20108
|
-
var root_2 = from_html(`<
|
|
20109
|
-
var
|
|
20183
|
+
var root_2 = from_html(`<button>▶</button>`);
|
|
20184
|
+
var root_3 = from_html(`<span></span>`);
|
|
20185
|
+
var root = from_html(`<div role="treeitem" tabindex="-1"><!> <span class="flex-1 text-ui whitespace-nowrap overflow-hidden text-ellipsis"> </span> <!> <!></div> <!>`, 1);
|
|
20110
20186
|
function TreeItem_1($$anchor, $$props) {
|
|
20111
20187
|
push($$props, true);
|
|
20112
20188
|
let itemIsTest = user_derived(() => isTest($$props.item));
|
|
@@ -20149,24 +20225,50 @@ function TreeItem_1($$anchor, $$props) {
|
|
|
20149
20225
|
var node_1 = sibling(span_1, 2);
|
|
20150
20226
|
{
|
|
20151
20227
|
var consequent_1 = ($$anchor2) => {
|
|
20152
|
-
var
|
|
20228
|
+
var button = root_2();
|
|
20229
|
+
template_effect(
|
|
20230
|
+
($0) => {
|
|
20231
|
+
set_class(button, 1, $0);
|
|
20232
|
+
set_attribute2(button, "aria-label", `Run ${get2(itemIsTest) ? "this test" : "this suite"}`);
|
|
20233
|
+
set_attribute2(button, "title", `Run ${get2(itemIsTest) ? "this test" : "this suite"}`);
|
|
20234
|
+
button.disabled = $$props.isRunning;
|
|
20235
|
+
},
|
|
20236
|
+
[
|
|
20237
|
+
() => clsx2(cn("size-5 flex items-center justify-center text-success text-[10px] rounded transition-opacity", $$props.isRunning ? "opacity-0 pointer-events-none" : "opacity-0 group-hover:opacity-100 focus:opacity-100 hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-accent"))
|
|
20238
|
+
]
|
|
20239
|
+
);
|
|
20240
|
+
delegated("click", button, (e) => {
|
|
20241
|
+
e.stopPropagation();
|
|
20242
|
+
$$props.onRun($$props.item);
|
|
20243
|
+
});
|
|
20244
|
+
delegated("keydown", button, (e) => e.stopPropagation());
|
|
20245
|
+
append($$anchor2, button);
|
|
20246
|
+
};
|
|
20247
|
+
if_block(node_1, ($$render) => {
|
|
20248
|
+
if ($$props.runEnabled) $$render(consequent_1);
|
|
20249
|
+
});
|
|
20250
|
+
}
|
|
20251
|
+
var node_2 = sibling(node_1, 2);
|
|
20252
|
+
{
|
|
20253
|
+
var consequent_2 = ($$anchor2) => {
|
|
20254
|
+
var span_2 = root_3();
|
|
20153
20255
|
template_effect(($0) => set_class(span_2, 1, $0), [
|
|
20154
20256
|
() => clsx2(cn("size-2 rounded-full inline-block shrink-0", statusDotClass($$props.item.status)))
|
|
20155
20257
|
]);
|
|
20156
20258
|
append($$anchor2, span_2);
|
|
20157
20259
|
};
|
|
20158
|
-
if_block(
|
|
20159
|
-
if ($$props.item.status) $$render(
|
|
20260
|
+
if_block(node_2, ($$render) => {
|
|
20261
|
+
if ($$props.item.status) $$render(consequent_2);
|
|
20160
20262
|
});
|
|
20161
20263
|
}
|
|
20162
20264
|
reset(div);
|
|
20163
20265
|
bind_this(div, ($$value) => set(rowEl, $$value), () => get2(rowEl));
|
|
20164
|
-
var
|
|
20266
|
+
var node_3 = sibling(div, 2);
|
|
20165
20267
|
{
|
|
20166
|
-
var
|
|
20268
|
+
var consequent_3 = ($$anchor2) => {
|
|
20167
20269
|
var fragment_1 = comment();
|
|
20168
|
-
var
|
|
20169
|
-
each(
|
|
20270
|
+
var node_4 = first_child(fragment_1);
|
|
20271
|
+
each(node_4, 17, () => Object.values(get2(suiteItem).children).filter(isDefined).filter((c) => hasScreenshots(c)), index, ($$anchor3, child2) => {
|
|
20170
20272
|
{
|
|
20171
20273
|
let $0 = user_derived(() => $$props.level + 1);
|
|
20172
20274
|
TreeItem_1($$anchor3, {
|
|
@@ -20185,6 +20287,12 @@ function TreeItem_1($$anchor, $$props) {
|
|
|
20185
20287
|
get isUpdateMode() {
|
|
20186
20288
|
return $$props.isUpdateMode;
|
|
20187
20289
|
},
|
|
20290
|
+
get runEnabled() {
|
|
20291
|
+
return $$props.runEnabled;
|
|
20292
|
+
},
|
|
20293
|
+
get isRunning() {
|
|
20294
|
+
return $$props.isRunning;
|
|
20295
|
+
},
|
|
20188
20296
|
get onSelect() {
|
|
20189
20297
|
return $$props.onSelect;
|
|
20190
20298
|
},
|
|
@@ -20193,14 +20301,17 @@ function TreeItem_1($$anchor, $$props) {
|
|
|
20193
20301
|
},
|
|
20194
20302
|
get onToggle() {
|
|
20195
20303
|
return $$props.onToggle;
|
|
20304
|
+
},
|
|
20305
|
+
get onRun() {
|
|
20306
|
+
return $$props.onRun;
|
|
20196
20307
|
}
|
|
20197
20308
|
});
|
|
20198
20309
|
}
|
|
20199
20310
|
});
|
|
20200
20311
|
append($$anchor2, fragment_1);
|
|
20201
20312
|
};
|
|
20202
|
-
if_block(
|
|
20203
|
-
if (get2(isOpen)) $$render(
|
|
20313
|
+
if_block(node_3, ($$render) => {
|
|
20314
|
+
if (get2(isOpen)) $$render(consequent_3);
|
|
20204
20315
|
});
|
|
20205
20316
|
}
|
|
20206
20317
|
template_effect(
|
|
@@ -20214,7 +20325,7 @@ function TreeItem_1($$anchor, $$props) {
|
|
|
20214
20325
|
set_text(text2, get2(itemIsTest) ? get2(testItem).browser ?? get2(testItem).title : get2(suiteItem).path[get2(suiteItem).path.length - 1] ?? "Tests");
|
|
20215
20326
|
},
|
|
20216
20327
|
[
|
|
20217
|
-
() => clsx2(cn("flex items-center py-1 cursor-pointer transition-colors select-none gap-1 hover:bg-surface-hover", get2(isSelected) && "bg-surface-selected", get2(isFocused) && "outline outline-1 outline-accent -outline-offset-1"))
|
|
20328
|
+
() => clsx2(cn("group flex items-center py-1 cursor-pointer transition-colors select-none gap-1 hover:bg-surface-hover", get2(isSelected) && "bg-surface-selected", get2(isFocused) && "outline outline-1 outline-accent -outline-offset-1"))
|
|
20218
20329
|
]
|
|
20219
20330
|
);
|
|
20220
20331
|
delegated("click", div, handleClick);
|
|
@@ -20229,15 +20340,16 @@ delegate(["click", "keydown"]);
|
|
|
20229
20340
|
// src/client/components/Sidebar.svelte
|
|
20230
20341
|
var root_12 = from_html(`<div class="text-xs mb-2 px-2 py-1 text-success bg-success/10 rounded-sm">Review and approve screenshots</div>`);
|
|
20231
20342
|
var root_22 = from_html(`<button><span></span> <span> </span></button>`);
|
|
20232
|
-
var
|
|
20343
|
+
var root_32 = from_html(`<button><span class="size-2 rounded-full inline-block shrink-0 bg-info"></span> <span> </span></button>`);
|
|
20233
20344
|
var root_5 = from_html(`<button class="size-9 flex items-center justify-center bg-surface-input border border-edge rounded text-error cursor-pointer text-sm transition-colors hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-accent" aria-label="Stop tests">■</button>`);
|
|
20234
20345
|
var root_6 = from_html(`<button class="size-9 flex items-center justify-center bg-surface-input border border-edge rounded text-success cursor-pointer text-sm transition-colors hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-accent" aria-label="Start tests">▶</button>`);
|
|
20235
20346
|
var root_4 = from_html(`<div class="mt-1"><!></div>`);
|
|
20236
|
-
var
|
|
20237
|
-
var root_9 = from_html(`<
|
|
20238
|
-
var root_10 = from_html(`<button class="flex-1 px-4 py-1.5 border border-
|
|
20239
|
-
var root_11 = from_html(`<
|
|
20240
|
-
var
|
|
20347
|
+
var root_7 = from_html(`<div class="mt-2 text-center text-xs text-fg-muted"> </div>`);
|
|
20348
|
+
var root_9 = from_html(`<div class="py-6 px-4 text-center text-fg-muted text-ui">No tests match filter</div>`);
|
|
20349
|
+
var root_10 = from_html(`<button class="flex-1 px-4 py-1.5 border border-edge rounded text-ui cursor-pointer transition-colors text-center bg-transparent text-fg hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed focus-visible:ring-2 focus-visible:ring-accent">Next ›</button>`);
|
|
20350
|
+
var root_11 = from_html(`<button class="flex-1 px-4 py-1.5 border border-success rounded text-ui cursor-pointer transition-colors text-center bg-success text-white font-semibold hover:brightness-110 disabled:opacity-40 disabled:cursor-not-allowed focus-visible:ring-2 focus-visible:ring-accent">Approve ›</button>`);
|
|
20351
|
+
var root_122 = from_html(`<div class="mt-2 text-center text-xs text-fg-muted"> </div>`);
|
|
20352
|
+
var root2 = from_html(`<div class="w-[300px] min-w-[300px] max-md:w-full max-md:min-w-0 max-md:max-h-[40vh] max-md:border-b max-md:border-r-0 bg-surface-alt border-r border-edge flex flex-col overflow-hidden"><div class="p-4 border-b border-edge bg-surface-alt shrink-0"><div class="flex justify-between items-start"><div><h1 class="m-0 mb-2 text-base font-normal text-fg-bright">Crvy Rprtr</h1> <!> <div class="flex gap-2 text-xs mb-3 tabular-nums"><!> <!></div></div> <!></div> <input type="text" class="w-full px-2.5 py-1.5 bg-surface-input border border-edge rounded text-fg text-ui outline-none focus-visible:ring-2 focus-visible:ring-accent transition-colors focus:border-accent placeholder:text-fg-muted" placeholder="Filter tests\u2026 (status:failed)" aria-label="Filter tests" autocomplete="off"/> <!></div> <div class="flex-1 overflow-y-auto overscroll-y-contain py-1" role="tree"><!> <!></div> <div class="py-3 px-4 border-t border-edge bg-surface-alt shrink-0"><div class="flex gap-2"><!> <button class="flex-1 px-4 py-1.5 border border-edge rounded text-ui cursor-pointer transition-colors text-center bg-transparent text-fg hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed focus-visible:ring-2 focus-visible:ring-accent">Approve All</button></div> <!></div></div>`);
|
|
20241
20353
|
function Sidebar($$anchor, $$props) {
|
|
20242
20354
|
push($$props, true);
|
|
20243
20355
|
let filterInput = state("");
|
|
@@ -20332,7 +20444,7 @@ function Sidebar($$anchor, $$props) {
|
|
|
20332
20444
|
var node_2 = sibling(node_1, 2);
|
|
20333
20445
|
{
|
|
20334
20446
|
var consequent_1 = ($$anchor2) => {
|
|
20335
|
-
var button_1 =
|
|
20447
|
+
var button_1 = root_32();
|
|
20336
20448
|
var span_2 = sibling(child(button_1), 2);
|
|
20337
20449
|
var text_1 = child(span_2, true);
|
|
20338
20450
|
reset(span_2);
|
|
@@ -20384,16 +20496,29 @@ function Sidebar($$anchor, $$props) {
|
|
|
20384
20496
|
append($$anchor2, div_6);
|
|
20385
20497
|
};
|
|
20386
20498
|
if_block(node_3, ($$render) => {
|
|
20387
|
-
if (
|
|
20499
|
+
if ($$props.runEnabled) $$render(consequent_3);
|
|
20388
20500
|
});
|
|
20389
20501
|
}
|
|
20390
20502
|
reset(div_2);
|
|
20391
20503
|
var input = sibling(div_2, 2);
|
|
20392
20504
|
remove_input_defaults(input);
|
|
20505
|
+
var node_5 = sibling(input, 2);
|
|
20506
|
+
{
|
|
20507
|
+
var consequent_4 = ($$anchor2) => {
|
|
20508
|
+
var div_7 = root_7();
|
|
20509
|
+
var text_2 = child(div_7, true);
|
|
20510
|
+
reset(div_7);
|
|
20511
|
+
template_effect(() => set_text(text_2, $$props.runMessage));
|
|
20512
|
+
append($$anchor2, div_7);
|
|
20513
|
+
};
|
|
20514
|
+
if_block(node_5, ($$render) => {
|
|
20515
|
+
if ($$props.runMessage) $$render(consequent_4);
|
|
20516
|
+
});
|
|
20517
|
+
}
|
|
20393
20518
|
reset(div_1);
|
|
20394
|
-
var
|
|
20395
|
-
var
|
|
20396
|
-
each(
|
|
20519
|
+
var div_8 = sibling(div_1, 2);
|
|
20520
|
+
var node_6 = child(div_8);
|
|
20521
|
+
each(node_6, 17, () => get2(visibleChildren), index, ($$anchor2, child2) => {
|
|
20397
20522
|
TreeItem_1($$anchor2, {
|
|
20398
20523
|
get item() {
|
|
20399
20524
|
return get2(child2);
|
|
@@ -20408,6 +20533,12 @@ function Sidebar($$anchor, $$props) {
|
|
|
20408
20533
|
get isUpdateMode() {
|
|
20409
20534
|
return $$props.isUpdateMode;
|
|
20410
20535
|
},
|
|
20536
|
+
get runEnabled() {
|
|
20537
|
+
return $$props.runEnabled;
|
|
20538
|
+
},
|
|
20539
|
+
get isRunning() {
|
|
20540
|
+
return $$props.isRunning;
|
|
20541
|
+
},
|
|
20411
20542
|
get onSelect() {
|
|
20412
20543
|
return $$props.onSelect;
|
|
20413
20544
|
},
|
|
@@ -20416,26 +20547,29 @@ function Sidebar($$anchor, $$props) {
|
|
|
20416
20547
|
},
|
|
20417
20548
|
get onToggle() {
|
|
20418
20549
|
return $$props.onToggle;
|
|
20550
|
+
},
|
|
20551
|
+
get onRun() {
|
|
20552
|
+
return $$props.onRun;
|
|
20419
20553
|
}
|
|
20420
20554
|
});
|
|
20421
20555
|
});
|
|
20422
|
-
var
|
|
20556
|
+
var node_7 = sibling(node_6, 2);
|
|
20423
20557
|
{
|
|
20424
|
-
var
|
|
20425
|
-
var
|
|
20426
|
-
append($$anchor2,
|
|
20558
|
+
var consequent_5 = ($$anchor2) => {
|
|
20559
|
+
var div_9 = root_9();
|
|
20560
|
+
append($$anchor2, div_9);
|
|
20427
20561
|
};
|
|
20428
|
-
if_block(
|
|
20429
|
-
if (get2(visibleChildren).length === 0) $$render(
|
|
20562
|
+
if_block(node_7, ($$render) => {
|
|
20563
|
+
if (get2(visibleChildren).length === 0) $$render(consequent_5);
|
|
20430
20564
|
});
|
|
20431
20565
|
}
|
|
20432
|
-
reset(
|
|
20433
|
-
var
|
|
20434
|
-
var
|
|
20435
|
-
var
|
|
20566
|
+
reset(div_8);
|
|
20567
|
+
var div_10 = sibling(div_8, 2);
|
|
20568
|
+
var div_11 = child(div_10);
|
|
20569
|
+
var node_8 = child(div_11);
|
|
20436
20570
|
{
|
|
20437
|
-
var
|
|
20438
|
-
var button_4 =
|
|
20571
|
+
var consequent_6 = ($$anchor2) => {
|
|
20572
|
+
var button_4 = root_10();
|
|
20439
20573
|
template_effect(() => button_4.disabled = !$$props.canApprove);
|
|
20440
20574
|
delegated("click", button_4, function(...$$args) {
|
|
20441
20575
|
$$props.onNext?.apply(this, $$args);
|
|
@@ -20443,34 +20577,34 @@ function Sidebar($$anchor, $$props) {
|
|
|
20443
20577
|
append($$anchor2, button_4);
|
|
20444
20578
|
};
|
|
20445
20579
|
var alternate_1 = ($$anchor2) => {
|
|
20446
|
-
var button_5 =
|
|
20580
|
+
var button_5 = root_11();
|
|
20447
20581
|
template_effect(() => button_5.disabled = !$$props.canApprove);
|
|
20448
20582
|
delegated("click", button_5, function(...$$args) {
|
|
20449
20583
|
$$props.onApprove?.apply(this, $$args);
|
|
20450
20584
|
});
|
|
20451
20585
|
append($$anchor2, button_5);
|
|
20452
20586
|
};
|
|
20453
|
-
if_block(
|
|
20454
|
-
if (get2(isAlt)) $$render(
|
|
20587
|
+
if_block(node_8, ($$render) => {
|
|
20588
|
+
if (get2(isAlt)) $$render(consequent_6);
|
|
20455
20589
|
else $$render(alternate_1, -1);
|
|
20456
20590
|
});
|
|
20457
20591
|
}
|
|
20458
|
-
var button_6 = sibling(
|
|
20459
|
-
reset(
|
|
20460
|
-
var
|
|
20592
|
+
var button_6 = sibling(node_8, 2);
|
|
20593
|
+
reset(div_11);
|
|
20594
|
+
var node_9 = sibling(div_11, 2);
|
|
20461
20595
|
{
|
|
20462
|
-
var
|
|
20463
|
-
var
|
|
20464
|
-
var
|
|
20465
|
-
reset(
|
|
20466
|
-
template_effect(() => set_text(
|
|
20467
|
-
append($$anchor2,
|
|
20596
|
+
var consequent_7 = ($$anchor2) => {
|
|
20597
|
+
var div_12 = root_122();
|
|
20598
|
+
var text_3 = child(div_12, true);
|
|
20599
|
+
reset(div_12);
|
|
20600
|
+
template_effect(() => set_text(text_3, $$props.approvalMessage));
|
|
20601
|
+
append($$anchor2, div_12);
|
|
20468
20602
|
};
|
|
20469
|
-
if_block(
|
|
20470
|
-
if (!$$props.approvalEnabled && $$props.approvalMessage) $$render(
|
|
20603
|
+
if_block(node_9, ($$render) => {
|
|
20604
|
+
if (!$$props.approvalEnabled && $$props.approvalMessage) $$render(consequent_7);
|
|
20471
20605
|
});
|
|
20472
20606
|
}
|
|
20473
|
-
reset(
|
|
20607
|
+
reset(div_10);
|
|
20474
20608
|
reset(div);
|
|
20475
20609
|
template_effect(() => {
|
|
20476
20610
|
set_value(input, get2(filterInput));
|
|
@@ -20569,7 +20703,7 @@ delegate(["input"]);
|
|
|
20569
20703
|
// src/client/components/SideBySideView.svelte
|
|
20570
20704
|
var root_14 = from_html(`<div class="flex-1 flex flex-col bg-surface-panel rounded-md overflow-hidden min-w-0 border-2 border-green-500/60"><h3 class="m-0 px-3 py-2 text-xs font-bold bg-green-500/25 text-green-800 dark:text-green-200 uppercase tracking-wider"> </h3> <div class="flex items-center justify-center p-2"><img alt="Expected" class="w-auto max-w-full object-contain mx-auto" loading="lazy"/></div></div>`);
|
|
20571
20705
|
var root_24 = from_html(`<div class="flex-1 flex flex-col bg-surface-panel rounded-md overflow-hidden min-w-0 border-2 border-yellow-500/60"><h3 class="m-0 px-3 py-2 text-xs font-bold bg-yellow-500/25 text-yellow-800 dark:text-yellow-200 uppercase tracking-wider">Diff</h3> <div class="flex items-center justify-center p-2"><img alt="Diff" class="w-auto max-w-full object-contain mx-auto" loading="lazy"/></div></div>`);
|
|
20572
|
-
var
|
|
20706
|
+
var root_33 = from_html(`<div class="flex-1 flex flex-col bg-surface-panel rounded-md overflow-hidden min-w-0 border-2 border-red-500/60"><h3 class="m-0 px-3 py-2 text-xs font-bold bg-red-500/25 text-red-800 dark:text-red-200 uppercase tracking-wider">Actual</h3> <div class="flex items-center justify-center p-2"><img alt="Actual" class="w-auto max-w-full object-contain mx-auto" loading="lazy"/></div></div>`);
|
|
20573
20707
|
var root3 = from_html(`<div><!> <!> <!></div>`);
|
|
20574
20708
|
function SideBySideView($$anchor, $$props) {
|
|
20575
20709
|
push($$props, true);
|
|
@@ -20627,7 +20761,7 @@ function SideBySideView($$anchor, $$props) {
|
|
|
20627
20761
|
var node_2 = sibling(node_1, 2);
|
|
20628
20762
|
{
|
|
20629
20763
|
var consequent_2 = ($$anchor2) => {
|
|
20630
|
-
var div_5 =
|
|
20764
|
+
var div_5 = root_33();
|
|
20631
20765
|
var div_6 = sibling(child(div_5), 2);
|
|
20632
20766
|
var img_3 = child(div_6);
|
|
20633
20767
|
reset(div_6);
|
|
@@ -20728,7 +20862,7 @@ delegate(["click", "keydown"]);
|
|
|
20728
20862
|
// src/client/components/BlendView.svelte
|
|
20729
20863
|
var root_16 = from_html(`<div class="absolute w-full h-full flex"><img alt="Expected" class="max-w-full border border-green-500 invert"/></div>`);
|
|
20730
20864
|
var root_26 = from_html(`<img alt="Diff" class="border border-transparent max-w-full invert opacity-0"/>`);
|
|
20731
|
-
var
|
|
20865
|
+
var root_34 = from_html(`<div class="absolute w-full h-full flex"><img alt="Actual" class="max-w-full border border-red-500 mix-blend-difference invert"/></div>`);
|
|
20732
20866
|
var root5 = from_html(`<div class="flex flex-col gap-3 items-center"><div class="flex flex-col bg-surface-panel rounded-md overflow-hidden w-fit max-w-full border-2 border-purple-500/60"><h3 class="m-0 px-3 py-2 text-xs font-bold bg-purple-500/25 text-purple-800 dark:text-purple-400 uppercase tracking-wider">Blend (Difference)</h3> <div class="p-2"><div class="relative flex invert"><!> <!> <!></div></div></div></div>`);
|
|
20733
20867
|
function BlendView($$anchor, $$props) {
|
|
20734
20868
|
push($$props, true);
|
|
@@ -20763,7 +20897,7 @@ function BlendView($$anchor, $$props) {
|
|
|
20763
20897
|
var node_2 = sibling(node_1, 2);
|
|
20764
20898
|
{
|
|
20765
20899
|
var consequent_2 = ($$anchor2) => {
|
|
20766
|
-
var div_5 =
|
|
20900
|
+
var div_5 = root_34();
|
|
20767
20901
|
var img_2 = child(div_5);
|
|
20768
20902
|
reset(div_5);
|
|
20769
20903
|
template_effect(() => set_attribute2(img_2, "src", $$props.image.actual));
|
|
@@ -20784,13 +20918,13 @@ function BlendView($$anchor, $$props) {
|
|
|
20784
20918
|
// src/client/components/ResultsPage.svelte
|
|
20785
20919
|
var root_27 = from_html(`<button> </button>`);
|
|
20786
20920
|
var root_42 = from_html(`<button> </button>`);
|
|
20787
|
-
var
|
|
20921
|
+
var root_35 = from_html(`<div class="flex gap-1 flex-wrap justify-center mt-1"></div>`);
|
|
20788
20922
|
var root_52 = from_html(`<div class="flex-1 flex items-center justify-center text-fg-muted text-base">No image to display</div>`);
|
|
20789
20923
|
var root_62 = from_html(`<div class="max-w-xl w-full rounded-md border border-edge bg-surface-panel px-5 py-6 text-center"><h3 class="m-0 text-sm font-semibold text-fg-bright uppercase tracking-wide">Passed Visual Assertion</h3> <p class="mt-3 text-sm leading-6 text-fg-muted">Playwright reported this screenshot assertion, but did not emit an actual, expected, or diff artifact for the passing comparison.</p></div>`);
|
|
20790
|
-
var
|
|
20924
|
+
var root_72 = from_html(`<div class="w-full flex flex-col gap-3"><!></div>`);
|
|
20791
20925
|
var root_112 = from_html(`<div class="flex flex-col bg-surface-panel rounded-md overflow-hidden min-w-0 border-2 border-red-500/60"><h3 class="m-0 px-3 py-2 text-xs font-bold bg-red-500/25 text-red-800 dark:text-red-200 uppercase tracking-wider">Actual</h3> <div class="flex items-center justify-center p-2"><img alt="Actual" class="w-auto max-w-full object-contain mx-auto" loading="lazy"/></div></div>`);
|
|
20792
20926
|
var root_92 = from_html(`<div class="flex flex-col gap-3"><!></div>`);
|
|
20793
|
-
var
|
|
20927
|
+
var root_123 = from_html(`<div class="w-full flex flex-col gap-3"><!></div>`);
|
|
20794
20928
|
var root_152 = from_html(`<button> </button>`);
|
|
20795
20929
|
var root_162 = from_html(`<span class="px-1 text-fg-muted text-xs"></span>`);
|
|
20796
20930
|
var root_132 = from_html(`<div class="py-2 px-5 bg-surface-alt border-t border-edge flex items-center justify-center gap-1 shrink-0"><button aria-label="Previous retry">‹</button> <div class="flex gap-0.5 items-center"></div> <button aria-label="Next retry">›</button></div>`);
|
|
@@ -20861,7 +20995,7 @@ function ResultsPage($$anchor, $$props) {
|
|
|
20861
20995
|
var node_2 = sibling(h2, 2);
|
|
20862
20996
|
{
|
|
20863
20997
|
var consequent_1 = ($$anchor2) => {
|
|
20864
|
-
var div_4 =
|
|
20998
|
+
var div_4 = root_35();
|
|
20865
20999
|
each(div_4, 21, () => get2(imageNames), index, ($$anchor3, name) => {
|
|
20866
21000
|
var button_1 = root_42();
|
|
20867
21001
|
var text_2 = child(button_1, true);
|
|
@@ -20900,7 +21034,7 @@ function ResultsPage($$anchor, $$props) {
|
|
|
20900
21034
|
append($$anchor2, div_8);
|
|
20901
21035
|
};
|
|
20902
21036
|
var consequent_4 = ($$anchor2) => {
|
|
20903
|
-
var div_9 =
|
|
21037
|
+
var div_9 = root_72();
|
|
20904
21038
|
var node_4 = child(div_9);
|
|
20905
21039
|
SideBySideView(node_4, {
|
|
20906
21040
|
get image() {
|
|
@@ -20952,7 +21086,7 @@ function ResultsPage($$anchor, $$props) {
|
|
|
20952
21086
|
append($$anchor2, div_10);
|
|
20953
21087
|
};
|
|
20954
21088
|
var consequent_9 = ($$anchor2) => {
|
|
20955
|
-
var div_13 =
|
|
21089
|
+
var div_13 = root_123();
|
|
20956
21090
|
var node_6 = child(div_13);
|
|
20957
21091
|
BlendView(node_6, {
|
|
20958
21092
|
get image() {
|
|
@@ -21087,7 +21221,10 @@ var root8 = from_html(`<div class="flex h-dvh relative max-md:flex-col overflow-
|
|
|
21087
21221
|
function App($$anchor, $$props) {
|
|
21088
21222
|
push($$props, true);
|
|
21089
21223
|
let tests = proxy($$props.initialTests);
|
|
21090
|
-
let isRunning =
|
|
21224
|
+
let isRunning = state(proxy($$props.isRunning));
|
|
21225
|
+
let runMessage = state(null);
|
|
21226
|
+
let runSnapshot = null;
|
|
21227
|
+
let runEventIds = /* @__PURE__ */ new Set();
|
|
21091
21228
|
let openedTestPath = state(proxy([]));
|
|
21092
21229
|
let filter = state(proxy({ status: null, subStrings: [] }));
|
|
21093
21230
|
let viewMode = state(proxy(getViewMode()));
|
|
@@ -21275,9 +21412,67 @@ function App($$anchor, $$props) {
|
|
|
21275
21412
|
});
|
|
21276
21413
|
recalcAllSuiteStatuses(tests);
|
|
21277
21414
|
}
|
|
21278
|
-
function handleStart() {
|
|
21415
|
+
async function handleStart(filters, target) {
|
|
21416
|
+
if (get2(isRunning) || runSnapshot !== null) {
|
|
21417
|
+
set(runMessage, "A run is already in progress");
|
|
21418
|
+
return;
|
|
21419
|
+
}
|
|
21420
|
+
const markTarget = target ?? tests;
|
|
21421
|
+
runSnapshot = captureTestStatuses(markTarget);
|
|
21422
|
+
runEventIds = /* @__PURE__ */ new Set();
|
|
21423
|
+
markTestsPending(markTarget);
|
|
21424
|
+
recalcAllSuiteStatuses(tests);
|
|
21425
|
+
const res = await fetch("/api/run", {
|
|
21426
|
+
method: "POST",
|
|
21427
|
+
headers: { "Content-Type": "application/json" },
|
|
21428
|
+
body: JSON.stringify(filters ?? {})
|
|
21429
|
+
});
|
|
21430
|
+
if (res.status === 409 || res.status === 400) {
|
|
21431
|
+
if (runSnapshot !== null) {
|
|
21432
|
+
restoreTestStatuses(tests, runSnapshot, runEventIds);
|
|
21433
|
+
recalcAllSuiteStatuses(tests);
|
|
21434
|
+
runSnapshot = null;
|
|
21435
|
+
runEventIds = /* @__PURE__ */ new Set();
|
|
21436
|
+
}
|
|
21437
|
+
const body = await res.json().catch(() => null);
|
|
21438
|
+
set(
|
|
21439
|
+
runMessage,
|
|
21440
|
+
body?.reason === "already-running" ? "A run is already in progress" : body?.reason === "no-tests" ? "No tests selected" : "Connect a Playwright reporter to enable running",
|
|
21441
|
+
true
|
|
21442
|
+
);
|
|
21443
|
+
}
|
|
21279
21444
|
}
|
|
21280
|
-
function
|
|
21445
|
+
function finalizeRunSnapshot() {
|
|
21446
|
+
if (runSnapshot === null) return;
|
|
21447
|
+
restoreTestStatuses(tests, runSnapshot, runEventIds);
|
|
21448
|
+
recalcAllSuiteStatuses(tests);
|
|
21449
|
+
if (runEventIds.size === 0) {
|
|
21450
|
+
set(runMessage, "Run produced no results \u2014 see the server terminal for details.");
|
|
21451
|
+
}
|
|
21452
|
+
runSnapshot = null;
|
|
21453
|
+
runEventIds = /* @__PURE__ */ new Set();
|
|
21454
|
+
}
|
|
21455
|
+
function toDescriptor(test) {
|
|
21456
|
+
const { file: file2, line, column } = test.location ?? {};
|
|
21457
|
+
if (file2 === void 0 || line === void 0) return null;
|
|
21458
|
+
return {
|
|
21459
|
+
file: file2,
|
|
21460
|
+
line,
|
|
21461
|
+
column,
|
|
21462
|
+
projectName: test.projectName,
|
|
21463
|
+
titlePath: [...test.titlePath ?? [], test.title]
|
|
21464
|
+
};
|
|
21465
|
+
}
|
|
21466
|
+
function handleRunItem(item) {
|
|
21467
|
+
const descriptors = (isTest(item) ? [item] : getAllTests(item)).map(toDescriptor).filter((d) => d !== null);
|
|
21468
|
+
if (descriptors.length === 0) {
|
|
21469
|
+
set(runMessage, "Cannot run: no source location for the selected test(s)");
|
|
21470
|
+
return;
|
|
21471
|
+
}
|
|
21472
|
+
handleStart({ tests: descriptors }, item);
|
|
21473
|
+
}
|
|
21474
|
+
async function handleStop() {
|
|
21475
|
+
await fetch("/api/stop", { method: "POST" });
|
|
21281
21476
|
}
|
|
21282
21477
|
function handleImageChange(name) {
|
|
21283
21478
|
set(imageName, name, true);
|
|
@@ -21320,6 +21515,7 @@ function App($$anchor, $$props) {
|
|
|
21320
21515
|
const current = collectTestsById(tests);
|
|
21321
21516
|
current[msg.data.id] = msg.data;
|
|
21322
21517
|
syncTreeState(tests, current);
|
|
21518
|
+
if (runSnapshot !== null) runEventIds.add(msg.data.id);
|
|
21323
21519
|
break;
|
|
21324
21520
|
}
|
|
21325
21521
|
case "run-end": {
|
|
@@ -21339,6 +21535,16 @@ function App($$anchor, $$props) {
|
|
|
21339
21535
|
}
|
|
21340
21536
|
case "approve":
|
|
21341
21537
|
break;
|
|
21538
|
+
case "run-status": {
|
|
21539
|
+
if (msg.data.running) {
|
|
21540
|
+
set(isRunning, true);
|
|
21541
|
+
set(runMessage, null);
|
|
21542
|
+
} else {
|
|
21543
|
+
set(isRunning, false);
|
|
21544
|
+
finalizeRunSnapshot();
|
|
21545
|
+
}
|
|
21546
|
+
break;
|
|
21547
|
+
}
|
|
21342
21548
|
}
|
|
21343
21549
|
};
|
|
21344
21550
|
ws.onmessage = handleMessage;
|
|
@@ -21363,7 +21569,9 @@ function App($$anchor, $$props) {
|
|
|
21363
21569
|
get isReport() {
|
|
21364
21570
|
return $$props.isReport;
|
|
21365
21571
|
},
|
|
21366
|
-
isRunning
|
|
21572
|
+
get isRunning() {
|
|
21573
|
+
return get2(isRunning);
|
|
21574
|
+
},
|
|
21367
21575
|
get isUpdateMode() {
|
|
21368
21576
|
return $$props.isUpdateMode;
|
|
21369
21577
|
},
|
|
@@ -21373,6 +21581,12 @@ function App($$anchor, $$props) {
|
|
|
21373
21581
|
get approvalMessage() {
|
|
21374
21582
|
return $$props.approvalMessage;
|
|
21375
21583
|
},
|
|
21584
|
+
get runEnabled() {
|
|
21585
|
+
return $$props.runEnabled;
|
|
21586
|
+
},
|
|
21587
|
+
get runMessage() {
|
|
21588
|
+
return get2(runMessage);
|
|
21589
|
+
},
|
|
21376
21590
|
get filter() {
|
|
21377
21591
|
return get2(filter);
|
|
21378
21592
|
},
|
|
@@ -21385,6 +21599,7 @@ function App($$anchor, $$props) {
|
|
|
21385
21599
|
onToggle: handleSuiteToggle,
|
|
21386
21600
|
onStart: handleStart,
|
|
21387
21601
|
onStop: handleStop,
|
|
21602
|
+
onRun: handleRunItem,
|
|
21388
21603
|
onApprove: handleApproveAndGoNext,
|
|
21389
21604
|
onNext: handleGoToNextFailed,
|
|
21390
21605
|
onApproveAll: handleApproveAllTests
|
|
@@ -21460,7 +21675,9 @@ function loadBootstrapData() {
|
|
|
21460
21675
|
isUpdateMode: parsed.report.isUpdateMode,
|
|
21461
21676
|
liveUpdates: parsed.liveUpdates,
|
|
21462
21677
|
approvalEnabled: parsed.approvalEnabled,
|
|
21463
|
-
approvalMessage: parsed.approvalMessage
|
|
21678
|
+
approvalMessage: parsed.approvalMessage,
|
|
21679
|
+
runEnabled: parsed.runEnabled ?? false,
|
|
21680
|
+
isRunning: parsed.report.isRunning ?? false
|
|
21464
21681
|
};
|
|
21465
21682
|
}
|
|
21466
21683
|
async function loadReportData() {
|
|
@@ -21475,7 +21692,9 @@ async function loadReportData() {
|
|
|
21475
21692
|
isReport: true,
|
|
21476
21693
|
isUpdateMode: parsed.isUpdateMode ?? false,
|
|
21477
21694
|
liveUpdates: true,
|
|
21478
|
-
approvalEnabled: true
|
|
21695
|
+
approvalEnabled: true,
|
|
21696
|
+
runEnabled: parsed.runEnabled ?? false,
|
|
21697
|
+
isRunning: parsed.isRunning ?? false
|
|
21479
21698
|
};
|
|
21480
21699
|
}
|
|
21481
21700
|
var handleApprove = async (id, retry, image) => {
|
|
@@ -21503,6 +21722,8 @@ mount(App, {
|
|
|
21503
21722
|
liveUpdates: initialState.liveUpdates,
|
|
21504
21723
|
approvalEnabled: initialState.approvalEnabled,
|
|
21505
21724
|
approvalMessage: initialState.approvalMessage,
|
|
21725
|
+
runEnabled: initialState.runEnabled,
|
|
21726
|
+
isRunning: initialState.isRunning,
|
|
21506
21727
|
onApprove: handleApprove,
|
|
21507
21728
|
onApproveAll: handleApproveAll
|
|
21508
21729
|
}
|