@piwitests/reporter 0.47.0 → 0.48.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 CHANGED
@@ -490,7 +490,7 @@ var DEFAULTS = {
490
490
  defaultCapture: true,
491
491
  checkBaseUrl: false,
492
492
  streaming: true,
493
- streamingBatchSize: 5,
493
+ streamingBatchSize: 20,
494
494
  streamingBatchDelay: 2e3,
495
495
  maxStreamBufferBytes: 100 * 1024 * 1024,
496
496
  submitTimeout: 15 * 60 * 1e3,
@@ -90,7 +90,7 @@ var DEFAULTS = {
90
90
  defaultCapture: true,
91
91
  checkBaseUrl: false,
92
92
  streaming: true,
93
- streamingBatchSize: 5,
93
+ streamingBatchSize: 20,
94
94
  streamingBatchDelay: 2e3,
95
95
  maxStreamBufferBytes: 100 * 1024 * 1024,
96
96
  submitTimeout: 15 * 60 * 1e3,
package/dist/index.d.ts CHANGED
@@ -205,7 +205,11 @@ interface PiwiDashboardOptions {
205
205
  pickLocatorOnFailure?: boolean;
206
206
  /** Enable live streaming of results (falls back to batch if unsupported). Defaults to `true`. */
207
207
  streaming?: boolean;
208
- /** Number of test results to batch before sending during streaming. Defaults to `5`. */
208
+ /**
209
+ * Number of queued events that triggers a send during streaming: test starts
210
+ * and results, and live steps while someone has the run open on the
211
+ * dashboard. Defaults to `20`.
212
+ */
209
213
  streamingBatchSize?: number;
210
214
  /** Max delay (ms) before flushing pending events during streaming. Defaults to `2000`. */
211
215
  streamingBatchDelay?: number;
package/dist/index.js CHANGED
@@ -91,7 +91,7 @@ var DEFAULTS = {
91
91
  defaultCapture: true,
92
92
  checkBaseUrl: false,
93
93
  streaming: true,
94
- streamingBatchSize: 5,
94
+ streamingBatchSize: 20,
95
95
  streamingBatchDelay: 2e3,
96
96
  maxStreamBufferBytes: 100 * 1024 * 1024,
97
97
  submitTimeout: 15 * 60 * 1e3,
@@ -1865,6 +1865,20 @@ var StreamManager = class {
1865
1865
  this.heartbeatStopped = false;
1866
1866
  this.lastActivityAt = 0;
1867
1867
  this.heartbeatInterval = 15e3;
1868
+ /**
1869
+ * The heartbeat interval while nobody watches the run: during a long test no
1870
+ * other request goes out, so the heartbeat's answer is how the reporter learns
1871
+ * that someone opened the run.
1872
+ */
1873
+ this.unwatchedHeartbeatInterval = 5e3;
1874
+ /**
1875
+ * Whether anyone has the run open live on the dashboard, from the `watched`
1876
+ * flag of the last answer to an events or heartbeat request; `null` until the
1877
+ * dashboard says, and for a dashboard that does not. Step events are live
1878
+ * progress only (each `complete` event carries its test's steps), so none are
1879
+ * queued while it is `false`.
1880
+ */
1881
+ this.watched = null;
1868
1882
  /** Tracks cases whose files have already been uploaded live, so `uploadRemaining` can skip them. */
1869
1883
  this.uploadedCaseFiles = /* @__PURE__ */ new WeakSet();
1870
1884
  /** Set when a test-result event left the live stream undelivered: too large to send, or still queued when the drain gave up. */
@@ -2035,8 +2049,13 @@ var StreamManager = class {
2035
2049
  this.pendingBeginEvents.enqueue(event);
2036
2050
  }
2037
2051
  }
2038
- /** Queue a test-case event. Triggers an immediate flush when the batch size is reached, otherwise schedules a timer-based flush. */
2052
+ /**
2053
+ * Queue a test-case event. Triggers an immediate flush when the batch size is
2054
+ * reached, otherwise schedules a timer-based flush. A step event is dropped
2055
+ * while nobody watches the run.
2056
+ */
2039
2057
  queueEvent(event) {
2058
+ if (this.watched === false && (event.type === "step-begin" || event.type === "step-end")) return;
2040
2059
  this.pendingEvents.enqueue(event);
2041
2060
  if (this.pendingEvents.length >= this.options.streamingBatchSize) {
2042
2061
  this.flush();
@@ -2064,11 +2083,12 @@ var StreamManager = class {
2064
2083
  while (chunks.length > 0) {
2065
2084
  const chunk = chunks.shift();
2066
2085
  try {
2067
- await this.httpClient.postJSON(
2086
+ const response = await this.httpClient.postJSON(
2068
2087
  `/api/test-runs/${this._runId}/events`,
2069
2088
  { streamToken: this._token, testCases: chunk },
2070
2089
  this._auth
2071
2090
  );
2091
+ this.noteWatched(response);
2072
2092
  this.retryCount = 0;
2073
2093
  this.lastActivityAt = Date.now();
2074
2094
  } catch (error) {
@@ -2142,13 +2162,29 @@ var StreamManager = class {
2142
2162
  if (this.pendingEvents.length > 0) this.flush();
2143
2163
  }, delay);
2144
2164
  }
2165
+ // Take the dashboard's answer on whether anyone watches the run. An answer
2166
+ // without the flag (an older dashboard) keeps step events flowing.
2167
+ noteWatched(response) {
2168
+ const watched = response?.watched;
2169
+ const next = typeof watched === "boolean" ? watched : null;
2170
+ if (next !== null && next !== this.watched) {
2171
+ this.logger.debug(
2172
+ next ? "The run is open on the dashboard: streaming live steps." : "Nobody is watching the run: live steps are not sent."
2173
+ );
2174
+ }
2175
+ this.watched = next;
2176
+ }
2177
+ // Idle time after which a heartbeat goes out.
2178
+ heartbeatDelay() {
2179
+ return this.watched === false ? this.unwatchedHeartbeatInterval : this.heartbeatInterval;
2180
+ }
2145
2181
  // Schedule the next idle heartbeat. Self-rescheduling; cleared by stopHeartbeat.
2146
2182
  scheduleHeartbeat() {
2147
2183
  if (this.heartbeatStopped || this.heartbeatTimer) return;
2148
2184
  this.heartbeatTimer = setTimeout(() => {
2149
2185
  this.heartbeatTimer = null;
2150
2186
  void this.sendHeartbeat();
2151
- }, this.heartbeatInterval);
2187
+ }, this.heartbeatDelay());
2152
2188
  }
2153
2189
  // Ping the server only when the run has actually been idle. Real event traffic
2154
2190
  // already bumps the server's activity timestamp, so a recent flush (or pending
@@ -2156,16 +2192,17 @@ var StreamManager = class {
2156
2192
  async sendHeartbeat() {
2157
2193
  if (this.heartbeatStopped || !this._enabled || !this._runId || !this._token) return;
2158
2194
  const idleFor = Date.now() - this.lastActivityAt;
2159
- if (idleFor < this.heartbeatInterval || this.pendingEvents.length > 0) {
2195
+ if (idleFor < this.heartbeatDelay() || this.pendingEvents.length > 0) {
2160
2196
  this.scheduleHeartbeat();
2161
2197
  return;
2162
2198
  }
2163
2199
  try {
2164
- await this.httpClient.postJSON(
2200
+ const response = await this.httpClient.postJSON(
2165
2201
  `/api/test-runs/${this._runId}/heartbeat`,
2166
2202
  { streamToken: this._token },
2167
2203
  this._auth
2168
2204
  );
2205
+ this.noteWatched(response);
2169
2206
  this.lastActivityAt = Date.now();
2170
2207
  } catch (error) {
2171
2208
  this.logger.debug(`Heartbeat failed: ${errorMessage(error)}`);
@@ -2158,7 +2158,7 @@ var DEFAULTS = {
2158
2158
  defaultCapture: true,
2159
2159
  checkBaseUrl: false,
2160
2160
  streaming: true,
2161
- streamingBatchSize: 5,
2161
+ streamingBatchSize: 20,
2162
2162
  streamingBatchDelay: 2e3,
2163
2163
  maxStreamBufferBytes: 100 * 1024 * 1024,
2164
2164
  submitTimeout: 15 * 60 * 1e3,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piwitests/reporter",
3
- "version": "0.47.0",
3
+ "version": "0.48.0",
4
4
  "description": "Playwright reporter that streams results, traces and HTML reports to a Piwi Dashboard instance",
5
5
  "url": "https://github.com/PiwiTests/platform",
6
6
  "homepage": "https://piwitests.dev",