@appetize/playwright
Advanced tools
Comparing version 1.1.4-beta.0 to 1.1.4-beta.1
@@ -1,3 +0,1 @@ | ||
export declare function uint8ArrayToString(uint8Arr: Uint8Array): string; | ||
export declare function uint8ArrayToBase64(uint8Arr: Uint8Array, mimeType: string): string; | ||
export declare function base64ToUint8Array(base64: any): Uint8Array; |
@@ -10,2 +10,3 @@ import { Session, SessionConfig, SessionEvents, UserSessionConfig } from '../session'; | ||
protected _config: SessionConfig | undefined; | ||
queue?: ClientEvents['queue']; | ||
constructor({ socket, logger, }: { | ||
@@ -30,2 +31,3 @@ socket: TSocket; | ||
}; | ||
queueEnd: void; | ||
error: { | ||
@@ -32,0 +34,0 @@ message: string; |
import { Client } from '../client'; | ||
export declare class MockClient extends Client<any, any, any> { | ||
constructor(); | ||
_mock: boolean; | ||
} |
@@ -1,5 +0,5 @@ | ||
var ne=(p,g,v)=>{if(!g.has(p))throw TypeError("Cannot "+v)};var _=(p,g,v)=>(ne(p,g,"read from private field"),v?v.call(p):g.get(p)),B=(p,g,v)=>{if(g.has(p))throw TypeError("Cannot add the same private member more than once");g instanceof WeakSet?g.add(p):g.set(p,v)},J=(p,g,v,L)=>(ne(p,g,"write to private field"),L?L.call(p,v):g.set(p,v),v);(function(p,g){typeof exports=="object"&&typeof module!="undefined"?g(exports,require("events"),require("@playwright/test"),require("fs")):typeof define=="function"&&define.amd?define(["exports","events","@playwright/test","fs"],g):(p=typeof globalThis!="undefined"?globalThis:p||self,g(p.playwright={},p.events,p.test$1,p.fs))})(this,function(p,g,v,L){var S,P;"use strict";function re(i){return i&&typeof i=="object"&&"default"in i?i:{default:i}}var $=re(L);async function oe(i,{retries:e=3,timeout:t=1e3,predicate:s=()=>!0}){for(let n=1;n<=e;n++)try{return await i()}catch(r){if(n===e||!s(r,n))throw r;await new Promise(o=>setTimeout(o,t))}throw null}function ae(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{const e=Math.random()*16|0;return(i==="x"?e:e&3|8).toString(16)})}function ce(i){return Object.entries(i).map(([e,t])=>`${e}=${encodeURIComponent(t)}`).join("&")}function he(){let i,e;return[new Promise((s,n)=>{i=s,e=n}),i,e]}function k(i){return Array.isArray(i)?i.map(k).filter(e=>e!=null):typeof i=="object"&&i!==null?Object.entries(i).reduce((e,[t,s])=>{const n=k(s);return n!=null&&(e[t]=n),e},{}):i}function Y(i,e){if("captureStackTrace"in Error)Error.captureStackTrace(i,e);else{const t=new Error;Object.defineProperty(i,"stack",{configurable:!0,get(){const{stack:s}=t;return Object.defineProperty(this,"stack",{value:s}),s}})}}async function d(i,e){i instanceof f&&Y(i,e)}class f extends Error{constructor(e){super(e),this.name="Error",this.isOperational=!0,Y(this,this.constructor)}}class ue extends f{constructor(e){super(`No element found for selector | ||
${JSON.stringify(e,null,2)}`)}}class le extends f{constructor(e){super(`Action requires 1 unique element but the selector returned ${e.length}. Provide a \`matchIndex\` to pick an element below or add additional attributes to your selector. | ||
var ne=(p,g,v)=>{if(!g.has(p))throw TypeError("Cannot "+v)};var _=(p,g,v)=>(ne(p,g,"read from private field"),v?v.call(p):g.get(p)),B=(p,g,v)=>{if(g.has(p))throw TypeError("Cannot add the same private member more than once");g instanceof WeakSet?g.add(p):g.set(p,v)},J=(p,g,v,L)=>(ne(p,g,"write to private field"),L?L.call(p,v):g.set(p,v),v);(function(p,g){typeof exports=="object"&&typeof module!="undefined"?g(exports,require("events"),require("@playwright/test"),require("fs")):typeof define=="function"&&define.amd?define(["exports","events","@playwright/test","fs"],g):(p=typeof globalThis!="undefined"?globalThis:p||self,g(p.playwright={},p.events,p.test$1,p.fs))})(this,function(p,g,v,L){var T,P;"use strict";function re(i){return i&&typeof i=="object"&&"default"in i?i:{default:i}}var $=re(L);async function oe(i,{retries:e=3,timeout:t=1e3,predicate:s=()=>!0}){for(let n=1;n<=e;n++)try{return await i()}catch(r){if(n===e||!s(r,n))throw r;await new Promise(o=>setTimeout(o,t))}throw null}function ae(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{const e=Math.random()*16|0;return(i==="x"?e:e&3|8).toString(16)})}function ce(i){return Object.entries(i).map(([e,t])=>`${e}=${encodeURIComponent(t)}`).join("&")}function he(){let i,e;return[new Promise((s,n)=>{i=s,e=n}),i,e]}function k(i){return Array.isArray(i)?i.map(k).filter(e=>e!=null):typeof i=="object"&&i!==null?Object.entries(i).reduce((e,[t,s])=>{const n=k(s);return n!=null&&(e[t]=n),e},{}):i}function Y(i,e){if("captureStackTrace"in Error)Error.captureStackTrace(i,e);else{const t=new Error;Object.defineProperty(i,"stack",{configurable:!0,get(){const{stack:s}=t;return Object.defineProperty(this,"stack",{value:s}),s}})}}async function d(i,e){i instanceof m&&Y(i,e)}class m extends Error{constructor(e){super(e),this.name="Error",this.isOperational=!0,Y(this,this.constructor)}}class ue extends m{constructor(e){super(`No element found for selector | ||
${JSON.stringify(e,null,2)}`)}}class le extends m{constructor(e){super(`Action requires 1 unique element but the selector returned ${e.length}. Provide a \`matchIndex\` to pick an element below or add additional attributes to your selector. | ||
${pe(e)}`)}}class de extends f{constructor(e){e.localPosition?super(`localPosition (${e.localPosition.x}, ${e.localPosition.y}) for the element evaluates to a coordinate outside of screen bounds.`):super("Element is outside of screen bounds.")}}class C extends f{}class M extends f{constructor(e){super(`App Recorder must be enabled to use ${e}. Please set "record" to true in the config.`)}}function pe(i){const t=i.slice(0,5),s=i.length>5;return`${t.map((r,o)=>`// ${o} | ||
${pe(e)}`)}}class de extends m{constructor(e){e.localPosition?super(`localPosition (${e.localPosition.x}, ${e.localPosition.y}) for the element evaluates to a coordinate outside of screen bounds.`):super("Element is outside of screen bounds.")}}class D extends m{}class M extends m{constructor(e){super(`App Recorder must be enabled to use ${e}. Please set "record" to true in the config.`)}}function pe(i){const t=i.slice(0,5),s=i.length>5;return`${t.map((r,o)=>`// ${o} | ||
${JSON.stringify(r,null,2)}`).join(` | ||
@@ -9,6 +9,6 @@ | ||
...and ${i.length-5} more`:""}`}async function H(i,e=5e3){const t=Date.now();let s=!1;for(;;)try{return await i(r=>{if(r)throw s=!0,r})}catch(n){if(await new Promise(r=>setTimeout(r,100)),s||e!==null&&Date.now()-t>e)throw n}}async function R(i){return new Promise(e=>setTimeout(e,i))}async function D(i,e,t){const s=typeof t=="function"?{}:t,n=typeof t=="function"?t:t==null?void 0:t.predicate,r=typeof(s==null?void 0:s.timeout)!="undefined"?s.timeout:1e4;return new Promise((o,a)=>{const c=h=>{(!n||n(h))&&(i.off(e,c),o(h))};i.on(e,c),r!==null&&setTimeout(()=>{i.off(e,c),a(new C(`Timeout ${r}ms exceeded while waiting for event "${e}"`))},r)})}class N extends g.EventEmitter{constructor(){super(),this.on("error",()=>{})}}function A(i){if(typeof i=="string"){if(i.endsWith("%"))return parseInt(i,10)/100;throw new Error(`Invalid position value: ${i}. Must be a number between 0 and 1, or a string ending with %`)}return i}class we{constructor({platform:e,screen:t,app:s}){this.platform=e,this.screen=t,this.app=s}pixelToDip(e){return e/(this.screen.devicePixelRatio||1)}dipToPixel(e){return e*(this.screen.devicePixelRatio||1)}getCoordinates(e,t){const s=A(e.x),n=A(e.y);return{x:s*t.width,y:n*t.height}}mapHardwareKey(e){switch(e){case"HOME":return"home";case"VOLUME_UP":return"volumeUp";case"VOLUME_DOWN":return"volumeDown"}return e}mapAction(e){return k((()=>{e=k(e);let s,n,r;if("element"in e&&e.element&&(s=this.mapElement(e.element)),"position"in e&&e.position){const o=A(e.position.x),a=A(e.position.y);if(!x.isValidNumber(o)||!x.isValidNumber(a))throw new f(`Invalid position: (${e.position.x}, ${e.position.y}). Values must be a number or a percentage`);if(!x.isPositionWithinBounds(e.position))throw typeof e.position.x=="string"?new Error(`Invalid position: (${e.position.x}, ${e.position.y}) must be within (0%, 0%) and (100%, 100%)`):new Error(`Invalid position: (${e.position.x}, ${e.position.y}) must be within (0, 0) and (1, 1)`);this.platform==="android"?n=this.getCoordinates(e.position,{width:this.dipToPixel(this.screen.width)-1,height:this.dipToPixel(this.screen.height)-1}):n=this.getCoordinates(e.position,{width:this.screen.width-1,height:this.screen.height-1})}else if("coordinates"in e&&e.coordinates){if(!x.isValidNumber(e.coordinates.x)||!x.isValidNumber(e.coordinates.y))throw new f(`Invalid coordinates: (${e.coordinates.x}, ${e.coordinates.y}). Values must be a number`);if(!x.isCoordinatesWithinBounds(e.coordinates,{width:this.screen.width-1,height:this.screen.height-1}))throw new f(`Invalid coordinates: (${e.coordinates.x}, ${e.coordinates.y}) exceed screen bounds (${this.screen.width-1}, ${this.screen.height-1})`);this.platform==="android"?n={x:this.dipToPixel(e.coordinates.x),y:this.dipToPixel(e.coordinates.y)}:n=e.coordinates}if("localPosition"in e&&e.localPosition){const o=A(e.localPosition.x),a=A(e.localPosition.y);if(!x.isValidNumber(o)||!x.isValidNumber(a))throw new f(`Invalid localPosition: (${e.localPosition.x}, ${e.localPosition.y}). Values must be a number or a percentage`);r={x:o,y:a}}else s&&(r={x:.5,y:.5});if("duration"in e&&e.duration&&!x.isValidNumber(e.duration))throw new f(`Invalid duration: ${e.duration}. Value must be a number`);switch(e.type){case"tap":{const{position:o,...a}=e;return{...a,element:s,localPosition:r,coordinates:n}}case"swipe":{const{position:o,...a}=e;return{...a,element:s,localPosition:r,coordinates:n,moves:e.moves.map(c=>{if(this.platform==="android"){const{x:h,y:u}=this.getCoordinates(c,{width:this.dipToPixel(this.screen.width)-1,height:this.dipToPixel(this.screen.height)-1});return{...c,x:h,y:u}}else{const{x:h,y:u}=this.getCoordinates(c,{width:this.screen.width-1,height:this.screen.height-1});return{...c,x:h,y:u}}})}}case"keypress":{const o=this.mapHardwareKey(e.key),a=this.mapHardwareKey(e.character);return{...e,key:o,character:a,shiftKey:this.platform==="ios"?ye(e.shiftKey):e.shiftKey}}case"findElements":return{...e,element:s}}return e})())}mapElement(e){const{attributes:t,bounds:s,...n}=e,r=()=>{if(s){const{x:a,y:c,width:h,height:u}=s;return this.platform==="android"?{x:this.dipToPixel(a),y:this.dipToPixel(c),width:this.dipToPixel(h),height:this.dipToPixel(u)}:{x:V(a),y:V(c),width:V(h),height:V(u)}}},o=()=>{if(t)return Object.keys(t).reduce((a,c)=>{if(this.platform==="ios")switch(c){case"userInteractionEnabled":case"isHidden":return{...a,[c]:t[c]?"1":"0"}}else this.platform;return{...a,[c]:t[c]}},{})};return k({...n,bounds:r(),attributes:o(),accessibilityElements:void 0})}}class fe{constructor({platform:e,screen:t,app:s}){this.platform=e,this.screen=t,this.app=s}pixelToDip(e){return e/(this.screen.devicePixelRatio||1)}dipToPixel(e){return e*(this.screen.devicePixelRatio||1)}getPosition(e,t){return{x:e.x/t.width,y:e.y/t.height}}mapHardwareKey(e){switch(e){case"home":return"HOME";case"volumeUp":return"VOLUME_UP";case"volumeDown":return"VOLUME_DOWN"}return e}mapAction(e){return k((()=>{let s,n,r,o="localPosition"in e?e.localPosition:void 0;switch("coordinates"in e&&e.coordinates&&(n={x:this.pixelToDip(e.coordinates.x),y:this.pixelToDip(e.coordinates.y)},r=this.getPosition(n,{width:this.screen.width-1,height:this.screen.height-1})),"element"in e&&e.element&&(s=this.mapElement(e.element),n&&s.bounds&&(o=this.getPosition({x:n.x-s.bounds.x,y:n.y-s.bounds.y},{width:s.bounds.width,height:s.bounds.height}))),e.type){case"tap":return{...e,element:s,position:r,localPosition:o};case"swipe":return{...e,element:s,position:r,localPosition:o,moves:e.moves.map(a=>{const{x:c,y:h}=this.getPosition({x:this.pixelToDip(a.x),y:this.pixelToDip(a.y)},{width:this.screen.width-1,height:this.screen.height-1});return{x:c,y:h,t:a.t}})};case"keypress":{const a=this.mapHardwareKey(e.key),c=this.mapHardwareKey(e.character);return{...e,key:a,character:c,shiftKey:typeof e.shiftKey=="number"?me(e.shiftKey):Boolean(e.shiftKey)}}case"findElements":return{...e,element:s}}return e})())}mapUI(e){var o,a;const t=(o=e.ui)!=null?o:e.result,s=e.springboard,n=c=>{var h;return{...this.mapElement(c),children:(h=c.children)==null?void 0:h.map(n)}},r=[];return t&&(this.platform==="ios"?r.push({type:"app",appId:(a=this.app)==null?void 0:a.bundle,children:t.map(n)}):r.push({type:"app",children:t.map(n)})),s&&r.push({type:"app",appId:"com.apple.springboard",children:s.map(n)}),r}mapElement(e){const{attributes:t,bounds:s,accessibilityElements:n,...r}=e,o=h=>this.platform==="android"?{x:this.pixelToDip(h.x),y:this.pixelToDip(h.y),width:this.pixelToDip(h.width),height:this.pixelToDip(h.height)}:{x:U(h.x),y:U(h.y),width:U(h.width),height:U(h.height)},a=h=>Object.keys(h).reduce((u,l)=>{switch(l){case"userInteractionEnabled":case"isHidden":return{...u,[l]:h[l]==="1"};default:return{...u,[l]:h[l]}}},{}),c=h=>h.map(u=>{const{accessibilityFrame:l}=u;return{...a(u),accessibilityFrame:l?o(l):void 0}});return k({...r,bounds:s?o(s):void 0,attributes:t?a(t):void 0,accessibilityElements:n?c(n):void 0})}mapAppetizerEvent(e,t){var s,n;switch(e){case"debug":return{type:"log",value:t};case"interceptResponse":return{type:"network",value:{type:"response",...t}};case"interceptRequest":return{type:"network",value:{type:"request",...t}};case"interceptError":return{type:"network",value:{type:"error",...t}};case"userError":return{type:"error",value:t};case"recordedAction":return{type:"action",value:this.mapAction(t)};case"playbackFoundAndSent":{const r=t;return{type:"playbackFoundAndSent",value:{...r,playback:{...r.playback,action:r.playback.action?this.mapAction(r.playback.action):void 0},matchedElements:(s=r.matchedElements)==null?void 0:s.map(o=>{if(o)return this.mapElement(o)})}}}case"playbackError":{const r=t;return{type:"playbackError",value:{...r,playback:{...r.playback,action:r.playback.action?this.mapAction(r.playback.action):void 0},matchedElements:(n=r.matchedElements)==null?void 0:n.map(o=>{if(o)return this.mapElement(o)})}}}case"uiDump":return{type:"uiDump",value:this.mapUI(t)};case"userInteractionReceived":return{type:"interaction",value:t};case"countdownWarning":return{type:"inactivityWarning",value:t};case"h264Data":return{type:"video",value:{...t,codec:"h264"}};case"frameData":return{type:"video",value:{...t,codec:"jpeg"}};case"audioData":return{type:"audio",value:{...t,codec:"aac"}};case"deleteEvent":return null}}}class x{static isCoordinatesWithinBounds(e,t){return!(e.x<0||e.x>t.width||e.y<0||e.y>t.height)}static isPositionWithinBounds(e){const t=A(e.x),s=A(e.y);return!(t<0||t>1||s<0||s>1)}static isValidNumber(e){return!(typeof e!="number"||isNaN(e))}}function ye(i){return i?1:0}function me(i){return i===1}function U(i){return typeof i=="number"?i:i==="inf"?1/0:i==="-inf"?-1/0:parseFloat(i)}function V(i){return i===1/0?"inf":i===-1/0?"-inf":i}function ge(i){if(typeof i!="object"||Array.isArray(i))throw new Error("Element must be an object");const e=Object.keys(i),s=be(e,["text","accessibilityIdentifier","accessibilityLabel","resource-id","content-desc","class","baseClass"]);if(s.length>0){const n=s.map(r=>`'${r}'`).join(", ");throw new Error(`Element has invalid properties: ${n}. Did you mean to put these under 'attributes'?`)}return i}function be(i,e){return i.filter(t=>e.includes(t))}function ve(i){const e=i.length;let t="";for(let s=0;s<e;s+=65535){let n=65535;s+65535>e&&(n=e-s),t+=String.fromCharCode.apply(null,i.subarray(s,s+n))}return t}function xe(i,e){const t=ve(i),s=btoa(t);return`data:${e};base64,`+s}class Ee{constructor({duration:e}){this.moves=[],this.duration=e,this.moves=[{x:0,y:0}]}to(e,t){if(typeof e!="string"||typeof t!="string")throw new f('x and y must be strings and in percentages (e.g. "50%")');if(!e.endsWith("%")||!t.endsWith("%"))throw new f('x and y must be in percentages (e.g. "50%")');return this.moves.push({x:parseFloat(e)/100,y:parseFloat(t)/100}),this}wait(e){var s;const t=this.moves[this.moves.length-1];return t&&(t.wait=e+((s=t.wait)!=null?s:0)),this}build(){var a;const t=(a=this.duration)!=null?a:Math.max(500,16*(this.moves.length-1)),s=Math.floor(t/16),n=[],r=Math.floor(s/(this.moves.length-1));let o=0;if(r===0){const c=(this.moves.length-1)*16;throw new Error(`Duration is too short for ${this.moves.length-1} moves, please set duration to at least ${c}ms`)}for(let c=0;c<this.moves.length-1;c++){const h=this.moves[c],u=this.moves[c+1],l=c===this.moves.length-2;for(let w=0;w<=r;w++){if(!l&&w===r)continue;const m=w/r,b=h.x+m*(u.x-h.x),F=h.y+m*(u.y-h.y),y=((c*r+w)*16+o)/1e3;n.push({x:b,y:F,t:y}),w===0&&h.wait&&(n.push({x:b,y:F,t:y+h.wait/1e3}),o+=h.wait)}}return n}up(e="50%"){const t=parseFloat(e);return this.to("0%",`-${t}%`)}down(e="50%"){const t=parseFloat(e);return this.to("0%",`${t}%`)}left(e="50%"){const t=parseFloat(e);return this.to(`-${t}%`,"0%")}right(e="50%"){const t=parseFloat(e);return this.to(`${t}%`,"0%")}}class K{constructor(){this.log=this.createLogFn("log"),this.warn=this.createLogFn("warn"),this.error=this.createLogFn("error"),this.debug=this.createLogFn("log")}createLogFn(e){const t=new Set,s="[Appetize]",n=Function.prototype.bind.call(console[e],console,s);return n.once=r=>{if(!t.has(r))return t.add(r),n.call(console,r)},n}}class ke extends N{constructor({socket:t,config:s,path:n,token:r,app:o,device:a,logger:c=new K}){super();B(this,S,void 0);B(this,P,void 0);this.isEndingManually=!1,this.countdownWarning=!1,this.ready=!1,this._waitForAnimationsPromises=new Set,this.actionLogs=[],this.config=s,this.socket=t,this.device=a,this.app=o,this.path=n,this.token=r,this.logger=c;const h=({type:u,value:l})=>{const m=new fe({platform:this.config.platform,screen:this.device.screen,app:this.app}).mapAppetizerEvent(u,l);switch(u){case"ready":this.ready=!0;break;case"adbOverTcp":{J(this,S,{...l,command:Ae(l)});break}case"networkInspectorUrl":J(this,P,l);break;case"countdownWarning":this.countdownWarning=!0;break;case"timeoutReset":this.countdownWarning=!1;break;case"deviceInfo":this.device=l;break}m?(this.emit(m.type,m.value),this.emit("*",m)):m!==null&&(this.emit(u,l),this.emit("*",{type:u,value:l}))};this.socket.on("*",h),this.on("disconnect",()=>{this.socket.off("*",h),this.isEndingManually||(this.countdownWarning?this.logger.warn("Appetize session has ended due to inactivity"):this.logger.warn("Session disconnected"))})}on(t,s){return t==="network"&&this.config.proxy!=="intercept"&&this.logger.warn('Session must be configured with `proxy: "intercept"` to listen to network events.'),t==="log"&&this.config.debug!==!0&&this.logger.warn("Session must be configured with `debug: true` to listen to log events."),t==="action"&&this.config.record!==!0&&this.logger.warn("Session must configured with `record: true` to listen to action events."),super.on(t,s)}async waitUntilReady(){let t=!0;const s=async r=>new Promise(o=>{const a=setInterval(()=>{r()&&o(void 0)},10);setTimeout(()=>{clearInterval(a),o(void 0)},3e3)}),n=()=>{t=!1};this.socket.once("disconnect",n);try{await H(r=>{if(!this.ready){if(t)throw new C("Timed out after 180s waiting for session to be ready");r(new Error("Session disconnected"))}},18e4)}finally{this.socket.off("disconnect",n)}await Promise.all([this.config.proxy==="intercept"?s(()=>Boolean(_(this,P))):Promise.resolve(),this.config.enableAdb?s(()=>Boolean(_(this,S))):Promise.resolve()])}async waitForEvent(t,s){try{return await D(this,t,s)}catch(n){throw d(n,this.waitForEvent),n}}async end(){this.isEndingManually=!0,await this.socket.disconnect()}get networkInspectorUrl(){return this.config.proxy!=="intercept"&&this.logger.warn('Session must be configured with `proxy: "intercept"` to use the network inspector'),_(this,P)}get adbConnection(){if(this.config.platform&&this.config.platform!=="android"&&this.logger.warn("Session must be connected to an Android device to use adb"),this.config.enableAdb||this.logger.warn("Session must be configured with `enableAdb: true` to use adb"),_(this,S))return _(this,S)}async rotate(t){try{const[s]=await Promise.all([this.waitForEvent("orientationChanged"),this.socket.send("userInteraction",{type:"keypress",key:t==="left"?"rotateLeft":"rotateRight",timeStamp:Date.now()})]);return s}catch(s){throw d(s,this.rotate),s}}async screenshot(t="buffer"){var s;try{this.socket.send("getScreenshot",{});const n=await D(this.socket,"screenshot",{timeout:6e4});if(!n.success)throw new f((s=n.error)!=null?s:"Screenshot failed");return{data:t==="buffer"?(a=>typeof window=="undefined"?Buffer.from(a):a)(n.data):xe(new Uint8Array(n.data),n.mimeType),mimeType:n.mimeType}}catch(n){throw d(n,this.screenshot),n}}async heartbeat(){try{return await this.socket.send("heartbeat")}catch(t){throw d(t,this.heartbeat),t}}async type(t){try{await R(1e3);const s=await this.playAction({type:"typeText",text:t});return await R(500),s}catch(s){throw d(s,this.type),s}}async keypress(t,s){try{if(t==="ANDROID_KEYCODE_MENU")return await this.socket.send("androidKeycodeMenu");if((s==null?void 0:s.shift)||t==="HOME"){switch(t){case"ArrowUp":t="arrowUp";break;case"ArrowDown":t="arrowDown";break;case"ArrowLeft":t="arrowLeft";break;case"ArrowRight":t="arrowRight";break;case"Enter":t="\r";break;case"Tab":t=" ";break;case"Backspace":t="\b";break}return this.playAction({type:"keypress",key:t,shiftKey:!!(s!=null&&s.shift)})}else return this.playAction({type:"keypress",character:t})}catch(n){throw d(n,this.keypress),n}}async setLanguage(t){try{return this.config.language=t,await this.socket.send("setLanguage",{language:t,timeStamp:Date.now()})}catch(s){throw d(s,this.setLanguage),s}}async setLocation(t,s){try{if(typeof t!="number"||typeof s!="number")throw new f("setLocation requires latitude and longitude to be numbers");const n=[t,s];return this.config.location=n,await this.socket.send("setLocation",{location:n,timeStamp:Date.now()})}catch(n){throw d(n,this.setLocation),n}}async openUrl(t){try{return await this.socket.send("openUrl",{url:t,timeStamp:Date.now()})}catch(s){throw d(s,this.openUrl),s}}async shake(){try{return await this.socket.send("shakeDevice")}catch(t){throw d(t,this.swipe),t}}async toggleSoftKeyboard(){try{if(this.config.platform!=="ios")throw new Error("toggleSoftKeyboard is only available on iOS devices");return await this.socket.send("toggleSoftKeyboard")}catch(t){throw d(t,this.toggleSoftKeyboard),t}}async biometry({match:t}){try{return await this.socket.send(t?"biometryMatch":"biometryNoMatch")}catch(s){throw d(s,this.biometry),s}}async allowInteractions(t){try{return await this.socket.send(t?"enableInteractions":"disableInteractions")}catch(s){throw d(s,this.allowInteractions),s}}async restartApp(){try{this.socket.send("restartApp");const{platform:t}=this.config;t==="ios"?await this.waitForEvent("appLaunch",{timeout:6e4}):await R(1e3)}catch(t){throw d(t,this.restartApp),t}}async reinstallApp(){try{this.socket.send("reinstallApp"),await this.waitForEvent("appLaunch",{timeout:6e4})}catch(t){throw d(t,this.reinstallApp),t}}async adbShellCommand(t){if(this.config.platform!=="android")throw new Error("adbShellCommand is only available on Android devices");try{return await this.socket.send("adbShellCommand",{command:t,timeStamp:Date.now()})}catch(s){throw d(s,this.adbShellCommand),s}}async playAction(t,s={}){const{timeout:n=1e4}=s;try{if(!this.config.record)throw new M("playAction()");if(isNaN(n))throw new f(`Invalid timeout value: ${s.timeout}`);if(n<0)throw new f(`Timeout value cannot be negative: ${s.timeout}`);"element"in t&&t.element&&ge(t.element);const r=new we({platform:this.config.platform,screen:this.device.screen,app:this.app}),o=ae(),c=s.noMap?t:r.mapAction(t),h={id:o,action:c,options:{...s,timeout:Math.round(n/1e3)}},[u]=await Promise.all([new Promise((l,w)=>{const m=()=>{this.off("playbackFoundAndSent",b),this.off("playbackError",F)},b=async y=>{var O;((O=y.playback)==null?void 0:O.id)===o&&(await this.addActionLog({action:t,payload:h,result:y}),m(),l(y))},F=async y=>{var O;if(((O=y.playback)==null?void 0:O.id)===o)switch(await this.addActionLog({action:t,payload:h,error:y}),m(),y.errorId){case"internalError":w(new f(`An internal error has occurred for the action: | ||
${JSON.stringify(t,null,2)}`));break;case"notFound":if(y.playback!==void 0&&"element"in y.playback.action&&y.playback.action.element!==void 0){w(new ue(y.playback.action.element));break}case"ambiguousMatch":if("matchedElements"in y&&y.matchedElements!==void 0){w(new le(y.matchedElements));break}case"invalidArgument":if("element"in t&&t.element&&y.message.match("outside the screen bounds")){w(new de(t));break}default:w(new f(y.message));break}};this.once("playbackFoundAndSent",b),this.once("playbackError",F)}),this.socket.send("playAction",h)]);return u}catch(r){throw d(r,this.playAction),r}}async playActions(t,s={}){try{if(!this.config.record)throw new M("playActions()");const n=[];for(const r of t){const o=await this.playAction(r,s);n.push(o);const a=t[t.indexOf(r)+1];a&&a.type==="keypress"&&r.type==="keypress"||await this.waitForAnimations({timeout:2e3}).catch(()=>{})}return n}catch(n){throw d(n,this.playActions),n}}async getUI({timeout:t=3e4}={}){try{return this.socket.send("dumpUi"),await D(this,"uiDump",{timeout:t})}catch(s){throw d(s,this.getUI),s}}async findElement(t,s){var n;try{return(n=(await this.playAction({type:"findElements",element:t,appId:s==null?void 0:s.appId},s)).matchedElements)==null?void 0:n[0]}catch(r){throw d(r,this.findElement),r}}async findElements(t,s){try{return(await this.playAction({type:"findElements",element:t,appId:s==null?void 0:s.appId},s)).matchedElements}catch(n){throw d(n,this.findElements),n}}async tap(t,s){var n;try{if(!this.config.record)throw new M("tap()");return await this.playAction({type:"tap",...t,duration:((n=t.duration)!=null?n:0)/1e3},s)}catch(r){throw d(r,this.tap),r}}async swipe({duration:t,gesture:s,...n},r){try{if(!this.config.record)throw new M("swipe()");let o;const a=new Ee({duration:t});if(typeof s=="function")s(a);else switch(s){case"up":a.up();break;case"down":a.down();break;case"left":a.left();break;case"right":a.right();break}if("element"in n)o={type:"swipe",element:n.element,localPosition:n.localPosition,moves:a.build()};else if("position"in n)o={type:"swipe",position:n.position,moves:a.build()};else throw new Error("Either element or position must be specified");return this.playAction(o,r)}catch(o){throw d(o,this.swipe),o}}async waitForAnimations(t={}){try{const{imageThreshold:s=.001,timeout:n=1e4}=t;let r=1e3,o=1;t.imageThresholdDuration&&(r=t.imageThresholdDuration);const[a,c,h]=he(),u=setTimeout(()=>{h(new C(`Timed out after ${n}ms waiting for animation to end. Waited for imageThreshold of ${s} but lowest was ${o.toFixed(2)}`))},n);let l;const w=({percentage:m,timestamp:b})=>{m<o&&(o=m),m<=s?(l||(l=b),l&&b-l>=r&&c()):l=void 0};return this.socket.send("enablePixelChangeDetection"),this.socket.on("pixelsChanged",w),this._waitForAnimationsPromises.add(a),await a.finally(()=>{clearTimeout(u),this.socket.off("pixelsChanged",w),this._waitForAnimationsPromises.delete(a),this._waitForAnimationsPromises.size===0&&this.socket.send("disablePixelChangeDetection")})}catch(s){throw d(s,this.waitForAnimations),s}}async addActionLog(t){this.actionLogs.push(t)}async getAdbInfo(){return this.logger.warn("getAdbInfo() is deprecated. Please use the `adbConnection` property instead."),Promise.resolve(_(this,S))}async getNetworkInspectorUrl(){return this.logger.warn("getNetworkInspectorUrl() is deprecated. Please use the `networkInspectorUrl` property instead."),Promise.resolve(_(this,P))}async getDeviceInfo(){return this.logger.warn("getDeviceInfo() is deprecated. Please use the `device` property instead."),Promise.resolve(this.device)}}S=new WeakMap,P=new WeakMap;function Ae(i){const e="ssh -fN -o StrictHostKeyChecking=no -oHostKeyAlgorithms=+ssh-rsa -p SERVER_PORT USERNAME@HOSTNAME -L6000:FORWARD_DESTINATION:FORWARD_PORT && adb connect localhost:6000";if(!i||!i.forwards[0])return;let t=e;return t=t.replace(/SERVER_PORT/,i.port.toString()),t=t.replace(/USERNAME/,i.user),t=t.replace(/HOSTNAME/,i.hostname),t=t.replace(/FORWARD_DESTINATION/,i.forwards[0].destination),t=t.replace(/FORWARD_PORT/,i.forwards[0].port.toString()),t}v.expect.extend({toHaveElement:async(i,e,t={})=>{try{const s=await i.findElements(e,t);return{pass:typeof t.matches=="number"?s.length===t.matches:s.length>0,message:()=>`Element not found: | ||
${JSON.stringify(e,null,2)}`}}catch(s){return{pass:!1,message:()=>s.message}}}});class Se extends N{constructor({socket:e,logger:t=new K}){super(),this.logger=t,this.socket=e,this.socket.on("*",({type:s,value:n})=>{const r=_e(s,n);r!==null&&(r?(this.emit(r.type,r.value),this.emit("*",r)):(this.emit(s,n),this.emit("*",{type:s,value:n})))})}on(e,t){return super.on(e,t)}async startSession(e){throw new Error("Not implemented")}async setConfig(e){throw new Error("Not implemented")}getConfig(){return this._config}async waitForSessionStart(e){return new Promise(async(t,s)=>{const n=()=>{s(new Error("Session disconnected before it was ready"))},r=a=>{s(new Error(`Session failed to start - ${typeof a.message=="object"?JSON.stringify(a.message):a.message}`))},o=a=>{a.message.match(/Too many requests/)&&s(new Error("Session failed to start due to too many requests"))};try{this.on("error",o),e.on("disconnect",n),e.on("error",r),await e.waitUntilReady()}finally{this.off("error",o),e.off("disconnect",n),e.off("error",r)}t(e)})}}function _e(i,e){switch(i){case"concurrentQueue":return{type:"queue",value:{type:"concurrent",name:e.name,position:e.position}};case"queue":return{type:"queue",value:{type:"session",position:e.position}};case"userError":return{type:"error",value:e};case"newSession":return null}}class Te extends Se{constructor({socket:e,window:t,logger:s=new K,config:n}){super({socket:e,logger:s}),this.ready=!1,this.window=t,n&&(this._config=this.mapConfig(n)),this.window.on("*",async({type:r,value:o})=>{if(this.ready)switch(r){case"app":this.app=o,this.emit(r,o);break;case"deviceInfo":this.device=o,this.emit(r,o);break;case"config":this._config=this.mapConfig(o);break}}),this.window.on("reinit",()=>{this.ready=!1,this.session=void 0,this.init({isReinit:!0})}),this.socket.on("*",async({type:r,value:o})=>{if(this.ready)switch(r){case"newSession":try{this.session=this.createSession(this._config,{path:o.path,token:o.sessionToken}),await this.waitForSessionStart(this.session),this.emit("session",this.session)}catch(a){this.session=void 0,this.emit("sessionError",a)}}}),this.init()}async init(e={isReinit:!1}){await this.window.waitUntilReady();const t=async()=>{if(e.isReinit){const r=this._config,o=await this.setConfig({});return this.setConfig({record:!0,...r,...o})}else return this.setConfig({record:!0,...this._config})},[s,n]=await Promise.all([this.window.postMessage({type:"getApp"},!0),this.window.postMessage({type:"getDeviceInfo"},!0),t()]);this.app=s,this.device=n,this.ready=!0}async waitUntilReady(){if(!this.ready)return H(async()=>{if(!this.ready)throw new Error("Timed out waiting for client to be ready")},3e4)}async startSession(e){try{await this.waitUntilReady()}catch(s){const n=s instanceof Error?s.message:s;throw new Error(`Failed to start session. ${n}`)}this.session&&await this.session.end(),await this.setConfig(e!=null?e:{});const[t]=await Promise.all([new Promise((s,n)=>{const r=a=>{this.off("session",r),this.off("sessionError",o),s(a)},o=a=>{this.off("session",r),this.off("sessionError",o),n(a)};this.on("session",r),this.on("sessionError",o)}),this.window.postMessage({type:"requestSession"},!0)]);return t}async config(e){return this.logger.warn("client.config() is deprecated and will be removed in a future major release. Use client.setConfig() instead."),this.setConfig(e)}async setConfig({publicKey:e,...t}){if(e){const n=await this.window.postMessage({type:"loadApp",value:e},!0);if(n&&"error"in n)throw new Error(n.error)}const s=await this.window.postMessage({type:"setConfig",value:this.validateConfig(t!=null?t:{})},!0).then(this.mapConfig.bind(this));return this._config=s,s}mapConfig(e){return e.autoplay===!0&&this.logger.warn.once("autoplay=true may cause the session to start before the SDK is ready. You should start the session programmatically using client.startSession() instead."),{...k(e),device:e.deviceType||e.device}}validateConfig(e){return e}createSession(e,t){throw new Error("Not implemented")}}class Pe extends K{constructor(){super(...arguments),this.logHistory=[],this.logLevel=process.env.CI==="true"?"warnings-errors":"verbose",this.log=this.createPlaywrightLogFn("log",()=>this.logLevel==="verbose"),this.warn=this.createPlaywrightLogFn("warn",()=>this.logLevel==="verbose"||this.logLevel==="warnings-errors"),this.error=this.createPlaywrightLogFn("error",()=>this.logLevel==="verbose"||this.logLevel==="warnings-errors"),this.debug=this.createPlaywrightLogFn("debug",()=>this.logLevel==="verbose"),this.clearLogHistory=()=>{this.logHistory=[]}}createPlaywrightLogFn(e,t){const s=new Set,n="[Appetize]",r=(...o)=>{this.logHistory.push({method:e,data:o}),t()&&console[e](n,...o)};return r.once=o=>{if(!s.has(o))return s.add(o),r.call(console,o)},r}}const E=new Pe;async function Fe(i){await i.pause()}function q(i){if(i.character)return i.character;const e=i.key;return j(e)?e:i.shiftKey?e.toUpperCase():e.toLowerCase()}function j(i){return/^[\b\t\r]/.test(i)}class $e{constructor({testInfo:e,session:t}){this.currentRecord=0,this.session=t,this.testInfo=e}async record(){const e=this.testInfo.file,t=await $.default.promises.readFile(e,"utf8"),n=t.split(` | ||
`).map((r,o)=>({line:r,num:o+1})).slice(this.testInfo.line).filter(({line:r})=>r.includes("session.record()"))[this.currentRecord].num;if(n!==void 0){console.log(`\u{1F534} Recording at line ${n}`);const r=[],o=c=>{Ce(r,c),console.log(Q(c))};this.session.on("action",o),await Fe(this.session.page),await R(2e3),this.session.off("action",o);const a=t.split(` | ||
...and ${i.length-5} more`:""}`}async function H(i,e=5e3){const t=Date.now();let s=!1;for(;;)try{return await i(r=>{if(r)throw s=!0,r})}catch(n){if(await new Promise(r=>setTimeout(r,100)),s||e!==null&&Date.now()-t>e)throw n}}async function R(i){return new Promise(e=>setTimeout(e,i))}async function C(i,e,t){const s=typeof t=="function"?{}:t,n=typeof t=="function"?t:t==null?void 0:t.predicate,r=typeof(s==null?void 0:s.timeout)!="undefined"?s.timeout:1e4;return new Promise((o,a)=>{const c=h=>{(!n||n(h))&&(i.off(e,c),o(h))};i.on(e,c),r!==null&&setTimeout(()=>{i.off(e,c),a(new D(`Timeout ${r}ms exceeded while waiting for event "${e}"`))},r)})}class N extends g.EventEmitter{constructor(){super(),this.on("error",()=>{})}}function A(i){if(typeof i=="string"){if(i.endsWith("%"))return parseInt(i,10)/100;throw new Error(`Invalid position value: ${i}. Must be a number between 0 and 1, or a string ending with %`)}return i}class we{constructor({platform:e,screen:t,app:s}){this.platform=e,this.screen=t,this.app=s}pixelToDip(e){return e/(this.screen.devicePixelRatio||1)}dipToPixel(e){return e*(this.screen.devicePixelRatio||1)}getCoordinates(e,t){const s=A(e.x),n=A(e.y);return{x:s*t.width,y:n*t.height}}mapHardwareKey(e){switch(e){case"HOME":return"home";case"VOLUME_UP":return"volumeUp";case"VOLUME_DOWN":return"volumeDown"}return e}mapAction(e){return k((()=>{e=k(e);let s,n,r;if("element"in e&&e.element&&(s=this.mapElement(e.element)),"position"in e&&e.position){const o=A(e.position.x),a=A(e.position.y);if(!E.isValidNumber(o)||!E.isValidNumber(a))throw new m(`Invalid position: (${e.position.x}, ${e.position.y}). Values must be a number or a percentage`);if(!E.isPositionWithinBounds(e.position))throw typeof e.position.x=="string"?new Error(`Invalid position: (${e.position.x}, ${e.position.y}) must be within (0%, 0%) and (100%, 100%)`):new Error(`Invalid position: (${e.position.x}, ${e.position.y}) must be within (0, 0) and (1, 1)`);this.platform==="android"?n=this.getCoordinates(e.position,{width:this.dipToPixel(this.screen.width)-1,height:this.dipToPixel(this.screen.height)-1}):n=this.getCoordinates(e.position,{width:this.screen.width-1,height:this.screen.height-1})}else if("coordinates"in e&&e.coordinates){if(!E.isValidNumber(e.coordinates.x)||!E.isValidNumber(e.coordinates.y))throw new m(`Invalid coordinates: (${e.coordinates.x}, ${e.coordinates.y}). Values must be a number`);if(!E.isCoordinatesWithinBounds(e.coordinates,{width:this.screen.width-1,height:this.screen.height-1}))throw new m(`Invalid coordinates: (${e.coordinates.x}, ${e.coordinates.y}) exceed screen bounds (${this.screen.width-1}, ${this.screen.height-1})`);this.platform==="android"?n={x:this.dipToPixel(e.coordinates.x),y:this.dipToPixel(e.coordinates.y)}:n=e.coordinates}if("localPosition"in e&&e.localPosition){const o=A(e.localPosition.x),a=A(e.localPosition.y);if(!E.isValidNumber(o)||!E.isValidNumber(a))throw new m(`Invalid localPosition: (${e.localPosition.x}, ${e.localPosition.y}). Values must be a number or a percentage`);r={x:o,y:a}}else s&&(r={x:.5,y:.5});if("duration"in e&&e.duration&&!E.isValidNumber(e.duration))throw new m(`Invalid duration: ${e.duration}. Value must be a number`);switch(e.type){case"tap":{const{position:o,...a}=e;return{...a,element:s,localPosition:r,coordinates:n}}case"swipe":{const{position:o,...a}=e;return{...a,element:s,localPosition:r,coordinates:n,moves:e.moves.map(c=>{if(this.platform==="android"){const{x:h,y:u}=this.getCoordinates(c,{width:this.dipToPixel(this.screen.width)-1,height:this.dipToPixel(this.screen.height)-1});return{...c,x:h,y:u}}else{const{x:h,y:u}=this.getCoordinates(c,{width:this.screen.width-1,height:this.screen.height-1});return{...c,x:h,y:u}}})}}case"keypress":{const o=this.mapHardwareKey(e.key),a=this.mapHardwareKey(e.character);return{...e,key:o,character:a,shiftKey:this.platform==="ios"?me(e.shiftKey):e.shiftKey}}case"findElements":return{...e,element:s}}return e})())}mapElement(e){const{attributes:t,bounds:s,...n}=e,r=()=>{if(s){const{x:a,y:c,width:h,height:u}=s;return this.platform==="android"?{x:this.dipToPixel(a),y:this.dipToPixel(c),width:this.dipToPixel(h),height:this.dipToPixel(u)}:{x:q(a),y:q(c),width:q(h),height:q(u)}}},o=()=>{if(t)return Object.keys(t).reduce((a,c)=>{if(this.platform==="ios")switch(c){case"userInteractionEnabled":case"isHidden":return{...a,[c]:t[c]?"1":"0"}}else this.platform;return{...a,[c]:t[c]}},{})};return k({...n,bounds:r(),attributes:o(),accessibilityElements:void 0})}}class fe{constructor({platform:e,screen:t,app:s}){this.platform=e,this.screen=t,this.app=s}pixelToDip(e){return e/(this.screen.devicePixelRatio||1)}dipToPixel(e){return e*(this.screen.devicePixelRatio||1)}getPosition(e,t){return{x:e.x/t.width,y:e.y/t.height}}mapHardwareKey(e){switch(e){case"home":return"HOME";case"volumeUp":return"VOLUME_UP";case"volumeDown":return"VOLUME_DOWN"}return e}mapAction(e){return k((()=>{let s,n,r,o="localPosition"in e?e.localPosition:void 0;switch("coordinates"in e&&e.coordinates&&(n={x:this.pixelToDip(e.coordinates.x),y:this.pixelToDip(e.coordinates.y)},r=this.getPosition(n,{width:this.screen.width-1,height:this.screen.height-1})),"element"in e&&e.element&&(s=this.mapElement(e.element),n&&s.bounds&&(o=this.getPosition({x:n.x-s.bounds.x,y:n.y-s.bounds.y},{width:s.bounds.width,height:s.bounds.height}))),e.type){case"tap":return{...e,element:s,position:r,localPosition:o};case"swipe":return{...e,element:s,position:r,localPosition:o,moves:e.moves.map(a=>{const{x:c,y:h}=this.getPosition({x:this.pixelToDip(a.x),y:this.pixelToDip(a.y)},{width:this.screen.width-1,height:this.screen.height-1});return{x:c,y:h,t:a.t}})};case"keypress":{const a=this.mapHardwareKey(e.key),c=this.mapHardwareKey(e.character);return{...e,key:a,character:c,shiftKey:typeof e.shiftKey=="number"?ye(e.shiftKey):Boolean(e.shiftKey)}}case"findElements":return{...e,element:s}}return e})())}mapUI(e){var o,a;const t=(o=e.ui)!=null?o:e.result,s=e.springboard,n=c=>{var h;return{...this.mapElement(c),children:(h=c.children)==null?void 0:h.map(n)}},r=[];return t&&(this.platform==="ios"?r.push({type:"app",appId:(a=this.app)==null?void 0:a.bundle,children:t.map(n)}):r.push({type:"app",children:t.map(n)})),s&&r.push({type:"app",appId:"com.apple.springboard",children:s.map(n)}),r}mapElement(e){const{attributes:t,bounds:s,accessibilityElements:n,...r}=e,o=h=>this.platform==="android"?{x:this.pixelToDip(h.x),y:this.pixelToDip(h.y),width:this.pixelToDip(h.width),height:this.pixelToDip(h.height)}:{x:U(h.x),y:U(h.y),width:U(h.width),height:U(h.height)},a=h=>Object.keys(h).reduce((u,l)=>{switch(l){case"userInteractionEnabled":case"isHidden":return{...u,[l]:h[l]==="1"};default:return{...u,[l]:h[l]}}},{}),c=h=>h.map(u=>{const{accessibilityFrame:l}=u;return{...a(u),accessibilityFrame:l?o(l):void 0}});return k({...r,bounds:s?o(s):void 0,attributes:t?a(t):void 0,accessibilityElements:n?c(n):void 0})}mapAppetizerEvent(e,t){var s,n;switch(e){case"debug":return{type:"log",value:t};case"interceptResponse":return{type:"network",value:{type:"response",...t}};case"interceptRequest":return{type:"network",value:{type:"request",...t}};case"interceptError":return{type:"network",value:{type:"error",...t}};case"userError":return{type:"error",value:t};case"recordedAction":return{type:"action",value:this.mapAction(t)};case"playbackFoundAndSent":{const r=t;return{type:"playbackFoundAndSent",value:{...r,playback:{...r.playback,action:r.playback.action?this.mapAction(r.playback.action):void 0},matchedElements:(s=r.matchedElements)==null?void 0:s.map(o=>{if(o)return this.mapElement(o)})}}}case"playbackError":{const r=t;return{type:"playbackError",value:{...r,playback:{...r.playback,action:r.playback.action?this.mapAction(r.playback.action):void 0},matchedElements:(n=r.matchedElements)==null?void 0:n.map(o=>{if(o)return this.mapElement(o)})}}}case"uiDump":return{type:"uiDump",value:this.mapUI(t)};case"userInteractionReceived":return{type:"interaction",value:t};case"countdownWarning":return{type:"inactivityWarning",value:t};case"h264Data":return{type:"video",value:{...t,codec:"h264"}};case"frameData":return{type:"video",value:{...t,codec:"jpeg"}};case"audioData":return{type:"audio",value:{...t,codec:"aac"}};case"deleteEvent":return null}}}class E{static isCoordinatesWithinBounds(e,t){return!(e.x<0||e.x>t.width||e.y<0||e.y>t.height)}static isPositionWithinBounds(e){const t=A(e.x),s=A(e.y);return!(t<0||t>1||s<0||s>1)}static isValidNumber(e){return!(typeof e!="number"||isNaN(e))}}function me(i){return i?1:0}function ye(i){return i===1}function U(i){return typeof i=="number"?i:i==="inf"?1/0:i==="-inf"?-1/0:parseFloat(i)}function q(i){return i===1/0?"inf":i===-1/0?"-inf":i}function ge(i){if(typeof i!="object"||Array.isArray(i))throw new Error("Element must be an object");const e=Object.keys(i),s=be(e,["text","accessibilityIdentifier","accessibilityLabel","resource-id","content-desc","class","baseClass"]);if(s.length>0){const n=s.map(r=>`'${r}'`).join(", ");throw new Error(`Element has invalid properties: ${n}. Did you mean to put these under 'attributes'?`)}return i}function be(i,e){return i.filter(t=>e.includes(t))}function ve(i,e){const t=Buffer.from(i).toString("base64");return`data:${e};base64,`+t}class Ee{constructor({duration:e}){this.moves=[],this.duration=e,this.moves=[{x:0,y:0}]}to(e,t){if(typeof e!="string"||typeof t!="string")throw new m('x and y must be strings and in percentages (e.g. "50%")');if(!e.endsWith("%")||!t.endsWith("%"))throw new m('x and y must be in percentages (e.g. "50%")');return this.moves.push({x:parseFloat(e)/100,y:parseFloat(t)/100}),this}wait(e){var s;const t=this.moves[this.moves.length-1];return t&&(t.wait=e+((s=t.wait)!=null?s:0)),this}build(){var a;const t=(a=this.duration)!=null?a:Math.max(500,16*(this.moves.length-1)),s=Math.floor(t/16),n=[],r=Math.floor(s/(this.moves.length-1));let o=0;if(r===0){const c=(this.moves.length-1)*16;throw new Error(`Duration is too short for ${this.moves.length-1} moves, please set duration to at least ${c}ms`)}for(let c=0;c<this.moves.length-1;c++){const h=this.moves[c],u=this.moves[c+1],l=c===this.moves.length-2;for(let w=0;w<=r;w++){if(!l&&w===r)continue;const f=w/r,b=h.x+f*(u.x-h.x),F=h.y+f*(u.y-h.y),y=((c*r+w)*16+o)/1e3;n.push({x:b,y:F,t:y}),w===0&&h.wait&&(n.push({x:b,y:F,t:y+h.wait/1e3}),o+=h.wait)}}return n}up(e="50%"){const t=parseFloat(e);return this.to("0%",`-${t}%`)}down(e="50%"){const t=parseFloat(e);return this.to("0%",`${t}%`)}left(e="50%"){const t=parseFloat(e);return this.to(`-${t}%`,"0%")}right(e="50%"){const t=parseFloat(e);return this.to(`${t}%`,"0%")}}class V{constructor(){this.log=this.createLogFn("log"),this.warn=this.createLogFn("warn"),this.error=this.createLogFn("error"),this.debug=this.createLogFn("log")}createLogFn(e){const t=new Set,s="[Appetize]",n=Function.prototype.bind.call(console[e],console,s);return n.once=r=>{if(!t.has(r))return t.add(r),n.call(console,r)},n}}class xe extends N{constructor({socket:t,config:s,path:n,token:r,app:o,device:a,logger:c=new V}){super();B(this,T,void 0);B(this,P,void 0);this.isEndingManually=!1,this.countdownWarning=!1,this.ready=!1,this._waitForAnimationsPromises=new Set,this.actionLogs=[],this.config=s,this.socket=t,this.device=a,this.app=o,this.path=n,this.token=r,this.logger=c;const h=({type:u,value:l})=>{const f=new fe({platform:this.config.platform,screen:this.device.screen,app:this.app}).mapAppetizerEvent(u,l);switch(u){case"ready":this.ready=!0;break;case"adbOverTcp":{J(this,T,{...l,command:ke(l)});break}case"networkInspectorUrl":J(this,P,l);break;case"countdownWarning":this.countdownWarning=!0;break;case"timeoutReset":this.countdownWarning=!1;break;case"deviceInfo":this.device=l;break}f?(this.emit(f.type,f.value),this.emit("*",f)):f!==null&&(this.emit(u,l),this.emit("*",{type:u,value:l}))};this.socket.on("*",h),this.on("disconnect",()=>{this.socket.off("*",h),this.isEndingManually||(this.countdownWarning?this.logger.warn("Appetize session has ended due to inactivity"):this.logger.warn("Session disconnected"))})}on(t,s){return t==="network"&&this.config.proxy!=="intercept"&&this.logger.warn('Session must be configured with `proxy: "intercept"` to listen to network events.'),t==="log"&&this.config.debug!==!0&&this.logger.warn("Session must be configured with `debug: true` to listen to log events."),t==="action"&&this.config.record!==!0&&this.logger.warn("Session must configured with `record: true` to listen to action events."),super.on(t,s)}async waitUntilReady(){let t=!0;const s=async r=>new Promise(o=>{const a=setInterval(()=>{r()&&o(void 0)},10);setTimeout(()=>{clearInterval(a),o(void 0)},3e3)}),n=()=>{t=!1};this.socket.once("disconnect",n);try{await H(r=>{if(!this.ready){if(t)throw new D("Timed out after 180s waiting for session to be ready");r(new Error("Session disconnected"))}},18e4)}finally{this.socket.off("disconnect",n)}await Promise.all([this.config.proxy==="intercept"?s(()=>Boolean(_(this,P))):Promise.resolve(),this.config.enableAdb?s(()=>Boolean(_(this,T))):Promise.resolve()])}async waitForEvent(t,s){try{return await C(this,t,s)}catch(n){throw d(n,this.waitForEvent),n}}async end(){this.isEndingManually=!0,await this.socket.disconnect()}get networkInspectorUrl(){return this.config.proxy!=="intercept"&&this.logger.warn('Session must be configured with `proxy: "intercept"` to use the network inspector'),_(this,P)}get adbConnection(){if(this.config.platform&&this.config.platform!=="android"&&this.logger.warn("Session must be connected to an Android device to use adb"),this.config.enableAdb||this.logger.warn("Session must be configured with `enableAdb: true` to use adb"),_(this,T))return _(this,T)}async rotate(t){try{const[s]=await Promise.all([this.waitForEvent("orientationChanged"),this.socket.send("userInteraction",{type:"keypress",key:t==="left"?"rotateLeft":"rotateRight",timeStamp:Date.now()})]);return s}catch(s){throw d(s,this.rotate),s}}async screenshot(t="buffer"){var s;try{this.socket.send("getScreenshot",{});const n=await C(this.socket,"screenshot",{timeout:6e4});if(!n.success)throw new m((s=n.error)!=null?s:"Screenshot failed");return{data:t==="buffer"?(a=>typeof window=="undefined"?Buffer.from(a):a)(n.data):ve(new Uint8Array(n.data),n.mimeType),mimeType:n.mimeType}}catch(n){throw d(n,this.screenshot),n}}async heartbeat(){try{return await this.socket.send("heartbeat")}catch(t){throw d(t,this.heartbeat),t}}async type(t){try{await R(1e3);const s=await this.playAction({type:"typeText",text:t});return await R(500),s}catch(s){throw d(s,this.type),s}}async keypress(t,s){try{if(t==="ANDROID_KEYCODE_MENU")return await this.socket.send("androidKeycodeMenu");if((s==null?void 0:s.shift)||t==="HOME"){switch(t){case"ArrowUp":t="arrowUp";break;case"ArrowDown":t="arrowDown";break;case"ArrowLeft":t="arrowLeft";break;case"ArrowRight":t="arrowRight";break;case"Enter":t="\r";break;case"Tab":t=" ";break;case"Backspace":t="\b";break}return this.playAction({type:"keypress",key:t,shiftKey:!!(s!=null&&s.shift)})}else return this.playAction({type:"keypress",character:t})}catch(n){throw d(n,this.keypress),n}}async setLanguage(t){try{return this.config.language=t,await this.socket.send("setLanguage",{language:t,timeStamp:Date.now()})}catch(s){throw d(s,this.setLanguage),s}}async setLocation(t,s){try{if(typeof t!="number"||typeof s!="number")throw new m("setLocation requires latitude and longitude to be numbers");const n=[t,s];return this.config.location=n,await this.socket.send("setLocation",{location:n,timeStamp:Date.now()})}catch(n){throw d(n,this.setLocation),n}}async openUrl(t){try{return await this.socket.send("openUrl",{url:t,timeStamp:Date.now()})}catch(s){throw d(s,this.openUrl),s}}async shake(){try{return await this.socket.send("shakeDevice")}catch(t){throw d(t,this.swipe),t}}async toggleSoftKeyboard(){try{if(this.config.platform!=="ios")throw new Error("toggleSoftKeyboard is only available on iOS devices");return await this.socket.send("toggleSoftKeyboard")}catch(t){throw d(t,this.toggleSoftKeyboard),t}}async biometry({match:t}){try{return await this.socket.send(t?"biometryMatch":"biometryNoMatch")}catch(s){throw d(s,this.biometry),s}}async allowInteractions(t){try{return await this.socket.send(t?"enableInteractions":"disableInteractions")}catch(s){throw d(s,this.allowInteractions),s}}async restartApp(){try{this.socket.send("restartApp");const{platform:t}=this.config;t==="ios"?await this.waitForEvent("appLaunch",{timeout:6e4}):await R(1e3)}catch(t){throw d(t,this.restartApp),t}}async reinstallApp(){try{this.socket.send("reinstallApp"),await this.waitForEvent("appLaunch",{timeout:6e4})}catch(t){throw d(t,this.reinstallApp),t}}async adbShellCommand(t){if(this.config.platform!=="android")throw new Error("adbShellCommand is only available on Android devices");try{return await this.socket.send("adbShellCommand",{command:t,timeStamp:Date.now()})}catch(s){throw d(s,this.adbShellCommand),s}}async playAction(t,s={}){const{timeout:n=1e4}=s;try{if(!this.config.record)throw new M("playAction()");if(isNaN(n))throw new m(`Invalid timeout value: ${s.timeout}`);if(n<0)throw new m(`Timeout value cannot be negative: ${s.timeout}`);"element"in t&&t.element&&ge(t.element);const r=new we({platform:this.config.platform,screen:this.device.screen,app:this.app}),o=ae(),c=s.noMap?t:r.mapAction(t),h={id:o,action:c,options:{...s,timeout:Math.round(n/1e3)}},[u]=await Promise.all([new Promise((l,w)=>{const f=()=>{this.off("playbackFoundAndSent",b),this.off("playbackError",F)},b=async y=>{var O;((O=y.playback)==null?void 0:O.id)===o&&(await this.addActionLog({action:t,payload:h,result:y}),f(),l(y))},F=async y=>{var O;if(((O=y.playback)==null?void 0:O.id)===o)switch(await this.addActionLog({action:t,payload:h,error:y}),f(),y.errorId){case"internalError":w(new m(`An internal error has occurred for the action: | ||
${JSON.stringify(t,null,2)}`));break;case"notFound":if(y.playback!==void 0&&"element"in y.playback.action&&y.playback.action.element!==void 0){w(new ue(y.playback.action.element));break}case"ambiguousMatch":if("matchedElements"in y&&y.matchedElements!==void 0){w(new le(y.matchedElements));break}case"invalidArgument":if("element"in t&&t.element&&y.message.match("outside the screen bounds")){w(new de(t));break}default:w(new m(y.message));break}};this.once("playbackFoundAndSent",b),this.once("playbackError",F)}),this.socket.send("playAction",h)]);return u}catch(r){throw d(r,this.playAction),r}}async playActions(t,s={}){try{if(!this.config.record)throw new M("playActions()");const n=[];for(const r of t){const o=await this.playAction(r,s);n.push(o);const a=t[t.indexOf(r)+1];a&&a.type==="keypress"&&r.type==="keypress"||await this.waitForAnimations({timeout:2e3}).catch(()=>{})}return n}catch(n){throw d(n,this.playActions),n}}async getUI({timeout:t=3e4}={}){try{return this.socket.send("dumpUi"),await C(this,"uiDump",{timeout:t})}catch(s){throw d(s,this.getUI),s}}async findElement(t,s){var n;try{return(n=(await this.playAction({type:"findElements",element:t,appId:s==null?void 0:s.appId},s)).matchedElements)==null?void 0:n[0]}catch(r){throw d(r,this.findElement),r}}async findElements(t,s){try{return(await this.playAction({type:"findElements",element:t,appId:s==null?void 0:s.appId},s)).matchedElements}catch(n){throw d(n,this.findElements),n}}async tap(t,s){var n;try{if(!this.config.record)throw new M("tap()");return await this.playAction({type:"tap",...t,duration:((n=t.duration)!=null?n:0)/1e3},s)}catch(r){throw d(r,this.tap),r}}async swipe({duration:t,gesture:s,...n},r){try{if(!this.config.record)throw new M("swipe()");let o;const a=new Ee({duration:t});if(typeof s=="function")s(a);else switch(s){case"up":a.up();break;case"down":a.down();break;case"left":a.left();break;case"right":a.right();break}if("element"in n)o={type:"swipe",element:n.element,localPosition:n.localPosition,moves:a.build()};else if("position"in n)o={type:"swipe",position:n.position,moves:a.build()};else throw new Error("Either element or position must be specified");return this.playAction(o,r)}catch(o){throw d(o,this.swipe),o}}async waitForAnimations(t={}){try{const{imageThreshold:s=.001,timeout:n=1e4}=t;let r=1e3,o=1;t.imageThresholdDuration&&(r=t.imageThresholdDuration);const[a,c,h]=he(),u=setTimeout(()=>{let f=`Timed out after ${n}ms waiting for animation to end.`;s<o&&(f+=` Waited for imageThreshold of ${s} but lowest was ${Math.round(o*1e4)/1e4}`),h(new D(f))},n);let l;const w=({percentage:f,timestamp:b})=>{f<o&&(o=f),f<=s?(l||(l=b),l&&b-l>=r&&c()):l=void 0};return this.socket.send("enablePixelChangeDetection"),this.socket.on("pixelsChanged",w),this._waitForAnimationsPromises.add(a),await a.finally(()=>{clearTimeout(u),this.socket.off("pixelsChanged",w),this._waitForAnimationsPromises.delete(a),this._waitForAnimationsPromises.size===0&&this.socket.send("disablePixelChangeDetection")})}catch(s){throw d(s,this.waitForAnimations),s}}async addActionLog(t){this.actionLogs.push(t)}async getAdbInfo(){return this.logger.warn("getAdbInfo() is deprecated. Please use the `adbConnection` property instead."),Promise.resolve(_(this,T))}async getNetworkInspectorUrl(){return this.logger.warn("getNetworkInspectorUrl() is deprecated. Please use the `networkInspectorUrl` property instead."),Promise.resolve(_(this,P))}async getDeviceInfo(){return this.logger.warn("getDeviceInfo() is deprecated. Please use the `device` property instead."),Promise.resolve(this.device)}}T=new WeakMap,P=new WeakMap;function ke(i){const e="ssh -fN -o StrictHostKeyChecking=no -oHostKeyAlgorithms=+ssh-rsa -p SERVER_PORT USERNAME@HOSTNAME -L6000:FORWARD_DESTINATION:FORWARD_PORT && adb connect localhost:6000";if(!i||!i.forwards[0])return;let t=e;return t=t.replace(/SERVER_PORT/,i.port.toString()),t=t.replace(/USERNAME/,i.user),t=t.replace(/HOSTNAME/,i.hostname),t=t.replace(/FORWARD_DESTINATION/,i.forwards[0].destination),t=t.replace(/FORWARD_PORT/,i.forwards[0].port.toString()),t}v.expect.extend({toHaveElement:async(i,e,t={})=>{try{const s=await i.findElements(e,t);return{pass:typeof t.matches=="number"?s.length===t.matches:s.length>0,message:()=>`Element not found: | ||
${JSON.stringify(e,null,2)}`}}catch(s){return{pass:!1,message:()=>s.message}}}});class Ae extends N{constructor({socket:e,logger:t=new V}){super(),this.logger=t,this.socket=e,this.socket.on("*",({type:s,value:n})=>{const r=Se(s,n);r!==null&&(r?(this.emit(r.type,r.value),this.emit("*",r)):(this.emit(s,n),this.emit("*",{type:s,value:n})))}),this.socket.on("newSession",()=>{this.queue&&(this.emit("queueEnd"),this.queue=void 0)}),this.on("queue",s=>{this.queue=s})}on(e,t){return super.on(e,t)}async startSession(e){throw new Error("Not implemented")}async setConfig(e){throw new Error("Not implemented")}getConfig(){return this._config}async waitForSessionStart(e){return new Promise(async(t,s)=>{const n=()=>{s(new Error("Session disconnected before it was ready"))},r=a=>{s(new Error(`Session failed to start - ${typeof a.message=="object"?JSON.stringify(a.message):a.message}`))},o=a=>{a.message.match(/Too many requests/)&&s(new Error("Session failed to start due to too many requests"))};try{this.on("error",o),e.on("disconnect",n),e.on("error",r),await e.waitUntilReady()}finally{this.off("error",o),e.off("disconnect",n),e.off("error",r)}t(e)})}}function Se(i,e){switch(i){case"concurrentQueue":return{type:"queue",value:{type:"concurrent",name:e.name,position:e.position}};case"queue":return{type:"queue",value:{type:"session",position:e.position}};case"userError":return{type:"error",value:e};case"newSession":return null}}class Te extends Ae{constructor({socket:e,window:t,logger:s=new V,config:n}){super({socket:e,logger:s}),this.ready=!1,this.window=t,n&&(this._config=this.mapConfig(n)),this.window.on("*",async({type:r,value:o})=>{if(this.ready)switch(r){case"app":this.app=o,this.emit(r,o);break;case"deviceInfo":this.device=o,this.emit(r,o);break;case"config":this._config=this.mapConfig(o);break}}),this.window.on("reinit",()=>{this.ready=!1,this.session=void 0,this.init({isReinit:!0})}),this.socket.on("*",async({type:r,value:o})=>{if(this.ready)switch(r){case"newSession":try{this.session=this.createSession(this._config,{path:o.path,token:o.sessionToken}),await this.waitForSessionStart(this.session),this.emit("session",this.session)}catch(a){this.session=void 0,this.emit("sessionError",a)}}}),this.init()}async init(e={isReinit:!1}){await this.window.waitUntilReady();const t=async()=>{if(e.isReinit){const r=this._config,o=await this.setConfig({});return this.setConfig({record:!0,...r,...o})}else return this.setConfig({record:!0,...this._config})},[s,n]=await Promise.all([this.window.postMessage({type:"getApp"},!0),this.window.postMessage({type:"getDeviceInfo"},!0),t()]);this.app=s,this.device=n,this.ready=!0}async waitUntilReady(){if(!this.ready)return H(async()=>{if(!this.ready)throw new Error("Timed out waiting for client to be ready")},3e4)}async startSession(e){try{await this.waitUntilReady()}catch(s){const n=s instanceof Error?s.message:s;throw new Error(`Failed to start session. ${n}`)}this.session&&await this.session.end(),await this.setConfig(e!=null?e:{});const[t]=await Promise.all([new Promise((s,n)=>{const r=a=>{this.off("session",r),this.off("sessionError",o),s(a)},o=a=>{this.off("session",r),this.off("sessionError",o),n(a)};this.on("session",r),this.on("sessionError",o)}),this.window.postMessage({type:"requestSession"},!0)]);return t}async config(e){return this.logger.warn("client.config() is deprecated and will be removed in a future major release. Use client.setConfig() instead."),this.setConfig(e)}async setConfig({publicKey:e,...t}){if(e){const n=await this.window.postMessage({type:"loadApp",value:e},!0);if(n&&"error"in n)throw new Error(n.error)}const s=await this.window.postMessage({type:"setConfig",value:this.validateConfig(t!=null?t:{})},!0).then(this.mapConfig.bind(this));return this._config=s,s}mapConfig(e){return e.autoplay===!0&&this.logger.warn.once("autoplay=true may cause the session to start before the SDK is ready. You should start the session programmatically using client.startSession() instead."),{...k(e),device:e.deviceType||e.device}}validateConfig(e){return e}createSession(e,t){throw new Error("Not implemented")}}class _e extends V{constructor(){super(...arguments),this.logHistory=[],this.logLevel=process.env.CI==="true"?"warnings-errors":"verbose",this.log=this.createPlaywrightLogFn("log",()=>this.logLevel==="verbose"),this.warn=this.createPlaywrightLogFn("warn",()=>this.logLevel==="verbose"||this.logLevel==="warnings-errors"),this.error=this.createPlaywrightLogFn("error",()=>this.logLevel==="verbose"||this.logLevel==="warnings-errors"),this.debug=this.createPlaywrightLogFn("debug",()=>this.logLevel==="verbose"),this.clearLogHistory=()=>{this.logHistory=[]}}createPlaywrightLogFn(e,t){const s=new Set,n="[Appetize]",r=(...o)=>{this.logHistory.push({method:e,data:o}),t()&&console[e](n,...o)};return r.once=o=>{if(!s.has(o))return s.add(o),r.call(console,o)},r}}const x=new _e;async function Pe(i){await i.pause()}function K(i){if(i.character)return i.character;const e=i.key;return j(e)?e:i.shiftKey?e.toUpperCase():e.toLowerCase()}function j(i){return/^[\b\t\r]/.test(i)}class Fe{constructor({testInfo:e,session:t}){this.currentRecord=0,this.session=t,this.testInfo=e}async record(){const e=this.testInfo.file,t=await $.default.promises.readFile(e,"utf8"),n=t.split(` | ||
`).map((r,o)=>({line:r,num:o+1})).slice(this.testInfo.line).filter(({line:r})=>r.includes("session.record()"))[this.currentRecord].num;if(n!==void 0){console.log(`\u{1F534} Recording at line ${n}`);const r=[],o=c=>{$e(r,c),console.log(Q(c))};this.session.on("action",o),await Pe(this.session.page),await R(2e3),this.session.off("action",o);const a=t.split(` | ||
`).map((c,h)=>{var u,l;if(h===n-1){const w=(l=(u=c.match(/^\s*/))==null?void 0:u[0])!=null?l:0;return`${r.map(b=>Q(b)).reduce((b,F,y)=>`${b} | ||
@@ -19,5 +19,5 @@ // ${y+1}. ${F}`,"// Recorded using session.record()")} | ||
`)}return c});await $.default.promises.writeFile(e,a.join(` | ||
`)),console.log("\u{1F7E2} Finished"),this.currentRecord+=1}}}function Ce(i,e){const t=i[i.length-1];if(t)switch(e.type){case"keypress":{(t==null?void 0:t.type)==="keypress"&&!j(e.key)&&!j(t.key)?(i.pop(),i.push({type:"typeText",text:q(t)+q(e)})):(t==null?void 0:t.type)==="typeText"&&!j(e.key)?(i.pop(),i.push({type:"typeText",text:t.text+q(e)})):i.push(e);break}default:i.push(e)}else i.push(e)}function Q(i){var t,s,n,r,o,a;let e="";switch(i.type){case"swipe":case"tap":{const c=i.element;return typeof c=="string"?e=` on element "${c}"`:(t=c==null?void 0:c.attributes)!=null&&t.accessibilityIdentifier?e=`element with accessibilityIdentifier "${(s=c.attributes)==null?void 0:s.accessibilityIdentifier}"`:(n=c==null?void 0:c.attributes)!=null&&n.class?e=`element with class "${(r=c.attributes)==null?void 0:r.class}"`:"position"in i&&((o=i.position)==null?void 0:o.x)&&((a=i.position)==null?void 0:a.y)&&(e=`position ${Math.round(i.position.x*100)}%, ${Math.round(i.position.y*100)}%`),e?`${i.type} on ${e}`:i.type}case"keypress":return`type "${q(i)}"`;case"typeText":return`type "${i.text}"`}}function X({type:i,value:e}){switch(i){case"deviceInfo":return{type:"deviceInfo",value:e};case"sessionRequested":return{type:"sessionRequested"};case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}const De="1.1.4-beta.0";class Ie extends N{constructor({page:e}){super(),this.ready=!1,this.page=e,this.page.exposeFunction("__appetize_on",t=>{const s=t==="string"?t:t.type,n=t.value;this.emit(s,n),this.emit("*",{type:s,value:n})})}async init(){this.ready=!1,await this.page.evaluate(async([e])=>new Promise((t,s)=>{const n=setTimeout(()=>{clearInterval(r),s(new C("Timed out after 60000ms waiting for connection to Appetize page"))},6e4),r=setInterval(()=>{const o=new MessageChannel;o.port1.onmessage=()=>{clearInterval(r),clearTimeout(n),t(!1)},window.postMessage({type:"init",appetizeClient:!0,version:e},"*",[o.port2]),window.__appetize_postMessage=async(a,c=!1)=>{const h=new MessageChannel;if(window.postMessage(a,"*",[h.port2]),c)return new Promise((u,l)=>{const w=setTimeout(()=>{l(new C("Timed out after 60000ms while waiting for postMessage response"))},6e4);h.port1.onmessage=m=>{clearTimeout(w),u(m.data)}})}},100)}),[De]),await this.page.evaluate(()=>{window.__appetize__video_frames=[],window.__appetize__audio_frames=[],window.addEventListener("message",e=>{var t;if(e.source===window){const s=typeof e.data=="string"?e.data:(t=e.data)==null?void 0:t.type;if(s==="socketEvent")switch(e.data.value.type){case"h264Data":case"frameData":if(window.__appetize__video_frames.push(e.data.value.value),!window.__appetize_emit_video)return;break;case"audioData":if(window.__appetize__audio_frames.push(e.data.value.value),!window.__appetize_emit_audio)return;break}else switch(s){case"frameData":case"recordedAction":case"playbackFoundAndSent":case"playbackNotFound":case"debug":case"interceptRequest":case"interceptResponse":case"interceptError":case"uiDump":return}window.__appetize_on(e.data)}})},[]),this.ready=!0}async waitUntilReady(){return H(async()=>{if(!this.ready)throw new C("Timed out after 60000ms while waiting for Appetize window to be ready.")},6e4)}enableVideoEvents(e=!0){return this.page.evaluate(t=>{window.__appetize_emit_video=t},e)}enableAudioEvents(e=!0){return this.page.evaluate(t=>{window.__appetize_emit_audio=t},e)}async postMessage(e,t=!1){return await this.waitUntilReady(),this.page.evaluate(async([s,n])=>window.__appetize_postMessage(s,n),[e,t])}async getVideoFrames(){return this.page.evaluate(()=>window.__appetize__video_frames)}async getAudioFrames(){return this.page.evaluate(()=>window.__appetize__audio_frames)}async resetVideoFrames(){return this.page.evaluate(()=>{window.__appetize__video_frames=[]})}async resetAudioFrames(){return this.page.evaluate(()=>{window.__appetize__audio_frames=[]})}}class G extends N{constructor({page:e,type:t,window:s}){super(),this.page=e,this.type=t,this.window=s,this.window.on("*",({type:n,value:r})=>{switch(n){case"socketEvent":if(r.socket===this.type){const o=r.type,a=r.value;o==="screenshot"&&a.data&&(a.data=new Uint8Array(Object.values(a.data).map(Number))),this.emit(o,a),this.emit("*",{type:o,value:a})}break;case"disconnect":this.emit("disconnect"),this.emit("*",{type:"disconnect"});break;case"sessionInfo":case"chromeDevToolsUrl":case"orientationChanged":case"deviceInfo":if(this.type==="appetizer"){const o=X({type:n,value:r});o&&(this.emit(o.type,o.value),this.emit("*",o))}break;case"sessionRequested":if(this.type==="webserver"){const o=X({type:n,value:r});o&&(this.emit(o.type,o.value),this.emit("*",o))}break}})}async send(e,t){return this.window.postMessage({type:"emitSocketEvent",value:{type:e,value:t,socket:this.type}})}async disconnect(){await this.send("disconnect")}waitForEvent(e,t){return D(this,e,t)}}class Re extends ke{constructor({page:e,config:t,window:s,testInfo:n,...r}){const o=new G({page:e,window:s,type:"appetizer"});super({...r,socket:o,config:t,logger:E}),this.actionLogs=[],this.window=s,this.page=e,this.config=t,this.testInfo=n,this.page.on("load",()=>{this.emit("disconnect")}),t.record&&this.on("disconnect",()=>{this.teardownUi()})}async addActionLog(e){if(e.error){const t=await this.getUI().catch(this.logger.warn);t&&(e.ui=t)}this.actionLogs.push(e)}teardownUi(){return this.window.page.evaluate(()=>{const e=document.querySelector("app-ui");e&&e.remove()})}async rotate(e){try{const[t]=await Promise.all([D(this.window,"orientationChanged"),await this.window.postMessage(e==="left"?"rotateLeft":"rotateRight")]);return this.window.page.waitForTimeout(1e3),t}catch(t){throw d(t,this.rotate),t}}async record(){try{if(!this.config.record)throw new f("Recording is not enabled, please enable it by setting `record: true` in session config");if(!this.testInfo)throw new f("session.record() requires using `session` from the test() arguments");return new $e({session:this,testInfo:this.testInfo}).record()}catch(e){throw d(e,this.record),e}}async waitForEvent(e,t){try{return await D(this,e,t)}catch(s){throw d(s,this.waitForEvent),s}}async waitForTimeout(e){try{return await R(e)}catch(t){throw d(t,this.waitForTimeout),t}}async waitForElement(e,t){try{const s=await this.findElements(e,t);if(s.length){if((t==null?void 0:t.matches)&&s.length!==t.matches)throw new Error(`Expected ${t.matches} elements, found ${s.length}`);return s}else throw new Error(`Element not found: | ||
${JSON.stringify(e)}`)}catch(s){throw d(s,this.waitForElement),s}}on(e,t){return e==="video"&&(z.onVideo||(this.logger.warn("Listening to the `video` event will significantly slow down your tests. It is recommended to use session.getVideoFrames() instead."),z.onVideo=!0),this.window.enableVideoEvents()),e==="audio"&&(z.onAudio||(this.logger.warn("Listening to the `audio` event will significantly slow down your tests. It is recommended to use session.getAudioFrames() instead."),z.onAudio=!0),this.window.enableAudioEvents()),super.on(e,t)}once(e,t){return e==="video"&&this.window.enableVideoEvents(),e==="audio"&&this.window.enableAudioEvents(),super.once(e,t)}async getVideoFrames(){return(await this.window.getVideoFrames()).map(t=>({...t,buffer:new Uint8Array(Object.values(t.buffer).map(Number))}))}async getAudioFrames(){return(await this.window.getAudioFrames()).map(t=>({...t,buffer:new Uint8Array(Object.values(t.buffer).map(Number))}))}}const z={onVideo:!1,onAudio:!1},Z=new WeakMap;class Oe extends Te{constructor({page:e}){var r;const t=(r=Z.get(e))!=null?r:new Ie({page:e});Z.set(e,t),t.init();const s=new G({type:"webserver",page:e,window:t});super({socket:s,window:t,logger:E}),this.window=t,this.page=e;let n=!1;this.on("queue",o=>{n||(n=!0,o.type==="concurrent"?this.logger.log(`Entered ${o.name}. Please wait until a slot becomes available.`):this.logger.log("All devices are currently in use. Please wait until requested device becomes available.")),o.position>0&&(o.type==="concurrent"?this.logger.log(`Position in ${o.name}: ${o.position}`):this.logger.log(`Position in queue: ${o.position}`))}),this.on("session",()=>{n&&(n=!1)})}validateConfig(e){var s;return{codec:((s=this.page.context().browser())==null?void 0:s.browserType().name())==="chromium"?"jpeg":"h264",...e}}createSession(e,t){return this.session=new Re({config:e,page:this.page,window:this.window,path:t.path,token:t.token,device:this.device,app:this.app}),this.session}}class Le{constructor(e){this.queue=null,this.page=e}async start(e){const{config:t,baseURL:s}=e,{publicKey:n,...r}=t;await this.page.goto(`${s!=null?s:"https://appetize.io"}/embed/${n}?${ce(r)}`),this.client=new Oe({page:this.page}),this.client.on("queue",a=>{this.queue=a});const o=async a=>{a!=null&&a.embed&&await this.page.setViewportSize({width:a.embed.width,height:a.embed.height})};this.client.on("deviceInfo",o),await this.client.waitUntilReady(),this.client.device&&o(this.client.device),this.session=await oe(()=>this.client.startSession({...t}),{retries:5,timeout:3e4,predicate:(a,c)=>a instanceof Error&&a.message.match(/too many requests/)?(console.warn(`Too many session requests. Retrying in 30 seconds... (attempt #${c})`),!0):!1})}}let I,ee;const W=v.test.extend({config:[async({_useConfig:i},e,t)=>{await e({...t.project.use.config,...i,autoplay:!1})},{auto:!0}],page:async({appetizePage:i},e)=>{await e(i.page)},appetizePage:async({context:i,config:e},t)=>{if(I&&JSON.stringify(ee)!==JSON.stringify(e)&&(await I.page.close(),I=null),!I&&(I=new Le(await i.newPage()),ee=e,!e.publicKey))throw new Error('Appetize public key not set. Make sure you have run test.use({ config: { publicKey: "..." } })');await t(I)},session:async({appetizePage:i,config:e,baseURL:t,_doSetupAndTeardown:s},n,r)=>{i.session||await T.step("Start Appetize session",async()=>{if(await i.start({config:e,baseURL:t}),!i.session)throw new Error("Appetize session failed to start");await T.step(`${i.session.path}/client?sessionToken=${i.session.token}`,()=>{T.info().annotations.push({type:"session.path",description:i.session.path},{type:"session.token",description:i.session.token}),Object.entries(i.session.config).forEach(([o,a])=>{T.info().annotations.push({type:`session.config.${o}`,description:JSON.stringify(a,null,2)})}),T.info().attach("Appetize Session Info",{contentType:"application/json",body:Buffer.from(JSON.stringify({path:i.session.path,token:i.session.token,config:i.session.config},null,2))})})}),i.session.testInfo=r,await n(i.session)},client:async({appetizePage:i},e)=>{await e(i.client)},_useConfig:[{},{option:!0}],_autoSnapshotSuffix:[async({},i,e)=>{e.snapshotSuffix="",await i()},{auto:!0}],_doSetupAndTeardown:[async({appetizePage:i},e,t)=>{var s,n;E.clearLogHistory(),i.session&&(i.session.actionLogs=[],await i.session.window.resetVideoFrames(),await i.session.window.resetAudioFrames()),await e(),await ie(i,t),t.status==="timedOut"&&((n=(s=i.queue)==null?void 0:s.position)!=null?n:0)>0&&E.warn("Test timed out while in queue for a device. You may increase your test timeout to account for higher queue times.")},{auto:!0}],context:[async({_browserContext:i,video:e,contextOptions:t},s)=>{e&&e!=="off"&&E.warn.once("Video recording is not yet supported with @appetize/playwright. Use tracing instead to view recordings of your tests https://docs.appetize.io/javascript-sdk/playwright/trace-viewer"),Object.keys(t).length&&E.warn.once("contextOptions are not supported with @appetize/playwright"),await s(i)},{scope:"test"}],_browserContext:[async({browser:i},e)=>{const t=await i.newContext();await e(t),await t.close()},{scope:"worker"}]}),Me=W.use,te=W.afterEach;Object.assign(W,{setup(i){return{}.__APPETIZE__SETUP_WARNED||E.warn("test.setup() is deprecated and will be removed in a future release. Use test.use({ config: {...} }) instead"),T.use({config:i})},use(i){const{config:e,...t}=i;return Me({...t,_useConfig:e})},afterEach(i){return te(async({appetizePage:e},t)=>{e&&await ie(e,t)}),te(i)}});const T=W,se=new WeakMap;async function ie(i,e){if(se.get(e.fn))return;se.set(e.fn,!0);const t=i.session,s="fail-output";if(e.status==="failed"||e.status==="timedOut"){if(t){const o=t.actionLogs;await $.default.promises.mkdir(e.outputPath(s));const a=async()=>{try{const h=await t.screenshot("buffer");await e.attach("screenshot",{body:h.data,contentType:"image/png"});const u=e.outputPath(s,"screenshot.png");await $.default.promises.writeFile(u,Buffer.from(h.data))}catch{}},c=async()=>{try{const h=o.map(w=>{if("screenshot"in w){const{screenshot:m,...b}=w;return b}return w});if(!h.length)return;const u=JSON.stringify(h,null,2);await e.attach("actions",{body:u,contentType:"application/json"});const l=e.outputPath(s,"actions.json");await $.default.promises.writeFile(l,u)}catch{}};await Promise.all([a(),c()])}await(async()=>{if(!!E.logHistory.length)try{const o=`${E.logHistory.map(c=>`[${c.method}] ${JSON.stringify(c.data).slice(2,-2)}`).join(` | ||
`)}`;await e.attach("sdk-logs",{body:o,contentType:"application/text"});const a=e.outputPath(s,"sdk-logs.txt");await $.default.promises.writeFile(a,o)}catch{}})()}}Object.defineProperty(p,"expect",{enumerable:!0,get:function(){return v.expect}}),p.test=T,Object.defineProperties(p,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
`)),console.log("\u{1F7E2} Finished"),this.currentRecord+=1}}}function $e(i,e){const t=i[i.length-1];if(t)switch(e.type){case"keypress":{(t==null?void 0:t.type)==="keypress"&&!j(e.key)&&!j(t.key)?(i.pop(),i.push({type:"typeText",text:K(t)+K(e)})):(t==null?void 0:t.type)==="typeText"&&!j(e.key)?(i.pop(),i.push({type:"typeText",text:t.text+K(e)})):i.push(e);break}default:i.push(e)}else i.push(e)}function Q(i){var t,s,n,r,o,a;let e="";switch(i.type){case"swipe":case"tap":{const c=i.element;return typeof c=="string"?e=` on element "${c}"`:(t=c==null?void 0:c.attributes)!=null&&t.accessibilityIdentifier?e=`element with accessibilityIdentifier "${(s=c.attributes)==null?void 0:s.accessibilityIdentifier}"`:(n=c==null?void 0:c.attributes)!=null&&n.class?e=`element with class "${(r=c.attributes)==null?void 0:r.class}"`:"position"in i&&((o=i.position)==null?void 0:o.x)&&((a=i.position)==null?void 0:a.y)&&(e=`position ${Math.round(i.position.x*100)}%, ${Math.round(i.position.y*100)}%`),e?`${i.type} on ${e}`:i.type}case"keypress":return`type "${K(i)}"`;case"typeText":return`type "${i.text}"`}}function X({type:i,value:e}){switch(i){case"deviceInfo":return{type:"deviceInfo",value:e};case"sessionRequested":return{type:"sessionRequested"};case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}const De="1.1.4-beta.1";class Ce extends N{constructor({page:e}){super(),this.ready=!1,this.page=e,this.page.exposeFunction("__appetize_on",t=>{const s=t==="string"?t:t.type,n=t.value;this.emit(s,n),this.emit("*",{type:s,value:n})})}async init(){this.ready=!1,await this.page.evaluate(async([e])=>new Promise((t,s)=>{const n=setTimeout(()=>{clearInterval(r),s(new D("Timed out after 60000ms waiting for connection to Appetize page"))},6e4),r=setInterval(()=>{const o=new MessageChannel;o.port1.onmessage=()=>{clearInterval(r),clearTimeout(n),o.port1.close(),o.port2.close(),t(!1)},window.postMessage({type:"init",appetizeClient:!0,version:e},"*",[o.port2]),window.__appetize_postMessage=async(a,c=!1)=>{const h=new MessageChannel;if(window.postMessage(a,"*",[h.port2]),c)return new Promise((u,l)=>{const w=setTimeout(()=>{l(new D("Timed out after 60000ms while waiting for postMessage response"))},6e4);h.port1.onmessage=f=>{clearTimeout(w),h.port1.close(),h.port2.close(),u(f.data)}});h.port1.close(),h.port2.close()}},100)}),[De]),await this.page.evaluate(()=>{window.__appetize__video_frames=[],window.__appetize__audio_frames=[],window.addEventListener("message",e=>{var t;if(e.source===window){const s=typeof e.data=="string"?e.data:(t=e.data)==null?void 0:t.type;if(s==="socketEvent")switch(e.data.value.type){case"h264Data":case"frameData":if(window.__appetize__video_frames.push(e.data.value.value),!window.__appetize_emit_video)return;break;case"audioData":if(window.__appetize__audio_frames.push(e.data.value.value),!window.__appetize_emit_audio)return;break}else switch(s){case"frameData":case"recordedAction":case"playbackFoundAndSent":case"playbackNotFound":case"debug":case"interceptRequest":case"interceptResponse":case"interceptError":case"uiDump":return}s==="socketEvent"&&e.data.value.type==="screenshot"&&(e.data.value.value.data=e.data.value.value.data.join(" ")),window.__appetize_on(e.data)}})},[]),this.ready=!0}async waitUntilReady(){return H(async()=>{if(!this.ready)throw new D("Timed out after 60000ms while waiting for Appetize window to be ready.")},6e4)}enableVideoEvents(e=!0){return this.page.evaluate(t=>{window.__appetize_emit_video=t},e)}enableAudioEvents(e=!0){return this.page.evaluate(t=>{window.__appetize_emit_audio=t},e)}async postMessage(e,t=!1){return await this.waitUntilReady(),this.page.evaluate(async([s,n])=>window.__appetize_postMessage(s,n),[e,t])}async getVideoFrames(){return this.page.evaluate(()=>window.__appetize__video_frames)}async getAudioFrames(){return this.page.evaluate(()=>window.__appetize__audio_frames)}async resetVideoFrames(){return this.page.evaluate(()=>{window.__appetize__video_frames=[]})}async resetAudioFrames(){return this.page.evaluate(()=>{window.__appetize__audio_frames=[]})}}class G extends N{constructor({page:e,type:t,window:s}){super(),this.page=e,this.type=t,this.window=s,this.window.on("*",({type:n,value:r})=>{switch(n){case"socketEvent":if(r.socket===this.type){const o=r.type,a=r.value;o==="screenshot"&&a.data&&(a.data=new Uint8Array(a.data.split(" ").map(Number))),this.emit(o,a),this.emit("*",{type:o,value:a})}break;case"disconnect":this.emit("disconnect"),this.emit("*",{type:"disconnect"});break;case"sessionInfo":case"chromeDevToolsUrl":case"orientationChanged":case"deviceInfo":if(this.type==="appetizer"){const o=X({type:n,value:r});o&&(this.emit(o.type,o.value),this.emit("*",o))}break;case"sessionRequested":if(this.type==="webserver"){const o=X({type:n,value:r});o&&(this.emit(o.type,o.value),this.emit("*",o))}break}})}async send(e,t){return this.window.postMessage({type:"emitSocketEvent",value:{type:e,value:t,socket:this.type}})}async disconnect(){await this.send("disconnect")}waitForEvent(e,t){return C(this,e,t)}}class Ie extends xe{constructor({page:e,config:t,window:s,testInfo:n,...r}){const o=new G({page:e,window:s,type:"appetizer"});super({...r,socket:o,config:t,logger:x}),this.actionLogs=[],this.window=s,this.page=e,this.config=t,this.testInfo=n,this.page.on("load",()=>{this.emit("disconnect")}),t.record&&this.on("disconnect",()=>{this.teardownUi()})}async addActionLog(e){if(e.error){const t=await this.getUI().catch(this.logger.warn);t&&(e.ui=t)}this.actionLogs.push(e)}teardownUi(){return this.window.page.evaluate(()=>{const e=document.querySelector("app-ui");e&&e.remove()})}async rotate(e){try{const[t]=await Promise.all([C(this.window,"orientationChanged"),await this.window.postMessage(e==="left"?"rotateLeft":"rotateRight")]);return this.window.page.waitForTimeout(1e3),t}catch(t){throw d(t,this.rotate),t}}async record(){try{if(!this.config.record)throw new m("Recording is not enabled, please enable it by setting `record: true` in session config");if(!this.testInfo)throw new m("session.record() requires using `session` from the test() arguments");return new Fe({session:this,testInfo:this.testInfo}).record()}catch(e){throw d(e,this.record),e}}async waitForEvent(e,t){try{return await C(this,e,t)}catch(s){throw d(s,this.waitForEvent),s}}async waitForTimeout(e){try{return await R(e)}catch(t){throw d(t,this.waitForTimeout),t}}async waitForElement(e,t){try{const s=await this.findElements(e,t);if(s.length){if((t==null?void 0:t.matches)&&s.length!==t.matches)throw new Error(`Expected ${t.matches} elements, found ${s.length}`);return s}else throw new Error(`Element not found: | ||
${JSON.stringify(e)}`)}catch(s){throw d(s,this.waitForElement),s}}on(e,t){return e==="video"&&(z.onVideo||(this.logger.warn("Listening to the `video` event will significantly slow down your tests. It is recommended to use session.getVideoFrames() instead."),z.onVideo=!0),this.window.enableVideoEvents()),e==="audio"&&(z.onAudio||(this.logger.warn("Listening to the `audio` event will significantly slow down your tests. It is recommended to use session.getAudioFrames() instead."),z.onAudio=!0),this.window.enableAudioEvents()),super.on(e,t)}once(e,t){return e==="video"&&this.window.enableVideoEvents(),e==="audio"&&this.window.enableAudioEvents(),super.once(e,t)}async getVideoFrames(){return(await this.window.getVideoFrames()).map(t=>({...t,buffer:new Uint8Array(Object.values(t.buffer).map(Number))}))}async getAudioFrames(){return(await this.window.getAudioFrames()).map(t=>({...t,buffer:new Uint8Array(Object.values(t.buffer).map(Number))}))}}const z={onVideo:!1,onAudio:!1},Z=new WeakMap;class Re extends Te{constructor({page:e}){var r;const t=(r=Z.get(e))!=null?r:new Ce({page:e});Z.set(e,t),t.init();const s=new G({type:"webserver",page:e,window:t});super({socket:s,window:t,logger:x}),this.window=t,this.page=e;let n=!1;this.on("queue",o=>{n||(n=!0,o.type==="concurrent"?this.logger.log(`Entered ${o.name}. Please wait until a slot becomes available.`):this.logger.log("All devices are currently in use. Please wait until requested device becomes available.")),o.position>0&&(o.type==="concurrent"?this.logger.log(`Position in ${o.name}: ${o.position}`):this.logger.log(`Position in queue: ${o.position}`))}),this.on("session",()=>{n&&(n=!1)})}validateConfig(e){var s;return{codec:((s=this.page.context().browser())==null?void 0:s.browserType().name())==="chromium"?"jpeg":"h264",...e}}createSession(e,t){return this.session=new Ie({config:e,page:this.page,window:this.window,path:t.path,token:t.token,device:this.device,app:this.app}),this.session}}class Oe{constructor(e){this.queueStart=null,this.queueEnd=null,this.page=e}get queueTime(){var e;return this.queueStart?((e=this.queueEnd)!=null?e:Date.now())-this.queueStart:null}async start(e){const{config:t,baseURL:s}=e,{publicKey:n,...r}=t;this.queueStart=null,this.queueEnd=null,await this.page.goto(`${s!=null?s:"https://appetize.io"}/embed/${n}?${ce(r)}`),this.client=new Re({page:this.page}),this.client.on("queue",()=>{this.queueStart||(this.queueStart=Date.now())}),this.client.on("queueEnd",()=>{this.queueStart&&(this.queueEnd=Date.now())});const o=async a=>{a!=null&&a.embed&&await this.page.setViewportSize({width:a.embed.width,height:a.embed.height})};this.client.on("deviceInfo",o),await this.client.waitUntilReady(),this.client.device&&o(this.client.device),this.session=await oe(()=>this.client.startSession({...t}),{retries:5,timeout:3e4,predicate:(a,c)=>a instanceof Error&&a.message.match(/too many requests/)?(console.warn(`Too many session requests. Retrying in 30 seconds... (attempt #${c})`),!0):!1})}}let I,ee;const W=v.test.extend({config:[async({_useConfig:i},e,t)=>{await e({...t.project.use.config,...i,autoplay:!1})},{auto:!0}],page:async({appetizePage:i},e)=>{await e(i.page)},appetizePage:async({context:i,config:e},t)=>{if(I&&JSON.stringify(ee)!==JSON.stringify(e)&&(await I.page.close(),I=null),!I&&(I=new Oe(await i.newPage()),ee=e,!e.publicKey))throw new Error('Appetize public key not set. Make sure you have run test.use({ config: { publicKey: "..." } })');await t(I)},session:async({appetizePage:i,config:e,baseURL:t,_doSetupAndTeardown:s},n,r)=>{i.session||await S.step("Start Appetize session",async()=>{if(await i.start({config:e,baseURL:t}),i.queueTime&&S.info().annotations.push({type:"queueTime",description:`${i.queueTime}ms`}),!i.session)throw new Error("Appetize session failed to start");await S.step(`${i.session.path}/client?sessionToken=${i.session.token}`,()=>{S.info().annotations.push({type:"session.path",description:i.session.path},{type:"session.token",description:i.session.token}),Object.entries(i.session.config).forEach(([o,a])=>{S.info().annotations.push({type:`session.config.${o}`,description:JSON.stringify(a,null,2)})}),S.info().attach("Appetize Session Info",{contentType:"application/json",body:Buffer.from(JSON.stringify({path:i.session.path,token:i.session.token,config:i.session.config},null,2))})})}),i.session.testInfo=r,await n(i.session)},client:async({appetizePage:i},e)=>{await e(i.client)},_useConfig:[{},{option:!0}],_autoSnapshotSuffix:[async({},i,e)=>{e.snapshotSuffix="",await i()},{auto:!0}],_doSetupAndTeardown:[async({appetizePage:i},e,t)=>{x.clearLogHistory(),i.session&&(i.session.actionLogs=[],await i.session.window.resetVideoFrames(),await i.session.window.resetAudioFrames()),await e(),await ie(i,t),t.status==="timedOut"&&i.queueTime&&x.error("Test timed out while in queue for a device. You may increase your test timeout to account for higher queue times.")},{auto:!0}],context:[async({_browserContext:i,video:e,contextOptions:t},s)=>{e&&e!=="off"&&x.warn.once("Video recording is not yet supported with @appetize/playwright. Use tracing instead to view recordings of your tests https://docs.appetize.io/javascript-sdk/playwright/trace-viewer"),Object.keys(t).length&&x.warn.once("contextOptions are not supported with @appetize/playwright"),await s(i)},{scope:"test"}],_browserContext:[async({browser:i},e)=>{const t=await i.newContext();await e(t),await t.close()},{scope:"worker"}]}),Le=W.use,te=W.afterEach;Object.assign(W,{setup(i){return{}.__APPETIZE__SETUP_WARNED||x.warn("test.setup() is deprecated and will be removed in a future release. Use test.use({ config: {...} }) instead"),S.use({config:i})},use(i){const{config:e,...t}=i;return Le({...t,_useConfig:e})},afterEach(i){return te(async({appetizePage:e},t)=>{e&&await ie(e,t)}),te(i)}});const S=W,se=new WeakMap;async function ie(i,e){if(se.get(e.fn))return;se.set(e.fn,!0);const t=i.session,s="fail-output";if(e.status==="failed"||e.status==="timedOut"){if(t){const o=t.actionLogs;await $.default.promises.mkdir(e.outputPath(s));const a=async()=>{try{const h=await t.screenshot("buffer");await e.attach("screenshot",{body:h.data,contentType:"image/png"});const u=e.outputPath(s,"screenshot.png");await $.default.promises.writeFile(u,Buffer.from(h.data))}catch{}},c=async()=>{try{const h=o.map(w=>{if("screenshot"in w){const{screenshot:f,...b}=w;return b}return w});if(!h.length)return;const u=JSON.stringify(h,null,2);await e.attach("actions",{body:u,contentType:"application/json"});const l=e.outputPath(s,"actions.json");await $.default.promises.writeFile(l,u)}catch{}};await Promise.all([a(),c()])}await(async()=>{if(!!x.logHistory.length)try{const o=`${x.logHistory.map(c=>`[${c.method}] ${JSON.stringify(c.data).slice(2,-2)}`).join(` | ||
`)}`;await e.attach("sdk-logs",{body:o,contentType:"application/text"});const a=e.outputPath(s,"sdk-logs.txt");await $.default.promises.writeFile(a,o)}catch{}})()}}Object.defineProperty(p,"expect",{enumerable:!0,get:function(){return v.expect}}),p.test=S,Object.defineProperties(p,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
//# sourceMappingURL=index.umd.js.map |
@@ -5,3 +5,2 @@ import { Page } from '@playwright/test'; | ||
import { PlaywrightSession } from './session'; | ||
import { ClientEvents } from '../../core/client'; | ||
export declare class AppetizePage { | ||
@@ -11,4 +10,6 @@ readonly page: Page; | ||
client: PlaywrightClient; | ||
queue: ClientEvents['queue'] | null; | ||
queueStart: number | null; | ||
queueEnd: number | null; | ||
constructor(page: Page); | ||
get queueTime(): number | null; | ||
start(options: { | ||
@@ -15,0 +16,0 @@ config: SessionConfig & { |
{ | ||
"name": "@appetize/playwright", | ||
"version": "1.1.4-beta.0", | ||
"version": "1.1.4-beta.1", | ||
"description": "Test your mobile apps on Appetize.io with Playwright", | ||
@@ -5,0 +5,0 @@ "files": [ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
598775
49
4467