@piwitests/reporter 0.54.0 → 0.56.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/dist/cli/index.js +3578 -2460
- package/dist/global-setup-module.js +96 -57
- package/dist/index.d.ts +30 -9
- package/dist/index.js +6171 -5344
- package/dist/internal/capture/attachments.d.ts +2 -0
- package/dist/internal/capture/attachments.js +2 -0
- package/dist/internal/capture/capture-fixtures.d.ts +32 -10
- package/dist/internal/capture/capture-fixtures.js +1914 -1391
- package/dist/internal/capture/locator-healing.js +70 -26
- package/dist/internal/capture/pick-on-failure.js +84 -54
- package/package.json +1 -1
|
@@ -35,7 +35,7 @@ __export(global_setup_module_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(global_setup_module_exports);
|
|
36
36
|
|
|
37
37
|
// src/public/global-setup.ts
|
|
38
|
-
var
|
|
38
|
+
var path7 = __toESM(require("path"));
|
|
39
39
|
|
|
40
40
|
// src/internal/support/errors.ts
|
|
41
41
|
function errorMessage(error) {
|
|
@@ -44,7 +44,7 @@ function errorMessage(error) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// src/public/global-setup.ts
|
|
47
|
-
var
|
|
47
|
+
var fs6 = __toESM(require("fs"));
|
|
48
48
|
|
|
49
49
|
// src/internal/config/desktop.ts
|
|
50
50
|
var fs = __toESM(require("fs"));
|
|
@@ -274,11 +274,15 @@ var HttpClient = class {
|
|
|
274
274
|
* @param logger Prefixed logger for verbose diagnostics.
|
|
275
275
|
* @param timeout Socket inactivity timeout in ms (default 30s), so a hung
|
|
276
276
|
* server fails fast rather than stalling the reporter.
|
|
277
|
+
* @param uploadTimeout Socket inactivity timeout of a multipart upload in ms
|
|
278
|
+
* (default 10 min): the dashboard answers one only once it
|
|
279
|
+
* has stored every report and trace in it.
|
|
277
280
|
*/
|
|
278
|
-
constructor(serverUrl, logger = new Logger(), timeout = 3e4) {
|
|
281
|
+
constructor(serverUrl, logger = new Logger(), timeout = 3e4, uploadTimeout = 10 * 60 * 1e3) {
|
|
279
282
|
this.serverUrl = serverUrl;
|
|
280
283
|
this.logger = logger;
|
|
281
284
|
this.timeout = timeout;
|
|
285
|
+
this.uploadTimeout = uploadTimeout;
|
|
282
286
|
/** Requests in flight, so `close` can abort them. */
|
|
283
287
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
284
288
|
/** Set by `close`: every later request fails with this reason. */
|
|
@@ -426,7 +430,7 @@ var HttpClient = class {
|
|
|
426
430
|
/** Send a multipart form-data POST request. Used for report and trace uploads. */
|
|
427
431
|
async postFormData(pathname, form, auth) {
|
|
428
432
|
const headers = form.getHeaders();
|
|
429
|
-
const res = await this.request("POST", pathname, { headers, form, auth });
|
|
433
|
+
const res = await this.request("POST", pathname, { headers, form, auth, timeout: this.uploadTimeout });
|
|
430
434
|
if (res.status < 200 || res.status >= 300) {
|
|
431
435
|
this.logger.debugError(`Response: ${res.text}`);
|
|
432
436
|
const detail = parseErrorMessage(res.text);
|
|
@@ -443,7 +447,7 @@ var HttpClient = class {
|
|
|
443
447
|
}
|
|
444
448
|
/** Unified request core: transport, headers, auth, response accumulation, timeout. */
|
|
445
449
|
request(method, pathname, opts) {
|
|
446
|
-
return new Promise((
|
|
450
|
+
return new Promise((resolve3, reject) => {
|
|
447
451
|
if (this.closedReason !== null) {
|
|
448
452
|
reject(new Error(this.closedReason));
|
|
449
453
|
return;
|
|
@@ -466,7 +470,7 @@ var HttpClient = class {
|
|
|
466
470
|
data += chunk;
|
|
467
471
|
});
|
|
468
472
|
res.on("end", () => {
|
|
469
|
-
|
|
473
|
+
resolve3({ status: res.statusCode ?? 0, text: data, headers: res.headers });
|
|
470
474
|
});
|
|
471
475
|
res.on("error", reject);
|
|
472
476
|
res.on("close", () => {
|
|
@@ -479,8 +483,9 @@ var HttpClient = class {
|
|
|
479
483
|
this.inFlight.add(req);
|
|
480
484
|
req.on("close", () => this.inFlight.delete(req));
|
|
481
485
|
req.on("error", reject);
|
|
482
|
-
|
|
483
|
-
|
|
486
|
+
const timeout = opts.timeout ?? this.timeout;
|
|
487
|
+
req.setTimeout(timeout, () => {
|
|
488
|
+
req.destroy(new Error(`Request to ${pathname} timed out after ${timeout}ms`));
|
|
484
489
|
});
|
|
485
490
|
if (opts.form) {
|
|
486
491
|
opts.form.on("error", (error) => req.destroy(error));
|
|
@@ -556,6 +561,16 @@ function resolveShardInfo(config, env = process.env) {
|
|
|
556
561
|
|
|
557
562
|
// src/internal/collect/metadata-collector.ts
|
|
558
563
|
var import_node_child_process = require("child_process");
|
|
564
|
+
|
|
565
|
+
// src/internal/support/test-paths.ts
|
|
566
|
+
var path2 = __toESM(require("path"));
|
|
567
|
+
function configDirFromConfig(config) {
|
|
568
|
+
if (config?.configFile) return path2.dirname(config.configFile);
|
|
569
|
+
if (config?.rootDir) return config.rootDir;
|
|
570
|
+
return process.cwd();
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// src/internal/collect/metadata-collector.ts
|
|
559
574
|
var DETACHED_HEAD = "HEAD";
|
|
560
575
|
function resolveScmBranch(env, gitBranch) {
|
|
561
576
|
const pick = (...names) => {
|
|
@@ -585,22 +600,22 @@ function normalizeRef(ref) {
|
|
|
585
600
|
}
|
|
586
601
|
|
|
587
602
|
// src/internal/support/setup-file.ts
|
|
588
|
-
var
|
|
603
|
+
var path3 = __toESM(require("path"));
|
|
589
604
|
var os3 = __toESM(require("os"));
|
|
590
605
|
var fs2 = __toESM(require("fs"));
|
|
591
606
|
function getSetupFilePath(projectName) {
|
|
592
|
-
return
|
|
607
|
+
return path3.join(os3.tmpdir(), `piwi-dashboard-setup-${hashForProject(projectName)}.json`);
|
|
593
608
|
}
|
|
594
609
|
|
|
595
610
|
// src/internal/support/aria-sampling.ts
|
|
596
|
-
var
|
|
611
|
+
var path4 = __toESM(require("path"));
|
|
597
612
|
var os4 = __toESM(require("os"));
|
|
598
613
|
var fs3 = __toESM(require("fs"));
|
|
599
614
|
function ariaSampleIdentity(filePath, title) {
|
|
600
615
|
return `${filePath}\0${title}`;
|
|
601
616
|
}
|
|
602
617
|
function getAriaSampleFilePath(projectName) {
|
|
603
|
-
return
|
|
618
|
+
return path4.join(os4.tmpdir(), `piwi-dashboard-aria-sample-${hashForProject(projectName)}.json`);
|
|
604
619
|
}
|
|
605
620
|
function writeAriaSampleFile(projectName, identities) {
|
|
606
621
|
try {
|
|
@@ -713,8 +728,8 @@ function requestError(error) {
|
|
|
713
728
|
return first.replace(/^apiRequestContext\.\w+:\s*/, "");
|
|
714
729
|
}
|
|
715
730
|
var playwrightProbe = async (target, timeoutMs) => {
|
|
716
|
-
const { request } = await import("@playwright/test");
|
|
717
|
-
const context = await
|
|
731
|
+
const { request: request3 } = await import("@playwright/test");
|
|
732
|
+
const context = await request3.newContext({ ignoreHTTPSErrors: target.ignoreHTTPSErrors, proxy: target.proxy });
|
|
718
733
|
try {
|
|
719
734
|
const response = await context.get(target.url, { timeout: timeoutMs, maxRedirects: 0, failOnStatusCode: false });
|
|
720
735
|
return { status: response.status() };
|
|
@@ -740,7 +755,7 @@ async function checkBaseUrl(target, options = {}) {
|
|
|
740
755
|
} else {
|
|
741
756
|
reason = outcome.error;
|
|
742
757
|
}
|
|
743
|
-
if (attempt < attempts) await new Promise((
|
|
758
|
+
if (attempt < attempts) await new Promise((resolve3) => setTimeout(resolve3, delayMs));
|
|
744
759
|
}
|
|
745
760
|
return `${reason} (${attempts} ${attempts === 1 ? "attempt" : "attempts"})`;
|
|
746
761
|
}
|
|
@@ -765,7 +780,13 @@ async function checkBaseUrls(config, options = {}) {
|
|
|
765
780
|
|
|
766
781
|
// src/internal/manifest/collect.ts
|
|
767
782
|
var fs4 = __toESM(require("fs"));
|
|
768
|
-
var
|
|
783
|
+
var path5 = __toESM(require("path"));
|
|
784
|
+
|
|
785
|
+
// src/internal/transport/backend-request.ts
|
|
786
|
+
var http2 = __toESM(require("http"));
|
|
787
|
+
var https2 = __toESM(require("https"));
|
|
788
|
+
|
|
789
|
+
// src/internal/manifest/collect.ts
|
|
769
790
|
var COMMITTED_MANIFEST_FILE = "piwi.manifest.json";
|
|
770
791
|
var INSTRUMENTATION_MANIFEST_PATH = "/__piwi/manifest";
|
|
771
792
|
var INSTRUMENTATION_HEADERS = ["x-piwi-logs", "x-piwi-trace"];
|
|
@@ -793,7 +814,7 @@ function hasInstrumentationHeader(headers) {
|
|
|
793
814
|
return INSTRUMENTATION_HEADERS.some((h) => get(h) != null && get(h) !== "");
|
|
794
815
|
}
|
|
795
816
|
function readCommittedManifest(configDir) {
|
|
796
|
-
const file =
|
|
817
|
+
const file = path5.join(configDir, COMMITTED_MANIFEST_FILE);
|
|
797
818
|
try {
|
|
798
819
|
if (!fs4.existsSync(file)) return null;
|
|
799
820
|
return parseManifestJson(fs4.readFileSync(file, "utf8"));
|
|
@@ -801,34 +822,20 @@ function readCommittedManifest(configDir) {
|
|
|
801
822
|
return null;
|
|
802
823
|
}
|
|
803
824
|
}
|
|
804
|
-
function configDirFromConfig(config) {
|
|
805
|
-
if (config?.configFile) return path4.dirname(config.configFile);
|
|
806
|
-
if (config?.rootDir) return config.rootDir;
|
|
807
|
-
return process.cwd();
|
|
808
|
-
}
|
|
809
|
-
function baseUrlFromConfig(config) {
|
|
810
|
-
const top = config?.use?.baseURL;
|
|
811
|
-
if (typeof top === "string" && top) return top;
|
|
812
|
-
for (const project of config?.projects ?? []) {
|
|
813
|
-
const url = project?.use?.baseURL;
|
|
814
|
-
if (typeof url === "string" && url) return url;
|
|
815
|
-
}
|
|
816
|
-
return null;
|
|
817
|
-
}
|
|
818
825
|
var INSTRUMENTATION_FETCH_TIMEOUT_MS = 5e3;
|
|
826
|
+
var MAX_MANIFEST_BYTES = 4 * 1024 * 1024;
|
|
819
827
|
function instrumentationManifestUrls(baseUrl) {
|
|
820
828
|
const base = new URL(baseUrl);
|
|
821
829
|
const root = new URL(INSTRUMENTATION_MANIFEST_PATH, base).toString();
|
|
822
|
-
const
|
|
823
|
-
return
|
|
830
|
+
const path8 = base.pathname.replace(/\/+$/, "");
|
|
831
|
+
return path8 ? [new URL(`${path8}${INSTRUMENTATION_MANIFEST_PATH}`, base).toString(), root] : [root];
|
|
824
832
|
}
|
|
825
833
|
async function fetchInstrumentationManifest(baseUrl, fetchImpl = fetch) {
|
|
826
834
|
try {
|
|
827
835
|
const probe = await fetchImpl(baseUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
|
|
828
836
|
if (!hasInstrumentationHeader(probe.headers)) return null;
|
|
829
837
|
for (const manifestUrl of instrumentationManifestUrls(baseUrl)) {
|
|
830
|
-
const
|
|
831
|
-
const manifest = res.ok ? parseManifestJson(await res.text()) : null;
|
|
838
|
+
const manifest = await fetchManifestAt(manifestUrl, fetchImpl);
|
|
832
839
|
if (manifest) return manifest;
|
|
833
840
|
}
|
|
834
841
|
return null;
|
|
@@ -836,18 +843,55 @@ async function fetchInstrumentationManifest(baseUrl, fetchImpl = fetch) {
|
|
|
836
843
|
return null;
|
|
837
844
|
}
|
|
838
845
|
}
|
|
846
|
+
async function fetchManifestAt(manifestUrl, fetchImpl) {
|
|
847
|
+
const res = await fetchImpl(manifestUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
|
|
848
|
+
return res.ok ? parseManifestJson(await res.text()) : null;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// src/internal/manifest/run-manifests.ts
|
|
852
|
+
async function lookupProjectId(httpClient, auth, projectName) {
|
|
853
|
+
const menu = await httpClient.getJSON("/api/projects/menu", auth);
|
|
854
|
+
return menu?.items?.find(
|
|
855
|
+
(p) => p.name.toLowerCase() === projectName.toLowerCase()
|
|
856
|
+
)?.id;
|
|
857
|
+
}
|
|
858
|
+
async function uploadManifest(httpClient, auth, projectId, upload, logger) {
|
|
859
|
+
await httpClient.putJSON(`/api/projects/${projectId}/surface/manifest`, upload, auth).catch((e) => logger.debug(`Manifest upload (${upload.source}) skipped: ${errorMessage(e)}`));
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// src/internal/collect/source-checksums.ts
|
|
863
|
+
var crypto2 = __toESM(require("crypto"));
|
|
864
|
+
var fs5 = __toESM(require("fs"));
|
|
865
|
+
var path6 = __toESM(require("path"));
|
|
866
|
+
|
|
867
|
+
// src/internal/collect/server-coverage.ts
|
|
868
|
+
var DEFAULT_COLLECTOR_LIMITS = {
|
|
869
|
+
batchTests: 25,
|
|
870
|
+
batchDelayMs: 1e3,
|
|
871
|
+
callTimeoutMs: 1e4,
|
|
872
|
+
runTimeoutMs: 3e4,
|
|
873
|
+
maxIdsPerCall: 1e4,
|
|
874
|
+
maxBodyBytes: 1024 * 1024 - 4096,
|
|
875
|
+
maxIdsPerTest: 5e4,
|
|
876
|
+
concurrency: 2
|
|
877
|
+
};
|
|
878
|
+
var MAX_ANSWER_BYTES = 64 * 1024 * 1024;
|
|
879
|
+
function baseUrlsFromConfig(config) {
|
|
880
|
+
const urls = [config?.use?.baseURL, ...(config?.projects ?? []).map((project) => project?.use?.baseURL)];
|
|
881
|
+
return [...new Set(urls.filter((url) => typeof url === "string" && url !== ""))];
|
|
882
|
+
}
|
|
839
883
|
|
|
840
884
|
// src/public/global-setup.ts
|
|
841
885
|
function createGlobalSetup(options, userSetup) {
|
|
842
886
|
return async function globalSetupFn(config) {
|
|
843
|
-
const piwiReporterPath =
|
|
887
|
+
const piwiReporterPath = path7.resolve(__dirname, "./index.js");
|
|
844
888
|
let inlineReporterOptions = {};
|
|
845
889
|
if (Array.isArray(config?.reporter)) {
|
|
846
890
|
for (const r of config.reporter) {
|
|
847
891
|
if (!Array.isArray(r) || typeof r[0] !== "string") continue;
|
|
848
892
|
const isPiwi = r[0].toLowerCase().includes("piwi") || (() => {
|
|
849
893
|
try {
|
|
850
|
-
return
|
|
894
|
+
return path7.resolve(require.resolve(r[0])) === piwiReporterPath;
|
|
851
895
|
} catch {
|
|
852
896
|
return false;
|
|
853
897
|
}
|
|
@@ -883,7 +927,7 @@ function createGlobalSetup(options, userSetup) {
|
|
|
883
927
|
if (!Array.isArray(r) || typeof r[0] !== "string") return false;
|
|
884
928
|
if (r[0].toLowerCase().includes("piwi")) return true;
|
|
885
929
|
try {
|
|
886
|
-
return
|
|
930
|
+
return path7.resolve(require.resolve(r[0])) === piwiReporterPath;
|
|
887
931
|
} catch {
|
|
888
932
|
return false;
|
|
889
933
|
}
|
|
@@ -913,7 +957,7 @@ function createGlobalSetup(options, userSetup) {
|
|
|
913
957
|
auth
|
|
914
958
|
);
|
|
915
959
|
if (response?.runId && response?.setupToken) {
|
|
916
|
-
|
|
960
|
+
fs6.writeFileSync(
|
|
917
961
|
getSetupFilePath(opts.projectName),
|
|
918
962
|
JSON.stringify({
|
|
919
963
|
runId: response.runId,
|
|
@@ -926,14 +970,11 @@ function createGlobalSetup(options, userSetup) {
|
|
|
926
970
|
if (opts.projectName) clearAriaSampleFile(opts.projectName);
|
|
927
971
|
const wantsAria = opts.sampleAriaOnPass !== false && !!opts.projectName;
|
|
928
972
|
const committedManifest = opts.uploadManifest !== false ? readCommittedManifest(configDirFromConfig(config)) : null;
|
|
929
|
-
const
|
|
930
|
-
const wantsManifest = opts.uploadManifest !== false && !!opts.projectName && (!!committedManifest ||
|
|
973
|
+
const manifestBaseUrls = opts.uploadManifest !== false ? baseUrlsFromConfig(config) : [];
|
|
974
|
+
const wantsManifest = opts.uploadManifest !== false && !!opts.projectName && (!!committedManifest || manifestBaseUrls.length > 0);
|
|
931
975
|
let projectId;
|
|
932
976
|
if ((wantsAria || wantsManifest) && opts.projectName) {
|
|
933
|
-
|
|
934
|
-
projectId = menu?.items?.find(
|
|
935
|
-
(p) => p.name.toLowerCase() === opts.projectName.toLowerCase()
|
|
936
|
-
)?.id;
|
|
977
|
+
projectId = await lookupProjectId(httpClient, auth, opts.projectName);
|
|
937
978
|
}
|
|
938
979
|
if (wantsAria && projectId != null) {
|
|
939
980
|
const sampling = await httpClient.getJSON(`/api/projects/${projectId}/aria-sampling`, auth);
|
|
@@ -947,20 +988,18 @@ function createGlobalSetup(options, userSetup) {
|
|
|
947
988
|
if (wantsManifest && projectId != null) {
|
|
948
989
|
const branch = resolveScmBranch(process.env) ?? null;
|
|
949
990
|
if (committedManifest) {
|
|
950
|
-
await
|
|
951
|
-
|
|
991
|
+
await uploadManifest(
|
|
992
|
+
httpClient,
|
|
993
|
+
auth,
|
|
994
|
+
projectId,
|
|
952
995
|
{ source: "committed", branch, manifest: committedManifest },
|
|
953
|
-
|
|
954
|
-
)
|
|
996
|
+
logger
|
|
997
|
+
);
|
|
955
998
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
if (
|
|
959
|
-
await httpClient
|
|
960
|
-
`/api/projects/${projectId}/surface/manifest`,
|
|
961
|
-
{ source: "instrumentation", branch, manifest: declared },
|
|
962
|
-
auth
|
|
963
|
-
).catch((e) => logger.debug(`Manifest upload (instrumentation) skipped: ${errorMessage(e)}`));
|
|
999
|
+
const declared = await Promise.all(manifestBaseUrls.map((url) => fetchInstrumentationManifest(url)));
|
|
1000
|
+
for (const manifest of declared) {
|
|
1001
|
+
if (manifest) {
|
|
1002
|
+
await uploadManifest(httpClient, auth, projectId, { source: "instrumentation", branch, manifest }, logger);
|
|
964
1003
|
}
|
|
965
1004
|
}
|
|
966
1005
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -90,8 +90,14 @@ interface PiwiDashboardOptions {
|
|
|
90
90
|
* repository root, which is always tried last. Defaults to the Playwright
|
|
91
91
|
* config's directory. Set it to the dev server's root when that is not the
|
|
92
92
|
* config's directory (Nuxt: `['app']`).
|
|
93
|
+
*
|
|
94
|
+
* An object gives directories per origin instead, for an application whose
|
|
95
|
+
* tests reach several dev servers that serve modules at the same paths (one per
|
|
96
|
+
* portal): a script resolves against the directories of its own origin, else
|
|
97
|
+
* of the origin of the page that loaded it, then those under `'*'`
|
|
98
|
+
* (`{ 'http://localhost:3001': 'portals/admin', '*': ['app'] }`).
|
|
93
99
|
*/
|
|
94
|
-
codeReachRoots?: string[]
|
|
100
|
+
codeReachRoots?: string[] | Record<string, string | string[]>;
|
|
95
101
|
/**
|
|
96
102
|
* Record, per test, which source code ran, in the browser and in the
|
|
97
103
|
* backend, and send it with the test as its coverage. Rides the capture
|
|
@@ -102,10 +108,13 @@ interface PiwiDashboardOptions {
|
|
|
102
108
|
/**
|
|
103
109
|
* What the browser side records: `'files'`, the application files whose
|
|
104
110
|
* functions ran, from Chromium's JavaScript coverage, which is what
|
|
105
|
-
* `captureCodeReach: true` records; `'
|
|
106
|
-
*
|
|
111
|
+
* `captureCodeReach: true` records; `'functions'` adds the start and end
|
|
112
|
+
* line of each function that ran, read through the dev server's modules or
|
|
113
|
+
* the bundle's source maps; `'off'` records nothing. Paths, and line numbers
|
|
114
|
+
* with `'functions'`, are sent, never source code or hit counts. Defaults to
|
|
115
|
+
* `'off'`. Can also be set with `PIWI_COVERAGE_BROWSER`.
|
|
107
116
|
*/
|
|
108
|
-
browser?: 'off' | 'files';
|
|
117
|
+
browser?: 'off' | 'files' | 'functions';
|
|
109
118
|
/**
|
|
110
119
|
* What the server side records, from a backend built with Piwi's coverage
|
|
111
120
|
* instrumentation (ASP.NET Core: `-p:PiwiCoverage=true`), which names each
|
|
@@ -170,10 +179,11 @@ interface PiwiDashboardOptions {
|
|
|
170
179
|
*/
|
|
171
180
|
sampleAriaOnPass?: boolean;
|
|
172
181
|
/**
|
|
173
|
-
* Upload the application's declared surface at run start
|
|
174
|
-
* `piwi.manifest.json` next to the Playwright config
|
|
175
|
-
*
|
|
176
|
-
*
|
|
182
|
+
* Upload the application's declared surface: at run start, a committed
|
|
183
|
+
* `piwi.manifest.json` next to the Playwright config and the instrumentation
|
|
184
|
+
* package's `/__piwi/manifest` of each `baseURL` whose first response carries an
|
|
185
|
+
* instrumentation header; as the run's last test ends, the manifest of every
|
|
186
|
+
* other origin whose responses carried one. A route or page the manifest declares that no
|
|
177
187
|
* test reaches becomes a "declared, never hit" gap. Defaults to `true`; set to
|
|
178
188
|
* `false` (or `PIWI_UPLOAD_MANIFEST=false`) to never upload.
|
|
179
189
|
*/
|
|
@@ -441,6 +451,8 @@ declare class PiwiDashboardReporter {
|
|
|
441
451
|
private reportedTestIds;
|
|
442
452
|
/** Per-test attempt history, keyed by `test.id`; snapshotted onto every attempt's payload. */
|
|
443
453
|
private attemptsByTest;
|
|
454
|
+
/** The folder test files are named relative to: the Playwright config's, set in `onBegin`. */
|
|
455
|
+
private testPathBase;
|
|
444
456
|
/** Derived in `onBegin`, once the shard is known. */
|
|
445
457
|
private instanceId;
|
|
446
458
|
private shardInfo;
|
|
@@ -476,8 +488,10 @@ declare class PiwiDashboardReporter {
|
|
|
476
488
|
private readonly coverageSettings;
|
|
477
489
|
/** Each test's coverage, when browser or server coverage is on; set in `onBegin`. */
|
|
478
490
|
private coverage;
|
|
479
|
-
/** Tells when the run's last test ended, while server coverage
|
|
491
|
+
/** Tells when the run's last test ended, while server coverage needs it. */
|
|
480
492
|
private lastTestWatch;
|
|
493
|
+
/** The manifests of the instrumented origins the run reaches beyond its base URLs. */
|
|
494
|
+
private manifests;
|
|
481
495
|
/** Result events that stream once their test's server coverage is in. */
|
|
482
496
|
private coverageSends;
|
|
483
497
|
private httpClient;
|
|
@@ -492,6 +506,13 @@ declare class PiwiDashboardReporter {
|
|
|
492
506
|
static wrapConfig: typeof wrapConfig;
|
|
493
507
|
static createGlobalSetup: typeof createGlobalSetup;
|
|
494
508
|
constructor(rawOptions?: Record<string, any>);
|
|
509
|
+
/**
|
|
510
|
+
* Playwright reporter hook: whether this reporter writes the run's progress to
|
|
511
|
+
* the terminal. It does not (it prints only its own `[Piwi Dashboard]` lines),
|
|
512
|
+
* so Playwright keeps its own terminal reporter when Piwi is the only one
|
|
513
|
+
* configured.
|
|
514
|
+
*/
|
|
515
|
+
printsToStdio(): boolean;
|
|
495
516
|
/** Playwright reporter hook: called once at the start of the test run */
|
|
496
517
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
497
518
|
/** Playwright reporter hook: called when an individual test begins */
|