@piwitests/reporter 0.60.0 → 0.62.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.
@@ -202,6 +202,7 @@ var PIWI_ENV_KEYS = {
202
202
  codeReachRoots: "PIWI_CODE_REACH_ROOTS",
203
203
  coverageBrowser: "PIWI_COVERAGE_BROWSER",
204
204
  coverageServer: "PIWI_COVERAGE_SERVER",
205
+ coverageCss: "PIWI_COVERAGE_CSS",
205
206
  externalIdPattern: "PIWI_EXTERNAL_ID_PATTERN",
206
207
  externalIdOnDuplicate: "PIWI_EXTERNAL_ID_ON_DUPLICATE",
207
208
  captureServerTraces: "PIWI_CAPTURE_SERVER_TRACES",
@@ -321,10 +322,14 @@ function resolveOptions(raw, configDir) {
321
322
  const coverage = { ...mergedRaw.coverage };
322
323
  const coverageBrowser = env[PIWI_ENV_KEYS.coverageBrowser];
323
324
  const coverageServer = env[PIWI_ENV_KEYS.coverageServer];
325
+ const coverageCss = env[PIWI_ENV_KEYS.coverageCss];
324
326
  if (coverage.browser === void 0 && coverageBrowser)
325
327
  coverage.browser = coverageBrowser;
326
328
  if (coverage.server === void 0 && coverageServer) coverage.server = coverageServer;
327
- if (coverage.browser !== void 0 || coverage.server !== void 0) mergedRaw.coverage = coverage;
329
+ if (coverage.css === void 0 && coverageCss) coverage.css = coverageCss;
330
+ if (coverage.browser !== void 0 || coverage.server !== void 0 || coverage.css !== void 0) {
331
+ mergedRaw.coverage = coverage;
332
+ }
328
333
  const externalId = { ...mergedRaw.externalId };
329
334
  const externalIdPattern = env[PIWI_ENV_KEYS.externalIdPattern];
330
335
  const externalIdOnDuplicate = env[PIWI_ENV_KEYS.externalIdOnDuplicate];
@@ -736,6 +741,7 @@ function isListMode(argv = process.argv) {
736
741
  }
737
742
 
738
743
  // src/internal/support/base-url-check.ts
744
+ var net = __toESM(require("net"));
739
745
  var BASE_URL_CHECK_ATTEMPTS = 3;
740
746
  var BASE_URL_CHECK_TIMEOUT_MS = 1e4;
741
747
  var BASE_URL_CHECK_DELAY_MS = 2e3;
@@ -757,9 +763,9 @@ function cliProjectNames(argv = process.argv) {
757
763
  }
758
764
  return names;
759
765
  }
760
- function wildcard(pattern) {
766
+ function wildcard(pattern, flags = "i") {
761
767
  const escaped = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
762
- return new RegExp(`^${escaped.join(".*")}$`, "i");
768
+ return new RegExp(`^${escaped.join(".*")}$`, flags);
763
769
  }
764
770
  function selectedProjects(projects, argv = process.argv, filtered) {
765
771
  let chosen;
@@ -769,7 +775,7 @@ function selectedProjects(projects, argv = process.argv, filtered) {
769
775
  } else {
770
776
  const names = cliProjectNames(argv);
771
777
  if (!names || names.length === 0) return projects;
772
- const patterns = names.map(wildcard);
778
+ const patterns = names.map((name) => wildcard(name));
773
779
  chosen = new Set(projects.filter((p) => patterns.some((re) => re.test(p.name ?? ""))));
774
780
  }
775
781
  if (!argv.includes("--no-deps")) {
@@ -791,23 +797,58 @@ function asProxy(value) {
791
797
  const proxy = value;
792
798
  return proxy && typeof proxy === "object" && typeof proxy.server === "string" ? proxy : void 0;
793
799
  }
794
- function baseUrlTargets(config, argv = process.argv) {
800
+ function exposeNetworkCovers(rule, url) {
801
+ if (!rule) return false;
802
+ let parsed;
803
+ try {
804
+ parsed = new URL(url);
805
+ } catch {
806
+ return false;
807
+ }
808
+ const host = parsed.hostname;
809
+ const port = parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80;
810
+ return rule.split(",").some((token) => {
811
+ const match = token.match(/^(.*?)(?::(\d+))?$/);
812
+ if (!match) return false;
813
+ if (match[2] !== void 0 && Number(match[2]) !== port) return false;
814
+ const tokenHost = match[1];
815
+ if (tokenHost === "<loopback>") {
816
+ return host === "localhost" || host.endsWith(".localhost") || host === "127.0.0.1" || host === "[::1]";
817
+ }
818
+ if (tokenHost === "*") return true;
819
+ if (net.isIP(tokenHost)) return host === tokenHost;
820
+ if (net.isIP(host)) return false;
821
+ return wildcard(tokenHost.startsWith(".") ? `*${tokenHost}` : tokenHost, "").test(host);
822
+ });
823
+ }
824
+ function reachedRemotely(use, url, env) {
825
+ if (use?.connectOptions === void 0) {
826
+ return !!env.PW_TEST_CONNECT_WS_ENDPOINT && !exposeNetworkCovers(env.PW_TEST_CONNECT_EXPOSE_NETWORK, url);
827
+ }
828
+ const connect = use.connectOptions;
829
+ if (!connect || typeof connect !== "object" || typeof connect.wsEndpoint !== "string") return false;
830
+ return !exposeNetworkCovers(typeof connect.exposeNetwork === "string" ? connect.exposeNetwork : void 0, url);
831
+ }
832
+ function baseUrlTargets(config, argv = process.argv, env = process.env) {
795
833
  const projects = config.projects?.length ? config.projects : [{ name: "", use: config.use }];
796
834
  const filtered = config.projects?.length ? config.filteredProjects : void 0;
797
835
  const targets = /* @__PURE__ */ new Map();
798
836
  for (const project of selectedProjects(projects, argv, filtered)) {
799
837
  const url = project.use?.baseURL;
800
838
  if (typeof url !== "string" || !/^https?:\/\//i.test(url)) continue;
839
+ const remoteBrowser = reachedRemotely(project.use, url, env);
801
840
  const target = targets.get(url);
802
841
  if (target) {
803
842
  if (project.name) target.projects.push(project.name);
843
+ target.remoteBrowser ||= remoteBrowser;
804
844
  continue;
805
845
  }
806
846
  targets.set(url, {
807
847
  url,
808
848
  projects: project.name ? [project.name] : [],
809
849
  ignoreHTTPSErrors: project.use?.ignoreHTTPSErrors === true,
810
- proxy: asProxy(project.use?.proxy)
850
+ proxy: asProxy(project.use?.proxy),
851
+ remoteBrowser
811
852
  });
812
853
  }
813
854
  return [...targets.values()];
@@ -848,14 +889,21 @@ async function checkBaseUrl(target, options = {}) {
848
889
  }
849
890
  return `${reason} (${attempts} ${attempts === 1 ? "attempt" : "attempts"})`;
850
891
  }
892
+ function remoteBrowserNote(target) {
893
+ if (!target.remoteBrowser) return null;
894
+ const { host } = new URL(target.url);
895
+ return `The check ran from this machine's network, but a project's remote browser reaches ${host} from its own, since its exposeNetwork rule does not cover that address.`;
896
+ }
851
897
  async function checkBaseUrls(config, options = {}) {
852
- const targets = baseUrlTargets(config, options.argv);
898
+ const targets = baseUrlTargets(config, options.argv, options.env);
853
899
  const reasons = await Promise.all(targets.map((target) => checkBaseUrl(target, options)));
854
900
  const down = targets.flatMap((target, i) => reasons[i] ? [{ target, reason: reasons[i] }] : []);
855
901
  if (down.length === 0) return targets;
856
902
  const lines = down.map(({ target, reason }) => {
857
903
  const used = target.projects.length > 0 ? ` (${target.projects.join(", ")})` : "";
858
- return ` ${target.url}${used}: ${reason}`;
904
+ const note = remoteBrowserNote(target);
905
+ return ` ${target.url}${used}: ${reason}${note ? `
906
+ ${note}` : ""}`;
859
907
  });
860
908
  const message = [
861
909
  `[Piwi Dashboard] The app under test did not answer, so no test ran:`,
@@ -1008,7 +1056,16 @@ function createGlobalSetup(options, userSetup) {
1008
1056
  }
1009
1057
  if (opts.checkBaseUrl === true) {
1010
1058
  const checked = await checkBaseUrls(config ?? {});
1011
- for (const target of checked) logger.debug(`Base URL answered: ${target.url}`);
1059
+ for (const target of checked) {
1060
+ const note = remoteBrowserNote(target);
1061
+ if (note) {
1062
+ logger.info(
1063
+ `Base URL answered: ${target.url}. ${note} A navigation failure from that browser can mean its machine cannot reach the app, not that the app is down.`
1064
+ );
1065
+ } else {
1066
+ logger.debug(`Base URL answered: ${target.url}`);
1067
+ }
1068
+ }
1012
1069
  }
1013
1070
  if (opts.enabled === false || !opts.serverUrl) {
1014
1071
  logger.info("Not enabled \u2014 set PIWI_DASHBOARD_URL or serverUrl to enable.");
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export { PiwiFixtures, PiwiWorkerFixtures, extendPiwiFixtures, piwiFixtures } fr
5
5
  import '@piwitests/picker-dom';
6
6
  import '@piwitests/core/page-interaction';
7
7
  import '@piwitests/core/wire';
8
+ import '@piwitests/core/run-channel';
8
9
  import '@piwitests/core/code-reach';
9
10
 
10
11
  /**
@@ -140,6 +141,19 @@ interface PiwiDashboardOptions {
140
141
  * `PIWI_COVERAGE_SERVER`.
141
142
  */
142
143
  server?: 'off' | 'files' | 'functions' | 'lines';
144
+ /**
145
+ * What the browser's style sheets record: `'rules'`, the rules that
146
+ * matched an element during each test, from Chromium's CSS rule usage, each
147
+ * as its first and last line in the application's style sheet (a `.css`
148
+ * file served as written, a Vite dev server's styles, or a sheet with a
149
+ * source map), sent where a script's functions are; `'off'` records
150
+ * nothing. A change to a rule then selects the tests that rendered it. It
151
+ * costs style recalculation time, a little on most pages and up to half
152
+ * the test's time on a page with large style sheets and frequent
153
+ * re-renders, so it is a choice of its own. Chromium only. Defaults to
154
+ * `'off'`. Can also be set with `PIWI_COVERAGE_CSS`.
155
+ */
156
+ css?: 'off' | 'rules';
143
157
  };
144
158
  /**
145
159
  * Match each test to its history on the dashboard by an external ID, the ID the test has in your own test
@@ -256,8 +270,11 @@ interface PiwiDashboardOptions {
256
270
  * `globalSetup`, so leave it off when that setup is what starts the app. Only
257
271
  * the projects the run selects are checked, with their dependencies: the ones
258
272
  * `--project` names, or every project but those declared `default: false`
259
- * (Playwright 1.64+). **Defaults to `false`**. Can
260
- * also be set with `PIWI_CHECK_BASE_URL=true`.
273
+ * (Playwright 1.64+). The check runs from this machine's network: for a
274
+ * project whose browser runs on another machine (`connectOptions`) and whose
275
+ * `exposeNetwork` rule does not cover the base URL's host and port, its message says
276
+ * so, since that browser reaches the app from its own network. **Defaults to
277
+ * `false`**. Can also be set with `PIWI_CHECK_BASE_URL=true`.
261
278
  */
262
279
  checkBaseUrl?: boolean;
263
280
  /**
@@ -525,6 +542,8 @@ declare class PiwiDashboardReporter {
525
542
  private resourcesFile;
526
543
  /** Samples the run's processes and the machine from `onBegin` to `onEnd`. */
527
544
  private sampler;
545
+ /** Posts what the sampler and the censuses add while someone has the run open on the dashboard. */
546
+ private liveResources;
528
547
  /** Bytes of the files the tests attached, by kind. */
529
548
  private artifactBytes;
530
549
  /** The run's resource report for the dashboard, built in `onEnd`. */