@pixeyedev/playwright 0.4.4 → 0.4.5

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.
@@ -1,10 +1,10 @@
1
- "use strict";var __pixeye=(()=>{var c=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var g=(e,t)=>{for(var o in t)c(e,o,{get:t[o],enumerable:!0})},b=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of h(t))!f.call(e,i)&&i!==o&&c(e,i,{get:()=>t[i],enumerable:!(n=p(t,i))||n.enumerable});return e};var x=e=>b(c({},"__esModule",{value:!0}),e);var A={};g(A,{scanRegions:()=>l,stabilize:()=>m,waitForReady:()=>u});var S=[{selector:"[data-pixeye-mask]",type:"mask"},{selector:"[data-pixeye-ignore]",type:"ignore"},{selector:"[data-pixeye-dynamic]",type:"dynamic"},{selector:'[data-visual-test="blackout"]',type:"mask"},{selector:'[data-visual-test="removed"]',type:"ignore"},{selector:'[data-visual-test="transparent"]',type:"mask"},{selector:"[data-percy-ignore]",type:"ignore"},{selector:"[data-percy-mask]",type:"mask"},{selector:'[data-chromatic="ignore"]',type:"ignore"}],l=()=>{let e=[],t=new Set;for(let{selector:o,type:n}of S){let i=document.querySelectorAll(o);for(let a of i){if(t.has(a))continue;t.add(a);let r=a.getBoundingClientRect();r.width===0||r.height===0||e.push({type:n,selector:o,bounds:{x:Math.max(0,Math.round(r.x)),y:Math.max(0,Math.round(r.y)),width:Math.round(r.width),height:Math.round(r.height)}})}}return e};var d="__pixeye_stabilize",s=e=>{let t=document.getElementById(d);t||(t=document.createElement("style"),t.id=d,document.head.appendChild(t)),t.textContent+=e+`
1
+ "use strict";var __pixeye=(()=>{var c=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var g=(e,t)=>{for(var o in t)c(e,o,{get:t[o],enumerable:!0})},b=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of f(t))!h.call(e,i)&&i!==o&&c(e,i,{get:()=>t[i],enumerable:!(n=p(t,i))||n.enumerable});return e};var S=e=>b(c({},"__esModule",{value:!0}),e);var T={};g(T,{scanRegions:()=>l,stabilize:()=>m,waitForReady:()=>y});var x=[{selector:"[data-pixeye-mask]",type:"mask"},{selector:"[data-pixeye-ignore]",type:"ignore"},{selector:"[data-pixeye-dynamic]",type:"dynamic"},{selector:'[data-visual-test="blackout"]',type:"mask"},{selector:'[data-visual-test="removed"]',type:"ignore"},{selector:'[data-visual-test="transparent"]',type:"mask"},{selector:"[data-percy-ignore]",type:"ignore"},{selector:"[data-percy-mask]",type:"mask"},{selector:'[data-chromatic="ignore"]',type:"ignore"}],l=()=>{let e=[],t=new Set;for(let{selector:o,type:n}of x){let i=document.querySelectorAll(o);for(let a of i){if(t.has(a))continue;t.add(a);let r=a.getBoundingClientRect();r.width===0||r.height===0||e.push({type:n,selector:o,bounds:{x:Math.max(0,Math.round(r.x)),y:Math.max(0,Math.round(r.y)),width:Math.round(r.width),height:Math.round(r.height)}})}}return e};var d="__pixeye_stabilize",s=e=>{let t=document.getElementById(d);t||(t=document.createElement("style"),t.id=d,document.head.appendChild(t)),t.textContent+=e+`
2
2
  `},w=()=>{s(`
3
3
  * {
4
4
  -webkit-font-smoothing: antialiased !important;
5
5
  -moz-osx-font-smoothing: grayscale !important;
6
6
  }
7
- `)},E=()=>{s(`
7
+ `)},E=()=>{if(s(`
8
8
  *, *::before, *::after {
9
9
  animation-duration: 0s !important;
10
10
  animation-delay: 0s !important;
@@ -12,9 +12,9 @@
12
12
  transition-delay: 0s !important;
13
13
  scroll-behavior: auto !important;
14
14
  }
15
- `)},R=()=>{s(`
15
+ `),typeof document.getAnimations=="function")for(let e of document.getAnimations())try{e.finish()}catch{e.cancel()}for(let e of document.querySelectorAll("svg")){let t=e;if(typeof t.pauseAnimations=="function")try{t.pauseAnimations(),t.setCurrentTime(0)}catch{}}},A=()=>{s(`
16
16
  * { caret-color: transparent !important; }
17
17
  ::-webkit-scrollbar { display: none !important; }
18
18
  * { scrollbar-width: none !important; }
19
- `),document.querySelectorAll("[contenteditable], textarea, input").forEach(e=>{e.setAttribute("spellcheck","false")})},v=()=>{document.querySelectorAll("img").forEach(e=>{let t=e.getBoundingClientRect(),o=Math.round(t.width),n=Math.round(t.height);(o!==t.width||n!==t.height)&&(e.style.width=`${o}px`,e.style.height=`${n}px`)})},T=()=>{document.querySelectorAll("*").forEach(e=>{let t=getComputedStyle(e);(t.position==="sticky"||t.position==="fixed")&&(e.style.position="absolute")})},m=(e={})=>{e.colorScheme&&(document.documentElement.style.colorScheme=e.colorScheme),e.fontAntialiasing!==!1&&w(),e.disableAnimations!==!1&&E(),e.hideNonDeterministic!==!1&&R(),e.roundImageSizes!==!1&&v(),e.stabilizeStickyFixed!==!1&&T(),e.css&&s(e.css)};var y=async(e,t,o)=>{let n=Date.now();for(;!e();){if(Date.now()-n>o)throw new Error(`[pixeye] waitForReady timed out waiting for: ${t}`);await new Promise(i=>setTimeout(i,100))}},u=async(e={})=>{let t=e.timeout??1e4,o=async()=>{await document.fonts.ready;let i=Array.from(document.querySelectorAll("img"));await Promise.all(i.filter(a=>!a.complete).map(a=>new Promise(r=>{a.addEventListener("load",()=>r(),{once:!0}),a.addEventListener("error",()=>r(),{once:!0})}))),await y(()=>!document.querySelector('[aria-busy="true"]'),'aria-busy="true" elements to clear',t),await y(()=>!document.querySelector("[data-pixeye-loading]"),"data-pixeye-loading elements to disappear",t)},n=new Promise((i,a)=>{setTimeout(()=>a(new Error(`[pixeye] waitForReady timed out after ${t}ms`)),t)});await Promise.race([o(),n])};return x(A);})();
19
+ `),document.querySelectorAll("[contenteditable], textarea, input").forEach(e=>{e.setAttribute("spellcheck","false")})},v=()=>{document.querySelectorAll("img").forEach(e=>{let t=e.getBoundingClientRect(),o=Math.round(t.width),n=Math.round(t.height);(o!==t.width||n!==t.height)&&(e.style.width=`${o}px`,e.style.height=`${n}px`)})},R=()=>{document.querySelectorAll("*").forEach(e=>{let t=getComputedStyle(e);(t.position==="sticky"||t.position==="fixed")&&(e.style.position="absolute")})},m=(e={})=>{e.colorScheme&&(document.documentElement.style.colorScheme=e.colorScheme),e.fontAntialiasing!==!1&&w(),e.disableAnimations!==!1&&E(),e.hideNonDeterministic!==!1&&A(),e.roundImageSizes!==!1&&v(),e.stabilizeStickyFixed!==!1&&R(),e.css&&s(e.css)};var u=async(e,t,o)=>{let n=Date.now();for(;!e();){if(Date.now()-n>o)throw new Error(`[pixeye] waitForReady timed out waiting for: ${t}`);await new Promise(i=>setTimeout(i,100))}},y=async(e={})=>{let t=e.timeout??1e4,o=async()=>{await document.fonts.ready;let i=Array.from(document.querySelectorAll("img"));await Promise.all(i.filter(a=>!a.complete).map(a=>new Promise(r=>{a.addEventListener("load",()=>r(),{once:!0}),a.addEventListener("error",()=>r(),{once:!0})}))),await u(()=>!document.querySelector('[aria-busy="true"]'),'aria-busy="true" elements to clear',t),await u(()=>!document.querySelector("[data-pixeye-loading]"),"data-pixeye-loading elements to disappear",t)},n=new Promise((i,a)=>{setTimeout(()=>a(new Error(`[pixeye] waitForReady timed out after ${t}ms`)),t)});await Promise.race([o(),n])};return S(T);})();
20
20
  if(typeof window!=="undefined")window.__pixeye=__pixeye;
@@ -3,6 +3,14 @@ interface ReporterOptions {
3
3
  token?: string;
4
4
  server?: string;
5
5
  uploadConcurrency?: number;
6
+ /**
7
+ * Fail the test run (non-zero exit) when the upload is rejected for a
8
+ * CUSTOMER misconfiguration (bad token, lapsed plan/quota, permissions) so a
9
+ * run can't pass green with zero screenshots uploaded. Default `true`. Our
10
+ * transient faults (5xx/network) never fail the run regardless. Opt out with
11
+ * `failOnError: false` or `PIXEYE_FAIL_ON_ERROR=false`.
12
+ */
13
+ failOnError?: boolean;
6
14
  }
7
15
  declare const detectShard: (config: FullConfig) => {
8
16
  index: number;
@@ -12,10 +20,13 @@ declare class PixeyeReporter implements Reporter {
12
20
  private options;
13
21
  private shard;
14
22
  private skipUpload;
23
+ private failOnError;
15
24
  constructor(options?: ReporterOptions);
16
25
  onBegin(config: FullConfig, _suite: Suite): void;
17
26
  onTestEnd(_test: TestCase, _result: TestResult): void;
18
- onEnd(_result: FullResult): Promise<void>;
27
+ onEnd(_result: FullResult): Promise<{
28
+ status: FullResult['status'];
29
+ } | undefined>;
19
30
  }
20
31
  export default PixeyeReporter;
21
32
  export { detectShard };
package/dist/reporter.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
- import { detectCIEnvironment, discoverScreenshots, PixeyeClient, PixeyeError, runParallel } from '@pixeyedev/core';
3
+ import { detectCIEnvironment, discoverScreenshots, isUploadMisconfig, PixeyeClient, renderUploadFailureBanner, resolveFailOnError, runParallel, uploadErrorSummary } from '@pixeyedev/core';
4
4
  import { getContext } from './screenshot.js';
5
5
  const DEFAULT_UPLOAD_CONCURRENCY = 32;
6
6
  const detectShard = (config) => {
@@ -27,8 +27,10 @@ class PixeyeReporter {
27
27
  options;
28
28
  shard = { index: 0, total: 1 };
29
29
  skipUpload = false;
30
+ failOnError;
30
31
  constructor(options = {}) {
31
32
  this.options = options;
33
+ this.failOnError = resolveFailOnError(options.failOnError, process.env.PIXEYE_FAIL_ON_ERROR);
32
34
  }
33
35
  onBegin(config, _suite) {
34
36
  const token = this.options.token ?? process.env.PIXEYE_TOKEN;
@@ -150,12 +152,23 @@ class PixeyeReporter {
150
152
  console.log(`[pixeye] ${files.length} screenshots (${uploaded} new, ${presign.existing.length} cached) — ${server}/builds/${buildId}`);
151
153
  }
152
154
  catch (err) {
153
- // Upload failure should NOT fail the test run
154
- if (err instanceof PixeyeError) {
155
- console.warn(`[pixeye] Upload failed (${err.status}): ${err.message}`);
155
+ // P0-2: a CUSTOMER misconfiguration (bad token, lapsed plan/quota,
156
+ // permissions) must NOT pass silently — surface a loud banner and fail
157
+ // the run by default so a green CI can't hide zero uploaded screenshots.
158
+ // Our transient faults (5xx/network/R2) only warn — never break their CI.
159
+ if (isUploadMisconfig(err)) {
160
+ console.error(renderUploadFailureBanner(err, { willFail: this.failOnError }));
161
+ if (this.failOnError) {
162
+ // Playwright IGNORES `process.exitCode` — its runner calls
163
+ // `process.exit(status === 'passed' ? 0 : 1)`, overriding it. The ONLY
164
+ // reporter-side lever is returning a status from onEnd, which the
165
+ // multiplexer applies (`result.status = outResult.status`). Return
166
+ // 'failed' so a passing run with a rejected upload still exits non-zero.
167
+ return { status: 'failed' };
168
+ }
156
169
  }
157
170
  else {
158
- console.warn(`[pixeye] Upload failed: ${err.message}`);
171
+ console.warn(`[pixeye] Upload failed (transient — CI not failed): ${uploadErrorSummary(err)}`);
159
172
  }
160
173
  }
161
174
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixeyedev/playwright",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "files": [