@appetize/playwright
Advanced tools
Comparing version 0.4.0-beta.4 to 0.4.0-beta.5
@@ -24,6 +24,6 @@ import { Platform, ScreenBounds } from './recorder/common'; | ||
bounds?: { | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
x: InternalRecorderAPI.ObjCNumber; | ||
y: InternalRecorderAPI.ObjCNumber; | ||
width: InternalRecorderAPI.ObjCNumber; | ||
height: InternalRecorderAPI.ObjCNumber; | ||
} | undefined; | ||
@@ -67,6 +67,6 @@ attributes?: ((InternalRecorderAPI.IOSElementAttributes | InternalRecorderAPI.AndroidElementAttributes) & Partial<InternalRecorderAPI.IOSElementAttributes | InternalRecorderAPI.AndroidElementAttributes>) | undefined; | ||
bounds?: { | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
x: InternalRecorderAPI.ObjCNumber; | ||
y: InternalRecorderAPI.ObjCNumber; | ||
width: InternalRecorderAPI.ObjCNumber; | ||
height: InternalRecorderAPI.ObjCNumber; | ||
} | undefined; | ||
@@ -127,6 +127,6 @@ attributes?: ((InternalRecorderAPI.IOSElementAttributes | InternalRecorderAPI.AndroidElementAttributes) & Partial<InternalRecorderAPI.IOSElementAttributes | InternalRecorderAPI.AndroidElementAttributes>) | undefined; | ||
bounds?: { | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
x: InternalRecorderAPI.ObjCNumber; | ||
y: InternalRecorderAPI.ObjCNumber; | ||
width: InternalRecorderAPI.ObjCNumber; | ||
height: InternalRecorderAPI.ObjCNumber; | ||
} | undefined; | ||
@@ -133,0 +133,0 @@ attributes?: ((InternalRecorderAPI.IOSElementAttributes | InternalRecorderAPI.AndroidElementAttributes) & Partial<InternalRecorderAPI.IOSElementAttributes | InternalRecorderAPI.AndroidElementAttributes>) | undefined; |
@@ -97,6 +97,6 @@ import { StrictUnion } from '../../util'; | ||
bounds: { | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
x: ObjCNumber; | ||
y: ObjCNumber; | ||
width: ObjCNumber; | ||
height: ObjCNumber; | ||
}; | ||
@@ -131,1 +131,2 @@ attributes: IOSElementAttributes | AndroidElementAttributes; | ||
export type BooleanString = '0' | '1'; | ||
export type ObjCNumber = number | 'inf' | '-inf'; |
@@ -1,2 +0,2 @@ | ||
import { Element } from './api/recorder'; | ||
import { Element, TouchAction } from './api/recorder'; | ||
import { ElementSelector } from './api/recorder'; | ||
@@ -15,2 +15,5 @@ export declare function captureStackTrace(targetObject: any, constructorOpt: any): void; | ||
} | ||
export declare class ElementOutOfBoundsError extends OperationalError { | ||
constructor(action: TouchAction); | ||
} | ||
export declare class TimeoutError extends OperationalError { | ||
@@ -17,0 +20,0 @@ } |
@@ -1,5 +0,5 @@ | ||
(function(m,E){typeof exports=="object"&&typeof module!="undefined"?E(exports,require("events"),require("@playwright/test"),require("fs")):typeof define=="function"&&define.amd?define(["exports","events","@playwright/test","fs"],E):(m=typeof globalThis!="undefined"?globalThis:m||self,E(m.playwright={},m.events,m.test$1,m.fs))})(this,function(m,E,L,B){"use strict";function J(i){return i&&typeof i=="object"&&"default"in i?i:{default:i}}var T=J(B);async function Y(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 Q(){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 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 X(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 h(i,e){i instanceof d&&X(i,e)}class d extends Error{constructor(e){super(e),this.name="Error",this.isOperational=!0,Error.captureStackTrace(this,this.constructor)}}class U extends d{constructor(e){super(`No element found for selector | ||
${JSON.stringify(e,null,2)}`)}}class G extends d{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. | ||
(function(y,E){typeof exports=="object"&&typeof module!="undefined"?E(exports,require("events"),require("@playwright/test"),require("fs")):typeof define=="function"&&define.amd?define(["exports","events","@playwright/test","fs"],E):(y=typeof globalThis!="undefined"?globalThis:y||self,E(y.playwright={},y.events,y.test$1,y.fs))})(this,function(y,E,U,Y){"use strict";function Q(i){return i&&typeof i=="object"&&"default"in i?i:{default:i}}var T=Q(Y);async function X(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 G(){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 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 Z(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 h(i,e){i instanceof d&&Z(i,e)}class d extends Error{constructor(e){super(e),this.name="Error",this.isOperational=!0,Error.captureStackTrace(this,this.constructor)}}class q extends d{constructor(e){super(`No element found for selector | ||
${JSON.stringify(e,null,2)}`)}}class ee extends d{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. | ||
${Z(e)}`)}}class P extends d{}class $ extends d{constructor(e){super(`App Recorder must be enabled to use ${e}. Please set "record" to true in the config.`)}}function Z(i){const t=i.slice(0,5),s=i.length>5;return`${t.map((r,o)=>`// ${o} | ||
${se(e)}`)}}class te extends d{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 P extends d{}class $ extends d{constructor(e){super(`App Recorder must be enabled to use ${e}. Please set "record" to true in the config.`)}}function se(i){const t=i.slice(0,5),s=i.length>5;return`${t.map((r,o)=>`// ${o} | ||
${JSON.stringify(r,null,2)}`).join(` | ||
@@ -9,14 +9,14 @@ | ||
...and ${i.length-5} more`:""}`}async function F(i,e=5e3){const t=Date.now();for(;;)try{return await i()}catch(s){if(await new Promise(n=>setTimeout(n,100)),e!==null&&Date.now()-t>e)throw s}}async function I(i){return new Promise(e=>setTimeout(e,i))}async function A(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,c)=>{const a=u=>{(!n||n(u))&&(i.off(e,a),o(u))};i.on(e,a),r!==null&&setTimeout(()=>{i.off(e,a),c(new P(`Timeout ${r}ms exceeded while waiting for event "${e}"`))},r)})}function ee(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 te(i,e){const t=ee(i),s=btoa(t);return`data:${e};base64,`+s}class se{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 d('x and y must be strings and in percentages (e.g. "50%")');if(!e.endsWith("%")||!t.endsWith("%"))throw new d('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 c;const t=(c=this.duration)!=null?c: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 a=(this.moves.length-1)*16;throw new Error(`Duration is too short for ${this.moves.length-1} moves, please set duration to at least ${a}ms`)}for(let a=0;a<this.moves.length-1;a++){const u=this.moves[a],l=this.moves[a+1],w=a===this.moves.length-2;for(let f=0;f<=r;f++){if(!w&&f===r)continue;const v=f/r,y=u.x+v*(l.x-u.x),p=u.y+v*(l.y-u.y),g=((a*r+f)*16+o)/1e3;n.push({x:y,y:p,t:g}),f===0&&u.wait&&(n.push({x:y,y:p,t:g+u.wait/1e3}),o+=u.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%")}}function x(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 ie{constructor({platform:e,screen:t}){this.platform=e,this.screen=t}pixelToDip(e){return e/(this.screen.devicePixelRatio||1)}dipToPixel(e){return e*(this.screen.devicePixelRatio||1)}getCoordinates(e,t){const s=x(e.x),n=x(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)),"coordinates"in e&&e.coordinates){if(!b.isValidNumber(e.coordinates.x)||!b.isValidNumber(e.coordinates.y))throw new d(`Invalid coordinates: (${e.coordinates.x}, ${e.coordinates.y}). Values must be a number`);if(!b.isCoordinatesWithinBounds(e.coordinates,{width:this.screen.width-1,height:this.screen.height-1}))throw new d(`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}else if("position"in e&&e.position){const o=x(e.position.x),c=x(e.position.y);if(!b.isValidNumber(o)||!b.isValidNumber(c))throw new d(`Invalid position: (${e.position.x}, ${e.position.y}). Values must be a number or a percentage`);if(!b.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})}if("localPosition"in e&&e.localPosition){const o=x(e.localPosition.x),c=x(e.localPosition.y);if(!b.isValidNumber(o)||!b.isValidNumber(c))throw new d(`Invalid localPosition: (${e.localPosition.x}, ${e.localPosition.y}). Values must be a number or a percentage`);r={x:o,y:c}}else s&&(r={x:.5,y:.5});if("duration"in e&&e.duration&&!b.isValidNumber(e.duration))throw new d(`Invalid duration: ${e.duration}. Value must be a number`);switch(e.type){case"tap":{const{position:o,...c}=e;return{...c,element:s,localPosition:r,coordinates:n}}case"swipe":{const{position:o,...c}=e;return{...c,element:s,localPosition:r,coordinates:n,moves:e.moves.map(a=>{if(this.platform==="android"){const{x:u,y:l}=this.getCoordinates(a,{width:this.dipToPixel(this.screen.width)-1,height:this.dipToPixel(this.screen.height)-1});return{...a,x:u,y:l}}else{const{x:u,y:l}=this.getCoordinates(a,{width:this.screen.width-1,height:this.screen.height-1});return{...a,x:u,y:l}}})}}case"keypress":{const o=this.mapHardwareKey(e.key),c=this.mapHardwareKey(e.character);return{...e,key:o,character:c,shiftKey:this.platform==="ios"?re(e.shiftKey):e.shiftKey}}case"findElements":return{...e,element:s}}return e})())}mapElement(e){var c;const{attributes:t,bounds:s,...n}=e,r=()=>{if(s){const{x:a,y:u,width:l,height:w}=s;return this.platform==="android"?{x:this.dipToPixel(a),y:this.dipToPixel(u),width:this.dipToPixel(l),height:this.dipToPixel(w)}:s}},o=()=>{if(t)return Object.keys(t).reduce((a,u)=>{if(this.platform==="ios")switch(u){case"userInteractionEnabled":case"isHidden":return{...a,[u]:t[u]?"1":"0"}}else this.platform;return{...a,[u]:t[u]}},{})};return k({...n,bounds:r(),attributes:o(),accessibilityElements:(c=e.accessibilityElements)==null?void 0:c.map(a=>this.mapElement({attributes:a}).attributes)})}}class ne{constructor({platform:e,screen:t}){this.platform=e,this.screen=t}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;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":{const{coordinates:c,...a}=e;return{...a,element:s,position:r,localPosition:o}}case"swipe":{const{coordinates:c,...a}=e;return{...a,element:s,position:r,localPosition:o,moves:e.moves.map(u=>{const{x:l,y:w}=this.getPosition({x:this.pixelToDip(u.x),y:this.pixelToDip(u.y)},{width:this.screen.width-1,height:this.screen.height-1});return{x:l,y:w,t:u.t}})}}case"keypress":{const c=this.mapHardwareKey(e.key),a=this.mapHardwareKey(e.character);return{...e,key:c,character:a,shiftKey:typeof e.shiftKey=="number"?oe(e.shiftKey):Boolean(e.shiftKey)}}case"findElements":return{...e,element:s}}return e})())}mapUI(e){const t=s=>{var n;return{...this.mapElement(s),children:(n=s.children)==null?void 0:n.map(t)}};return Object.keys(e).reduce((s,n)=>(s[n]=e[n].map(t),s),{})}mapElement(e){var c;const{attributes:t,bounds:s,...n}=e,r=()=>{if(s)return this.platform==="android"?{x:this.pixelToDip(s.x),y:this.pixelToDip(s.y),width:this.pixelToDip(s.width),height:this.pixelToDip(s.height)}:s},o=()=>{if(t)return Object.keys(t).reduce((a,u)=>{switch(u){case"userInteractionEnabled":case"isHidden":return{...a,[u]:t[u]==="1"};default:return{...a,[u]:t[u]}}},{})};return k({...n,bounds:r(),attributes:o(),accessibilityElements:(c=e.accessibilityElements)==null?void 0:c.map(a=>this.mapElement({attributes:a}).attributes)})}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"dumpUi":return{type:"dumpUi",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 b{static isCoordinatesWithinBounds(e,t){return!(e.x<0||e.x>t.width||e.y<0||e.y>t.height)}static isPositionWithinBounds(e){const t=x(e.x),s=x(e.y);return!(t<0||t>1||s<0||s>1)}static isValidNumber(e){return!(typeof e!="number"||isNaN(e))}}function re(i){return i?1:0}function oe(i){return i===1}class C{constructor(){this.log=function(){const e="[Appetize]";return Function.prototype.bind.call(console.log,console,e)}(),this.warn=function(){const e="[Appetize]";return Function.prototype.bind.call(console.warn,console,e)}(),this.error=function(){const e="[Appetize]";return Function.prototype.bind.call(console.error,console,e)}(),this.debug=function(){const e="[Appetize]";return Function.prototype.bind.call(console.debug,console,e)}()}}function ae(i){if(typeof i!="object"||Array.isArray(i))throw new Error("Element must be an object");const e=Object.keys(i),s=ce(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 ce(i,e){return i.filter(t=>e.includes(t))}class ue extends E.EventEmitter{constructor({socket:e,config:t,deviceInfo:s,sessionInfo:n,logger:r=new C}){super(),this.isEndingManually=!1,this.countdownWarning=!1,this.ready=!1,this.actionLogs=[],this.config=t,this.socket=e,this.deviceInfo=s,this.path=n.path,this.token=n.token,this.logger=r;const o=new ne({platform:t.platform,screen:s.screen}),c=({type:a,value:u})=>{const l=o.mapAppetizerEvent(a,u);switch(a){case"ready":this.ready=!0;break;case"adbOverTcp":{this.adbConnectionInfo={...u,command:he(u)};break}case"networkInspectorUrl":this.networkInspectorUrl=u;break;case"countdownWarning":this.countdownWarning=!0;break;case"timeoutReset":this.countdownWarning=!1;break}l?(this.emit(l.type,l.value),this.emit("*",l)):l!==null&&(this.emit(a,u),this.emit("*",{type:a,value:u}))};this.socket.on("*",c),this.on("disconnect",()=>{this.socket.off("*",c),this.isEndingManually||(this.countdownWarning?console.warn("Appetize session has ended due to inactivity"):console.warn("Session disconnected"))})}on(e,t){return e==="network"&&this.config.proxy!=="intercept"&&console.warn('Session must be configured with `proxy: "intercept"` to listen to network events.'),e==="log"&&this.config.debug!==!0&&console.warn("Session must be configured with `debug: true` to listen to log events."),e==="action"&&this.config.record!==!0&&console.warn("Session must configured with `record: true` to listen to action events."),super.on(e,t)}async waitUntilReady(){const e=async t=>new Promise(s=>{const n=setInterval(()=>{this[t]&&s(void 0)},10);setTimeout(()=>{clearInterval(n),s(void 0)},3e3)});await F(()=>{if(!this.ready)throw new P("Timed out after 180s waiting for session to be ready")},18e4),await Promise.all([e("networkInspectorUrl"),e("adbConnectionInfo")])}async waitForEvent(e,t){try{return await A(this,e,t)}catch(s){throw h(s,this.waitForEvent),s}}async end(){this.isEndingManually=!0,await this.socket.disconnect()}getConfig(){return this.config}getDeviceInfo(){return this.deviceInfo}getNetworkInspectorUrl(){try{if(this.config.proxy!=="intercept")throw new d('Session must be configured with `proxy: "intercept"` to use the network inspector');return this.networkInspectorUrl}catch(e){throw h(e,this.getNetworkInspectorUrl),e}}getAdbInfo(){try{if(this.config.platform&&this.config.platform!=="android")throw new d("Session must be connected to an Android device to use adb");if(!this.config.enableAdb)throw new d("Session must be configured with `enableAdb: true` to use adb");return this.adbConnectionInfo?this.adbConnectionInfo:F(()=>{if(this.adbConnectionInfo)return this.adbConnectionInfo;throw new P("Timed out after 60000ms waiting for adb connection")},6e4)}catch(e){throw h(e,this.getAdbInfo),e}}async rotate(e){try{const[t]=await Promise.all([this.waitForEvent("orientationChanged"),this.socket.send("userInteraction",{type:"keypress",key:e==="left"?"rotateLeft":"rotateRight",timeStamp:Date.now()})]);return t}catch(t){throw h(t,this.rotate),t}}async screenshot(e="buffer"){try{this.socket.send("getScreenshot");const t=await A(this.socket,"screenshot",{timeout:6e4});if(!t.success)throw new d("Screenshot failed");return{data:e==="buffer"?(r=>typeof window=="undefined"?Buffer.from(r):r)(t.data):te(new Uint8Array(t.data),t.mimeType),mimeType:t.mimeType}}catch(t){throw h(t,this.screenshot),t}}async heartbeat(){try{return await this.socket.send("heartbeat")}catch(e){throw h(e,this.heartbeat),e}}async type(e){try{this.config.platform==="ios"&&await I(1e3);const t=await this.playAction({type:"typeText",text:e});return this.config.platform==="ios"&&await I(e.length*50),t}catch(t){throw h(t,this.type),t}}async keypress(e,t){try{if(e==="ANDROID_KEYCODE_MENU")return await this.socket.send("androidKeycodeMenu");if((t==null?void 0:t.shift)||e==="HOME"){switch(e){case"ArrowUp":e="arrowUp";break;case"ArrowDown":e="arrowDown";break;case"ArrowLeft":e="arrowLeft";break;case"ArrowRight":e="arrowRight";break;case"Enter":e="\r";break;case"Tab":e=" ";break;case"Backspace":e="\b";break}return this.playAction({type:"keypress",key:e,shiftKey:!!(t!=null&&t.shift)})}else return this.playAction({type:"keypress",character:e})}catch(s){throw h(s,this.keypress),s}}async setLanguage(e){try{return this.config.language=e,await this.socket.send("setLanguage",{language:e,timeStamp:Date.now()})}catch(t){throw h(t,this.setLanguage),t}}async setLocation(e,t){try{if(typeof e!="number"||typeof t!="number")throw new d("setLocation requires latitude and longitude to be numbers");const s=[e,t];return this.config.location=s,await this.socket.send("setLocation",{location:s,timeStamp:Date.now()})}catch(s){throw h(s,this.setLocation),s}}async openUrl(e){try{return await this.socket.send("openUrl",{url:e,timeStamp:Date.now()})}catch(t){throw h(t,this.openUrl),t}}async shake(){try{return await this.socket.send("shakeDevice")}catch(e){throw h(e,this.swipe),e}}async biometry({match:e}){try{return await this.socket.send(e?"biometryMatch":"biometryNoMatch")}catch(t){throw h(t,this.biometry),t}}async allowInteractions(e){try{return await this.socket.send(e?"enableInteractions":"disableInteractions")}catch(t){throw h(t,this.allowInteractions),t}}async restartApp(){try{this.socket.send("restartApp");const{platform:e}=this.config;e==="ios"?await this.waitForEvent("appLaunch",{timeout:6e4}):await I(1e3)}catch(e){throw h(e,this.restartApp),e}}async reinstallApp(){try{this.socket.send("reinstallApp"),await this.waitForEvent("appLaunch",{timeout:6e4})}catch(e){throw h(e,this.reinstallApp),e}}async adbShellCommand(e){if(this.config.platform!=="android")throw new Error("adbShellCommand is only available on Android devices");try{return await this.socket.send("adbShellCommand",{command:e,timeStamp:Date.now()})}catch(t){throw h(t,this.adbShellCommand),t}}async playAction(e,t={}){const{timeout:s=1e4}=t;try{if(!this.config.record)throw new $("playAction()");if(isNaN(s))throw new d(`Invalid timeout value: ${t.timeout}`);if(s<0)throw new d(`Timeout value cannot be negative: ${t.timeout}`);"element"in e&&e.element&&ae(e.element);const n=new ie({platform:this.config.platform,screen:this.deviceInfo.screen}),r=Q(),c=t.noMap?e:n.mapAction(e),a={id:r,action:c,options:{...t,timeout:Math.round(s/1e3)}},[u]=await Promise.all([new Promise((l,w)=>{const f=()=>{this.off("playbackFoundAndSent",v),this.off("playbackError",y)},v=async p=>{var g;((g=p.playback)==null?void 0:g.id)===r&&(await this.addActionLog({action:e,payload:a,result:p}),f(),l(p))},y=async p=>{var g;if(((g=p.playback)==null?void 0:g.id)===r)switch(await this.addActionLog({action:e,payload:a,error:p}),f(),p.errorId){case"internalError":w(new d(`An internal error has occurred for the action: | ||
${JSON.stringify(e,null,2)}`));break;case"notFound":if(p.playback!==void 0&&"element"in p.playback.action&&p.playback.action.element!==void 0){w(new U(p.playback.action.element));break}case"ambiguousMatch":if("matchedElements"in p&&p.matchedElements!==void 0){w(new G(p.matchedElements));break}default:w(new d(p.message));break}};this.once("playbackFoundAndSent",v),this.once("playbackError",y)}),this.socket.send("playAction",a)]);return u}catch(n){throw h(n,this.playAction),n}}async playActions(e,t={}){try{if(!this.config.record)throw new $("playActions()");const s=[];for(const n of e){const r=await this.playAction(n,t);s.push(r);const o=e[e.indexOf(n)+1];o&&o.type==="keypress"&&n.type==="keypress"||await I(1e3)}return s}catch(s){throw h(s,this.playActions),s}}async getUI({all:e,timeout:t=3e4}={}){var s;try{this.socket.send("dumpUi");const n=await A(this.socket,"uiDump",{timeout:t});return e?n:(s=n==null?void 0:n.ui)!=null?s:n==null?void 0:n.result}catch(n){throw h(n,this.getUI),n}}async findElement(e,t){var s;try{const n=await this.playAction({type:"findElements",element:e,appId:t==null?void 0:t.appId},t);if((s=n.matchedElements)!=null&&s[0])return n.matchedElements[0];throw new U(e)}catch(n){throw h(n,this.findElement),n}}async findElements(e,t){try{return(await this.playAction({type:"findElements",element:e,appId:t==null?void 0:t.appId},t)).matchedElements}catch(s){throw h(s,this.findElements),s}}async tap(e,t){var s;try{if(!this.config.record)throw new $("tap()");return await this.playAction({type:"tap",...e,duration:((s=e.duration)!=null?s:0)/1e3},t)}catch(n){throw h(n,this.tap),n}}async swipe({duration:e,gesture:t,...s},n){try{if(!this.config.record)throw new $("swipe()");let r;const o=new se({duration:e});if(typeof t=="function")t(o);else switch(t){case"up":o.up();break;case"down":o.down();break;case"left":o.left();break;case"right":o.right();break}if("element"in s)r={type:"swipe",element:s.element,localPosition:s.localPosition,moves:o.build()};else if("position"in s)r={type:"swipe",position:s.position,moves:o.build()};else throw new Error("Either element or position must be specified");return this.playAction(r,n)}catch(r){throw h(r,this.swipe),r}}async addActionLog(e){this.actionLogs.push(e)}}function he(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}L.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 le extends E.EventEmitter{constructor({socket:e,logger:t=new C}){super(),this.logger=t,this.socket=e,this.socket.on("*",({type:s,value:n})=>{const r=de(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 config(e){throw new Error("Not implemented")}async waitForSessionStart(e){return new Promise(async(t,s)=>{const n=()=>{s(new Error("Session disconnected before it was ready"))},r=c=>{s(new Error(`Session failed to start - ${typeof c.message=="object"?JSON.stringify(c.message):c.message}`))},o=c=>{c.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 de(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 pe extends le{constructor({socket:e,window:t,logger:s=new C}){super({socket:e,logger:s}),this.ready=!1,this.window=t,this.window.on("*",async({type:n,value:r})=>{switch(n){case"app":this.app=r,this.emit(n,r);break;case"deviceInfo":this.deviceInfo=r,this.emit(n,r);break;case"config":this._config=this.mapConfig(r);break}}),this.socket.on("*",async({type:n,value:r})=>{switch(n){case"newSession":{try{this.session=this.createSession(this._config,{path:r.path,token:r.sessionToken}),await this.waitForSessionStart(this.session)}catch(o){this.emit("sessionError",o)}this.emit("session",this.session)}}}),this.init()}async init(){await this.window.waitUntilReady();const[e,t]=await Promise.all([this.window.postMessage({type:"getApp"},!0),this.window.postMessage({type:"getDeviceInfo"},!0),this.config({record:!0})]);this.app=e,this.deviceInfo=t,this.ready=!0}async waitUntilReady(){if(!this.ready)return F(async()=>{if(!this.ready)throw new Error("Timed out waiting for client to be ready")},3e4)}async startSession(e){await this.waitUntilReady(),await this.config(e!=null?e:{});const[t]=await Promise.all([new Promise((s,n)=>{const r=c=>{this.off("session",r),this.off("sessionError",o),s(c)},o=c=>{this.off("session",r),this.off("sessionError",o),n(c)};this.on("session",r),this.on("sessionError",o)}),this.window.postMessage({type:"requestSession"},!0)]);return t}async config({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);return this._config=s,s}getConfig(){return this._config}getApp(){return this.app}getDeviceInfo(){return this.deviceInfo}mapConfig(e){return{...e,device:e.deviceType||e.device}}validateConfig(e){return e}createSession(e,t){throw new Error("Not implemented")}}class we extends C{constructor(){super(...arguments),this.logHistory=[],this.logLevel=process.env.CI==="true"?"warnings-errors":"verbose",this.log=(...e)=>{if(this.logHistory.push({method:"log",data:e}),this.logLevel==="verbose")return console.log(...e)},this.warn=(...e)=>{if(this.logHistory.push({method:"warn",data:e}),this.logLevel==="verbose"||this.logLevel==="warnings-errors")return console.warn(...e)},this.error=(...e)=>{if(this.logHistory.push({method:"error",data:e}),this.logLevel==="verbose"||this.logLevel==="warnings-errors")return console.error(...e)},this.debug=(...e)=>{if(this.logHistory.push({method:"debug",data:e}),this.logLevel==="verbose")return console.debug(...e)},this.clearLogHistory=()=>{this.logHistory=[]}}}const S=new we;async function fe(i){await i.pause()}function D(i){if(i.character)return i.character;const e=i.key;return R(e)?e:i.shiftKey?e.toUpperCase():e.toLowerCase()}function R(i){return/^[\b\t\r]/.test(i)}class ye{constructor({testInfo:e,session:t}){this.currentRecord=0,this.session=t,this.testInfo=e}async record(){const e=this.testInfo.file,t=await T.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=a=>{me(r,a),console.log(V(a))};this.session.on("action",o),await fe(this.session.page),await I(2e3),this.session.off("action",o);const c=t.split(` | ||
`).map((a,u)=>{var l,w;if(u===n-1){const f=(w=(l=a.match(/^\s*/))==null?void 0:l[0])!=null?w:0;return`${r.map(y=>V(y)).reduce((y,p,g)=>`${y} | ||
...and ${i.length-5} more`:""}`}async function F(i,e=5e3){const t=Date.now();for(;;)try{return await i()}catch(s){if(await new Promise(n=>setTimeout(n,100)),e!==null&&Date.now()-t>e)throw s}}async function I(i){return new Promise(e=>setTimeout(e,i))}async function A(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,c)=>{const a=u=>{(!n||n(u))&&(i.off(e,a),o(u))};i.on(e,a),r!==null&&setTimeout(()=>{i.off(e,a),c(new P(`Timeout ${r}ms exceeded while waiting for event "${e}"`))},r)})}function ie(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 ne(i,e){const t=ie(i),s=btoa(t);return`data:${e};base64,`+s}class re{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 d('x and y must be strings and in percentages (e.g. "50%")');if(!e.endsWith("%")||!t.endsWith("%"))throw new d('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 c;const t=(c=this.duration)!=null?c: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 a=(this.moves.length-1)*16;throw new Error(`Duration is too short for ${this.moves.length-1} moves, please set duration to at least ${a}ms`)}for(let a=0;a<this.moves.length-1;a++){const u=this.moves[a],l=this.moves[a+1],w=a===this.moves.length-2;for(let f=0;f<=r;f++){if(!w&&f===r)continue;const v=f/r,m=u.x+v*(l.x-u.x),p=u.y+v*(l.y-u.y),g=((a*r+f)*16+o)/1e3;n.push({x:m,y:p,t:g}),f===0&&u.wait&&(n.push({x:m,y:p,t:g+u.wait/1e3}),o+=u.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%")}}function x(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 oe{constructor({platform:e,screen:t}){this.platform=e,this.screen=t}pixelToDip(e){return e/(this.screen.devicePixelRatio||1)}dipToPixel(e){return e*(this.screen.devicePixelRatio||1)}getCoordinates(e,t){const s=x(e.x),n=x(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)),"coordinates"in e&&e.coordinates){if(!b.isValidNumber(e.coordinates.x)||!b.isValidNumber(e.coordinates.y))throw new d(`Invalid coordinates: (${e.coordinates.x}, ${e.coordinates.y}). Values must be a number`);if(!b.isCoordinatesWithinBounds(e.coordinates,{width:this.screen.width-1,height:this.screen.height-1}))throw new d(`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}else if("position"in e&&e.position){const o=x(e.position.x),c=x(e.position.y);if(!b.isValidNumber(o)||!b.isValidNumber(c))throw new d(`Invalid position: (${e.position.x}, ${e.position.y}). Values must be a number or a percentage`);if(!b.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})}if("localPosition"in e&&e.localPosition){const o=x(e.localPosition.x),c=x(e.localPosition.y);if(!b.isValidNumber(o)||!b.isValidNumber(c))throw new d(`Invalid localPosition: (${e.localPosition.x}, ${e.localPosition.y}). Values must be a number or a percentage`);r={x:o,y:c}}else s&&(r={x:.5,y:.5});if("duration"in e&&e.duration&&!b.isValidNumber(e.duration))throw new d(`Invalid duration: ${e.duration}. Value must be a number`);switch(e.type){case"tap":{const{position:o,...c}=e;return{...c,element:s,localPosition:r,coordinates:n}}case"swipe":{const{position:o,...c}=e;return{...c,element:s,localPosition:r,coordinates:n,moves:e.moves.map(a=>{if(this.platform==="android"){const{x:u,y:l}=this.getCoordinates(a,{width:this.dipToPixel(this.screen.width)-1,height:this.dipToPixel(this.screen.height)-1});return{...a,x:u,y:l}}else{const{x:u,y:l}=this.getCoordinates(a,{width:this.screen.width-1,height:this.screen.height-1});return{...a,x:u,y:l}}})}}case"keypress":{const o=this.mapHardwareKey(e.key),c=this.mapHardwareKey(e.character);return{...e,key:o,character:c,shiftKey:this.platform==="ios"?ce(e.shiftKey):e.shiftKey}}case"findElements":return{...e,element:s}}return e})())}mapElement(e){var c;const{attributes:t,bounds:s,...n}=e,r=()=>{if(s){const{x:a,y:u,width:l,height:w}=s;return this.platform==="android"?{x:this.dipToPixel(a),y:this.dipToPixel(u),width:this.dipToPixel(l),height:this.dipToPixel(w)}:{x:D(a),y:D(u),width:D(l),height:D(w)}}},o=()=>{if(t)return Object.keys(t).reduce((a,u)=>{if(this.platform==="ios")switch(u){case"userInteractionEnabled":case"isHidden":return{...a,[u]:t[u]?"1":"0"}}else this.platform;return{...a,[u]:t[u]}},{})};return k({...n,bounds:r(),attributes:o(),accessibilityElements:(c=e.accessibilityElements)==null?void 0:c.map(a=>this.mapElement({attributes:a}).attributes)})}}class ae{constructor({platform:e,screen:t}){this.platform=e,this.screen=t}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;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":{const{coordinates:c,...a}=e;return{...a,element:s,position:r,localPosition:o}}case"swipe":{const{coordinates:c,...a}=e;return{...a,element:s,position:r,localPosition:o,moves:e.moves.map(u=>{const{x:l,y:w}=this.getPosition({x:this.pixelToDip(u.x),y:this.pixelToDip(u.y)},{width:this.screen.width-1,height:this.screen.height-1});return{x:l,y:w,t:u.t}})}}case"keypress":{const c=this.mapHardwareKey(e.key),a=this.mapHardwareKey(e.character);return{...e,key:c,character:a,shiftKey:typeof e.shiftKey=="number"?ue(e.shiftKey):Boolean(e.shiftKey)}}case"findElements":return{...e,element:s}}return e})())}mapUI(e){const t=s=>{var n;return{...this.mapElement(s),children:(n=s.children)==null?void 0:n.map(t)}};return Object.keys(e).reduce((s,n)=>(s[n]=e[n].map(t),s),{})}mapElement(e){var c;const{attributes:t,bounds:s,...n}=e,r=()=>{if(s)return this.platform==="android"?{x:this.pixelToDip(s.x),y:this.pixelToDip(s.y),width:this.pixelToDip(s.width),height:this.pixelToDip(s.height)}:{x:C(s.x),y:C(s.y),width:C(s.width),height:C(s.height)}},o=()=>{if(t)return Object.keys(t).reduce((a,u)=>{switch(u){case"userInteractionEnabled":case"isHidden":return{...a,[u]:t[u]==="1"};default:return{...a,[u]:t[u]}}},{})};return k({...n,bounds:r(),attributes:o(),accessibilityElements:(c=e.accessibilityElements)==null?void 0:c.map(a=>this.mapElement({attributes:a}).attributes)})}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 b{static isCoordinatesWithinBounds(e,t){return!(e.x<0||e.x>t.width||e.y<0||e.y>t.height)}static isPositionWithinBounds(e){const t=x(e.x),s=x(e.y);return!(t<0||t>1||s<0||s>1)}static isValidNumber(e){return!(typeof e!="number"||isNaN(e))}}function ce(i){return i?1:0}function ue(i){return i===1}function C(i){return typeof i=="number"?i:i==="inf"?1/0:i==="-inf"?-1/0:parseFloat(i)}function D(i){return i===1/0?"inf":i===-1/0?"-inf":i}class O{constructor(){this.log=function(){const e="[Appetize]";return Function.prototype.bind.call(console.log,console,e)}(),this.warn=function(){const e="[Appetize]";return Function.prototype.bind.call(console.warn,console,e)}(),this.error=function(){const e="[Appetize]";return Function.prototype.bind.call(console.error,console,e)}(),this.debug=function(){const e="[Appetize]";return Function.prototype.bind.call(console.debug,console,e)}()}}function he(i){if(typeof i!="object"||Array.isArray(i))throw new Error("Element must be an object");const e=Object.keys(i),s=le(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 le(i,e){return i.filter(t=>e.includes(t))}class de extends E.EventEmitter{constructor({socket:e,config:t,deviceInfo:s,sessionInfo:n,logger:r=new O}){super(),this.isEndingManually=!1,this.countdownWarning=!1,this.ready=!1,this.actionLogs=[],this.config=t,this.socket=e,this.deviceInfo=s,this.path=n.path,this.token=n.token,this.logger=r;const o=new ae({platform:t.platform,screen:s.screen}),c=({type:a,value:u})=>{const l=o.mapAppetizerEvent(a,u);switch(a){case"ready":this.ready=!0;break;case"adbOverTcp":{this.adbConnectionInfo={...u,command:pe(u)};break}case"networkInspectorUrl":this.networkInspectorUrl=u;break;case"countdownWarning":this.countdownWarning=!0;break;case"timeoutReset":this.countdownWarning=!1;break}l?(this.emit(l.type,l.value),this.emit("*",l)):l!==null&&(this.emit(a,u),this.emit("*",{type:a,value:u}))};this.socket.on("*",c),this.on("disconnect",()=>{this.socket.off("*",c),this.isEndingManually||(this.countdownWarning?console.warn("Appetize session has ended due to inactivity"):console.warn("Session disconnected"))})}on(e,t){return e==="network"&&this.config.proxy!=="intercept"&&console.warn('Session must be configured with `proxy: "intercept"` to listen to network events.'),e==="log"&&this.config.debug!==!0&&console.warn("Session must be configured with `debug: true` to listen to log events."),e==="action"&&this.config.record!==!0&&console.warn("Session must configured with `record: true` to listen to action events."),super.on(e,t)}async waitUntilReady(){const e=async t=>new Promise(s=>{const n=setInterval(()=>{this[t]&&s(void 0)},10);setTimeout(()=>{clearInterval(n),s(void 0)},3e3)});await F(()=>{if(!this.ready)throw new P("Timed out after 180s waiting for session to be ready")},18e4),await Promise.all([e("networkInspectorUrl"),e("adbConnectionInfo")])}async waitForEvent(e,t){try{return await A(this,e,t)}catch(s){throw h(s,this.waitForEvent),s}}async end(){this.isEndingManually=!0,await this.socket.disconnect()}getConfig(){return this.config}getDeviceInfo(){return this.deviceInfo}getNetworkInspectorUrl(){try{if(this.config.proxy!=="intercept")throw new d('Session must be configured with `proxy: "intercept"` to use the network inspector');return this.networkInspectorUrl}catch(e){throw h(e,this.getNetworkInspectorUrl),e}}getAdbInfo(){try{if(this.config.platform&&this.config.platform!=="android")throw new d("Session must be connected to an Android device to use adb");if(!this.config.enableAdb)throw new d("Session must be configured with `enableAdb: true` to use adb");return this.adbConnectionInfo?this.adbConnectionInfo:F(()=>{if(this.adbConnectionInfo)return this.adbConnectionInfo;throw new P("Timed out after 60000ms waiting for adb connection")},6e4)}catch(e){throw h(e,this.getAdbInfo),e}}async rotate(e){try{const[t]=await Promise.all([this.waitForEvent("orientationChanged"),this.socket.send("userInteraction",{type:"keypress",key:e==="left"?"rotateLeft":"rotateRight",timeStamp:Date.now()})]);return t}catch(t){throw h(t,this.rotate),t}}async screenshot(e="buffer"){try{this.socket.send("getScreenshot");const t=await A(this.socket,"screenshot",{timeout:6e4});if(!t.success)throw new d("Screenshot failed");return{data:e==="buffer"?(r=>typeof window=="undefined"?Buffer.from(r):r)(t.data):ne(new Uint8Array(t.data),t.mimeType),mimeType:t.mimeType}}catch(t){throw h(t,this.screenshot),t}}async heartbeat(){try{return await this.socket.send("heartbeat")}catch(e){throw h(e,this.heartbeat),e}}async type(e){try{this.config.platform==="ios"&&await I(1e3);const t=await this.playAction({type:"typeText",text:e});return this.config.platform==="ios"&&await I(e.length*50),t}catch(t){throw h(t,this.type),t}}async keypress(e,t){try{if(e==="ANDROID_KEYCODE_MENU")return await this.socket.send("androidKeycodeMenu");if((t==null?void 0:t.shift)||e==="HOME"){switch(e){case"ArrowUp":e="arrowUp";break;case"ArrowDown":e="arrowDown";break;case"ArrowLeft":e="arrowLeft";break;case"ArrowRight":e="arrowRight";break;case"Enter":e="\r";break;case"Tab":e=" ";break;case"Backspace":e="\b";break}return this.playAction({type:"keypress",key:e,shiftKey:!!(t!=null&&t.shift)})}else return this.playAction({type:"keypress",character:e})}catch(s){throw h(s,this.keypress),s}}async setLanguage(e){try{return this.config.language=e,await this.socket.send("setLanguage",{language:e,timeStamp:Date.now()})}catch(t){throw h(t,this.setLanguage),t}}async setLocation(e,t){try{if(typeof e!="number"||typeof t!="number")throw new d("setLocation requires latitude and longitude to be numbers");const s=[e,t];return this.config.location=s,await this.socket.send("setLocation",{location:s,timeStamp:Date.now()})}catch(s){throw h(s,this.setLocation),s}}async openUrl(e){try{return await this.socket.send("openUrl",{url:e,timeStamp:Date.now()})}catch(t){throw h(t,this.openUrl),t}}async shake(){try{return await this.socket.send("shakeDevice")}catch(e){throw h(e,this.swipe),e}}async biometry({match:e}){try{return await this.socket.send(e?"biometryMatch":"biometryNoMatch")}catch(t){throw h(t,this.biometry),t}}async allowInteractions(e){try{return await this.socket.send(e?"enableInteractions":"disableInteractions")}catch(t){throw h(t,this.allowInteractions),t}}async restartApp(){try{this.socket.send("restartApp");const{platform:e}=this.config;e==="ios"?await this.waitForEvent("appLaunch",{timeout:6e4}):await I(1e3)}catch(e){throw h(e,this.restartApp),e}}async reinstallApp(){try{this.socket.send("reinstallApp"),await this.waitForEvent("appLaunch",{timeout:6e4})}catch(e){throw h(e,this.reinstallApp),e}}async adbShellCommand(e){if(this.config.platform!=="android")throw new Error("adbShellCommand is only available on Android devices");try{return await this.socket.send("adbShellCommand",{command:e,timeStamp:Date.now()})}catch(t){throw h(t,this.adbShellCommand),t}}async playAction(e,t={}){const{timeout:s=1e4}=t;try{if(!this.config.record)throw new $("playAction()");if(isNaN(s))throw new d(`Invalid timeout value: ${t.timeout}`);if(s<0)throw new d(`Timeout value cannot be negative: ${t.timeout}`);"element"in e&&e.element&&he(e.element);const n=new oe({platform:this.config.platform,screen:this.deviceInfo.screen}),r=G(),c=t.noMap?e:n.mapAction(e),a={id:r,action:c,options:{...t,timeout:Math.round(s/1e3)}},[u]=await Promise.all([new Promise((l,w)=>{const f=()=>{this.off("playbackFoundAndSent",v),this.off("playbackError",m)},v=async p=>{var g;((g=p.playback)==null?void 0:g.id)===r&&(await this.addActionLog({action:e,payload:a,result:p}),f(),l(p))},m=async p=>{var g;if(((g=p.playback)==null?void 0:g.id)===r)switch(await this.addActionLog({action:e,payload:a,error:p}),f(),p.errorId){case"internalError":w(new d(`An internal error has occurred for the action: | ||
${JSON.stringify(e,null,2)}`));break;case"notFound":if(p.playback!==void 0&&"element"in p.playback.action&&p.playback.action.element!==void 0){w(new q(p.playback.action.element));break}case"ambiguousMatch":if("matchedElements"in p&&p.matchedElements!==void 0){w(new ee(p.matchedElements));break}case"invalidArgument":if("element"in e&&e.element&&p.message.match("outside the screen bounds")){w(new te(e));break}default:w(new d(p.message));break}};this.once("playbackFoundAndSent",v),this.once("playbackError",m)}),this.socket.send("playAction",a)]);return u}catch(n){throw h(n,this.playAction),n}}async playActions(e,t={}){try{if(!this.config.record)throw new $("playActions()");const s=[];for(const n of e){const r=await this.playAction(n,t);s.push(r);const o=e[e.indexOf(n)+1];o&&o.type==="keypress"&&n.type==="keypress"||await I(1e3)}return s}catch(s){throw h(s,this.playActions),s}}async getUI({all:e,timeout:t=3e4}={}){var s;try{this.socket.send("dumpUi");const n=await A(this,"uiDump",{timeout:t});return e?n:(s=n==null?void 0:n.ui)!=null?s:n==null?void 0:n.result}catch(n){throw h(n,this.getUI),n}}async findElement(e,t){var s;try{const n=await this.playAction({type:"findElements",element:e,appId:t==null?void 0:t.appId},t);if((s=n.matchedElements)!=null&&s[0])return n.matchedElements[0];throw new q(e)}catch(n){throw h(n,this.findElement),n}}async findElements(e,t){try{return(await this.playAction({type:"findElements",element:e,appId:t==null?void 0:t.appId},t)).matchedElements}catch(s){throw h(s,this.findElements),s}}async tap(e,t){var s;try{if(!this.config.record)throw new $("tap()");return await this.playAction({type:"tap",...e,duration:((s=e.duration)!=null?s:0)/1e3},t)}catch(n){throw h(n,this.tap),n}}async swipe({duration:e,gesture:t,...s},n){try{if(!this.config.record)throw new $("swipe()");let r;const o=new re({duration:e});if(typeof t=="function")t(o);else switch(t){case"up":o.up();break;case"down":o.down();break;case"left":o.left();break;case"right":o.right();break}if("element"in s)r={type:"swipe",element:s.element,localPosition:s.localPosition,moves:o.build()};else if("position"in s)r={type:"swipe",position:s.position,moves:o.build()};else throw new Error("Either element or position must be specified");return this.playAction(r,n)}catch(r){throw h(r,this.swipe),r}}async addActionLog(e){this.actionLogs.push(e)}}function pe(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}U.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 we extends E.EventEmitter{constructor({socket:e,logger:t=new O}){super(),this.logger=t,this.socket=e,this.socket.on("*",({type:s,value:n})=>{const r=fe(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 config(e){throw new Error("Not implemented")}async waitForSessionStart(e){return new Promise(async(t,s)=>{const n=()=>{s(new Error("Session disconnected before it was ready"))},r=c=>{s(new Error(`Session failed to start - ${typeof c.message=="object"?JSON.stringify(c.message):c.message}`))},o=c=>{c.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 fe(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 me extends we{constructor({socket:e,window:t,logger:s=new O}){super({socket:e,logger:s}),this.ready=!1,this.window=t,this.window.on("*",async({type:n,value:r})=>{switch(n){case"app":this.app=r,this.emit(n,r);break;case"deviceInfo":this.deviceInfo=r,this.emit(n,r);break;case"config":this._config=this.mapConfig(r);break}}),this.socket.on("*",async({type:n,value:r})=>{switch(n){case"newSession":{try{this.session=this.createSession(this._config,{path:r.path,token:r.sessionToken}),await this.waitForSessionStart(this.session)}catch(o){this.emit("sessionError",o)}this.emit("session",this.session)}}}),this.init()}async init(){await this.window.waitUntilReady();const[e,t]=await Promise.all([this.window.postMessage({type:"getApp"},!0),this.window.postMessage({type:"getDeviceInfo"},!0),this.config({record:!0})]);this.app=e,this.deviceInfo=t,this.ready=!0}async waitUntilReady(){if(!this.ready)return F(async()=>{if(!this.ready)throw new Error("Timed out waiting for client to be ready")},3e4)}async startSession(e){await this.waitUntilReady(),await this.config(e!=null?e:{});const[t]=await Promise.all([new Promise((s,n)=>{const r=c=>{this.off("session",r),this.off("sessionError",o),s(c)},o=c=>{this.off("session",r),this.off("sessionError",o),n(c)};this.on("session",r),this.on("sessionError",o)}),this.window.postMessage({type:"requestSession"},!0)]);return t}async config({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);return this._config=s,s}getConfig(){return this._config}getApp(){return this.app}getDeviceInfo(){return this.deviceInfo}mapConfig(e){return{...e,device:e.deviceType||e.device}}validateConfig(e){return e}createSession(e,t){throw new Error("Not implemented")}}class ye extends O{constructor(){super(...arguments),this.logHistory=[],this.logLevel=process.env.CI==="true"?"warnings-errors":"verbose",this.log=(...e)=>{if(this.logHistory.push({method:"log",data:e}),this.logLevel==="verbose")return console.log(...e)},this.warn=(...e)=>{if(this.logHistory.push({method:"warn",data:e}),this.logLevel==="verbose"||this.logLevel==="warnings-errors")return console.warn(...e)},this.error=(...e)=>{if(this.logHistory.push({method:"error",data:e}),this.logLevel==="verbose"||this.logLevel==="warnings-errors")return console.error(...e)},this.debug=(...e)=>{if(this.logHistory.push({method:"debug",data:e}),this.logLevel==="verbose")return console.debug(...e)},this.clearLogHistory=()=>{this.logHistory=[]}}}const S=new ye;async function ge(i){await i.pause()}function R(i){if(i.character)return i.character;const e=i.key;return M(e)?e:i.shiftKey?e.toUpperCase():e.toLowerCase()}function M(i){return/^[\b\t\r]/.test(i)}class be{constructor({testInfo:e,session:t}){this.currentRecord=0,this.session=t,this.testInfo=e}async record(){const e=this.testInfo.file,t=await T.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=a=>{ve(r,a),console.log(z(a))};this.session.on("action",o),await ge(this.session.page),await I(2e3),this.session.off("action",o);const c=t.split(` | ||
`).map((a,u)=>{var l,w;if(u===n-1){const f=(w=(l=a.match(/^\s*/))==null?void 0:l[0])!=null?w:0;return`${r.map(m=>z(m)).reduce((m,p,g)=>`${m} | ||
// ${g+1}. ${p}`,"// Recorded using session.record()")} | ||
await session.playActions(${JSON.stringify(r,null," ")})`.split(` | ||
`).map(y=>f+y).join(` | ||
`).map(m=>f+m).join(` | ||
`)}return a});await T.default.promises.writeFile(e,c.join(` | ||
`)),console.log("\u{1F7E2} Finished"),this.currentRecord+=1}}}function me(i,e){const t=i[i.length-1];if(t)switch(e.type){case"keypress":{(t==null?void 0:t.type)==="keypress"&&!R(e.key)&&!R(t.key)?(i.pop(),i.push({type:"typeText",text:D(t)+D(e)})):(t==null?void 0:t.type)==="typeText"&&!R(e.key)?(i.pop(),i.push({type:"typeText",text:t.text+D(e)})):i.push(e);break}default:i.push(e)}else i.push(e)}function V(i){var t,s,n,r,o,c;let e="";switch(i.type){case"swipe":case"tap":{const a=i.element;return typeof a=="string"?e=` on element "${a}"`:(t=a==null?void 0:a.attributes)!=null&&t.accessibilityIdentifier?e=`element with accessibilityIdentifier "${(s=a.attributes)==null?void 0:s.accessibilityIdentifier}"`:(n=a==null?void 0:a.attributes)!=null&&n.class?e=`element with class "${(r=a.attributes)==null?void 0:r.class}"`:"position"in i&&((o=i.position)==null?void 0:o.x)&&((c=i.position)==null?void 0:c.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 "${D(i)}"`;case"typeText":return`type "${i.text}"`}}function ge({type:i,value:e}){switch(i){case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}const be="0.4.0-beta.4";class ve extends E.EventEmitter{constructor({page:e}){super(),this.ready=!1,this.page=e,this.page.exposeFunction("__appetize_on",t=>{const s=t==="string"?t:t.type;this.emit(s,t.value),this.emit("*",{type:s,value:t.value})})}async init(){this.ready=!1,await this.page.evaluate(async([e])=>new Promise((t,s)=>{const n=setTimeout(()=>{clearInterval(r),s(new P("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(c,a=!1)=>{const u=new MessageChannel;if(window.postMessage(c,"*",[u.port2]),a)return new Promise((l,w)=>{const f=setTimeout(()=>{w(new P("Timed out after 60000ms while waiting for postMessage response"))},6e4);u.port1.onmessage=v=>{clearTimeout(f),l(v.data)}})}},100)}),[be]),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),!window.__appetize_emit_video)return;break;case"audioData":if(window.__appetize__audio_frames.push(e.data.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 F(async()=>{if(!this.ready)throw new P("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 q extends E.EventEmitter{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":r.socket===this.type&&(this.emit(r.type,r.value),this.emit("*",{type:r.type,value:r.value}));break;case"disconnect":this.emit("disconnect"),this.emit("*",{type:"disconnect"});break;case"sessionInfo":case"chromeDevToolsUrl":case"orientationChanged":if(this.type==="appetizer"){const o=ge({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 A(this,e,t)}}class Ee extends ue{constructor({page:e,config:t,window:s,testInfo:n,deviceInfo:r,sessionInfo:o}){const c=new q({page:e,window:s,type:"appetizer"});super({socket:c,config:t,deviceInfo:r,sessionInfo:o,logger:S}),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){e.error&&(e.ui=await this.getUI({all:!0})),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([A(this.window,"orientationChanged"),await this.window.postMessage(e==="left"?"rotateLeft":"rotateRight")]);return this.window.page.waitForTimeout(1e3),t}catch(t){throw h(t,this.rotate),t}}async screenshot(e="buffer"){try{const[t]=await Promise.all([A(this.socket,"screenshot",{timeout:6e4}),this.socket.send("getScreenshot")]),s=new Uint8Array(Object.values(t.data).map(Number));return{data:e==="buffer"?Buffer.from(s):t.data,mimeType:t.mimeType}}catch(t){throw h(t,this.screenshot),t}}async record(){try{if(!this.config.record)throw new d("Recording is not enabled, please enable it by setting `record: true` in session config");if(!this.testInfo)throw new d("session.record() requires using `session` from the test() arguments");return new ye({session:this,testInfo:this.testInfo}).record()}catch(e){throw h(e,this.record),e}}async waitForEvent(e,t){try{return await A(this,e,t)}catch(s){throw h(s,this.waitForEvent),s}}async waitForTimeout(e){try{return await I(e)}catch(t){throw h(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 h(s,this.waitForElement),s}}on(e,t){return e==="video"&&(M.onVideo||(this.logger.warn("Listening to the `video` event will significantly slow down your tests. It is recommended to use session.getVideoFrames() instead."),M.onVideo=!0),this.window.enableVideoEvents()),e==="audio"&&(M.onAudio||(this.logger.warn("Listening to the `audio` event will significantly slow down your tests. It is recommended to use session.getAudioFrames() instead."),M.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()}async getAudioFrames(){return await this.window.getAudioFrames()}}const M={onVideo:!1,onAudio:!1},z=new WeakMap;class xe extends pe{constructor({page:e}){var r;const t=(r=z.get(e))!=null?r:new ve({page:e});z.set(e,t),t.init();const s=new q({type:"webserver",page:e,window:t});super({socket:s,window:t,logger:S}),this.window=t,this.page=e;let n=!1;this.on("queue",({type:o,position:c})=>{n||(n=!0,o==="account"?this.logger.log("All slots for this account are currently in use. Please wait until a slot becomes available."):this.logger.log("All devices are currently in use. Please wait until requested device becomes available.")),c>0&&(o==="account"?this.logger.log(`Position in account-level queue: ${c}`):this.logger.log(`Position in queue: ${c}`))}),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 Ee({config:e,page:this.page,window:this.window,sessionInfo:t,deviceInfo:this.deviceInfo}),this.session}}class ke{constructor(e){this.queue=null,this.page=e}async start(e){var r,o;const{config:t,baseURL:s}=e,n=new URLSearchParams;t.device&&n.set("device",t.device),t.deviceColor&&n.set("deviceColor",t.deviceColor),t.screenOnly&&n.set("screenOnly",t.screenOnly.toString()),n.set("scale",(o=(r=t.scale)==null?void 0:r.toString())!=null?o:"auto"),await this.page.goto(`${s!=null?s:"https://appetize.io"}/embed/${t.publicKey}?${n.toString()}`),this.client=new xe({page:this.page}),this.client.on("queue",c=>{this.queue=c}),await this.client.waitUntilReady(),this.session=await Y(()=>this.client.startSession({...t}),{retries:5,timeout:3e4,predicate:(c,a)=>c instanceof Error&&c.message.match(/too many requests/)?(console.warn(`Too many session requests. Retrying in 30 seconds... (attempt #${a})`),!0):!1})}}let _,K;const O=L.test.extend({_useConfig:[{},{option:!0}],config:[async({_useConfig:i},e,t)=>{await e({...t.project.use.config,...i,autoplay:!1})},{auto:!0}],_autoSnapshotSuffix:[async({},i,e)=>{e.snapshotSuffix="",await i()},{auto:!0}],appetizePage:async({browser:i,config:e},t)=>{if(_&&JSON.stringify(K)!==JSON.stringify(e)&&(await _.page.close(),await _.page.context().close(),_=null),!_){const s=await i.newContext();if(_=new ke(await s.newPage()),K=e,!e.publicKey)throw new Error('Appetize public key not set. Make sure you have run test.use({ config: { publicKey: "..." } })')}await t(_)},session:async({appetizePage:i,config:e,baseURL:t,_doSetupAndTeardown:s},n,r)=>{if(i.session||await N.step("Start Appetize session",async()=>{await i.start({config:e,baseURL:t})}),!i.session)throw new Error("Appetize session failed to start");i.session.testInfo=r,await n(i.session)},client:async({appetizePage:i},e)=>{await e(i.client)},_doSetupAndTeardown:[async({appetizePage:i},e,t)=>{var s,n;S.clearLogHistory(),i.session&&(i.session.actionLogs=[],await i.session.window.resetVideoFrames(),await i.session.window.resetAudioFrames()),await e(),await j(i,t),t.status==="timedOut"&&((n=(s=i.queue)==null?void 0:s.position)!=null?n:0)>0&&S.warn("Test timed out while in queue for a device. You may increase your test timeout to account for higher queue times.")},{auto:!0}]}),Ae=O.use,H=O.afterEach;Object.assign(O,{setup(i){return{}.__APPETIZE__SETUP_WARNED||S.warn("test.setup() is deprecated and will be removed in a future release. Use test.use({ config: {...} }) instead"),N.use({config:i})},use(i){const{config:e,...t}=i;return Ae({...t,_useConfig:e})},afterEach(i){return H(async({appetizePage:e},t)=>{e&&await j(e,t)}),H(i)}});const N=O,W=new WeakMap;async function j(i,e){if(W.get(e.fn))return;W.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 T.default.promises.mkdir(e.outputPath(s));const c=async()=>{try{const u=await t.screenshot("buffer");e.attach("screenshot",{body:u.data,contentType:"image/png"});const l=e.outputPath(s,"screenshot.png");await T.default.promises.writeFile(l,Buffer.from(u.data))}catch{}},a=async()=>{try{const u=o.map(f=>{if("screenshot"in f){const{screenshot:v,...y}=f;return y}return f});if(!u.length)return;const l=JSON.stringify(u,null,2);e.attach("actions",{body:l,contentType:"application/json"});const w=e.outputPath(s,"actions.json");await T.default.promises.writeFile(w,l)}catch{}};await Promise.all([c(),a()])}await(async()=>{if(!!S.logHistory.length)try{const o=`${S.logHistory.map(a=>`[${a.method}] ${JSON.stringify(a.data).slice(2,-2)}`).join(` | ||
`)}`;e.attach("sdk-logs",{body:o,contentType:"application/text"});const c=e.outputPath(s,"sdk-logs.txt");await T.default.promises.writeFile(c,o)}catch{}})()}}Object.defineProperty(m,"expect",{enumerable:!0,get:function(){return L.expect}}),m.test=N,Object.defineProperties(m,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
`)),console.log("\u{1F7E2} Finished"),this.currentRecord+=1}}}function ve(i,e){const t=i[i.length-1];if(t)switch(e.type){case"keypress":{(t==null?void 0:t.type)==="keypress"&&!M(e.key)&&!M(t.key)?(i.pop(),i.push({type:"typeText",text:R(t)+R(e)})):(t==null?void 0:t.type)==="typeText"&&!M(e.key)?(i.pop(),i.push({type:"typeText",text:t.text+R(e)})):i.push(e);break}default:i.push(e)}else i.push(e)}function z(i){var t,s,n,r,o,c;let e="";switch(i.type){case"swipe":case"tap":{const a=i.element;return typeof a=="string"?e=` on element "${a}"`:(t=a==null?void 0:a.attributes)!=null&&t.accessibilityIdentifier?e=`element with accessibilityIdentifier "${(s=a.attributes)==null?void 0:s.accessibilityIdentifier}"`:(n=a==null?void 0:a.attributes)!=null&&n.class?e=`element with class "${(r=a.attributes)==null?void 0:r.class}"`:"position"in i&&((o=i.position)==null?void 0:o.x)&&((c=i.position)==null?void 0:c.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 "${R(i)}"`;case"typeText":return`type "${i.text}"`}}function Ee({type:i,value:e}){switch(i){case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}const xe="0.4.0-beta.5";class ke extends E.EventEmitter{constructor({page:e}){super(),this.ready=!1,this.page=e,this.page.exposeFunction("__appetize_on",t=>{const s=t==="string"?t:t.type;this.emit(s,t.value),this.emit("*",{type:s,value:t.value})})}async init(){this.ready=!1,await this.page.evaluate(async([e])=>new Promise((t,s)=>{const n=setTimeout(()=>{clearInterval(r),s(new P("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(c,a=!1)=>{const u=new MessageChannel;if(window.postMessage(c,"*",[u.port2]),a)return new Promise((l,w)=>{const f=setTimeout(()=>{w(new P("Timed out after 60000ms while waiting for postMessage response"))},6e4);u.port1.onmessage=v=>{clearTimeout(f),l(v.data)}})}},100)}),[xe]),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),!window.__appetize_emit_video)return;break;case"audioData":if(window.__appetize__audio_frames.push(e.data.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 F(async()=>{if(!this.ready)throw new P("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 K extends E.EventEmitter{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":r.socket===this.type&&(this.emit(r.type,r.value),this.emit("*",{type:r.type,value:r.value}));break;case"disconnect":this.emit("disconnect"),this.emit("*",{type:"disconnect"});break;case"sessionInfo":case"chromeDevToolsUrl":case"orientationChanged":if(this.type==="appetizer"){const o=Ee({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 A(this,e,t)}}class Ae extends de{constructor({page:e,config:t,window:s,testInfo:n,deviceInfo:r,sessionInfo:o}){const c=new K({page:e,window:s,type:"appetizer"});super({socket:c,config:t,deviceInfo:r,sessionInfo:o,logger:S}),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){e.error&&(e.ui=await this.getUI({all:!0})),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([A(this.window,"orientationChanged"),await this.window.postMessage(e==="left"?"rotateLeft":"rotateRight")]);return this.window.page.waitForTimeout(1e3),t}catch(t){throw h(t,this.rotate),t}}async screenshot(e="buffer"){try{const[t]=await Promise.all([A(this.socket,"screenshot",{timeout:6e4}),this.socket.send("getScreenshot")]),s=new Uint8Array(Object.values(t.data).map(Number));return{data:e==="buffer"?Buffer.from(s):t.data,mimeType:t.mimeType}}catch(t){throw h(t,this.screenshot),t}}async record(){try{if(!this.config.record)throw new d("Recording is not enabled, please enable it by setting `record: true` in session config");if(!this.testInfo)throw new d("session.record() requires using `session` from the test() arguments");return new be({session:this,testInfo:this.testInfo}).record()}catch(e){throw h(e,this.record),e}}async waitForEvent(e,t){try{return await A(this,e,t)}catch(s){throw h(s,this.waitForEvent),s}}async waitForTimeout(e){try{return await I(e)}catch(t){throw h(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 h(s,this.waitForElement),s}}on(e,t){return e==="video"&&(N.onVideo||(this.logger.warn("Listening to the `video` event will significantly slow down your tests. It is recommended to use session.getVideoFrames() instead."),N.onVideo=!0),this.window.enableVideoEvents()),e==="audio"&&(N.onAudio||(this.logger.warn("Listening to the `audio` event will significantly slow down your tests. It is recommended to use session.getAudioFrames() instead."),N.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()}async getAudioFrames(){return await this.window.getAudioFrames()}}const N={onVideo:!1,onAudio:!1},H=new WeakMap;class Se extends me{constructor({page:e}){var r;const t=(r=H.get(e))!=null?r:new ke({page:e});H.set(e,t),t.init();const s=new K({type:"webserver",page:e,window:t});super({socket:s,window:t,logger:S}),this.window=t,this.page=e;let n=!1;this.on("queue",({type:o,position:c})=>{n||(n=!0,o==="account"?this.logger.log("All slots for this account are currently in use. Please wait until a slot becomes available."):this.logger.log("All devices are currently in use. Please wait until requested device becomes available.")),c>0&&(o==="account"?this.logger.log(`Position in account-level queue: ${c}`):this.logger.log(`Position in queue: ${c}`))}),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 Ae({config:e,page:this.page,window:this.window,sessionInfo:t,deviceInfo:this.deviceInfo}),this.session}}class _e{constructor(e){this.queue=null,this.page=e}async start(e){var r,o;const{config:t,baseURL:s}=e,n=new URLSearchParams;t.device&&n.set("device",t.device),t.deviceColor&&n.set("deviceColor",t.deviceColor),t.screenOnly&&n.set("screenOnly",t.screenOnly.toString()),n.set("scale",(o=(r=t.scale)==null?void 0:r.toString())!=null?o:"auto"),await this.page.goto(`${s!=null?s:"https://appetize.io"}/embed/${t.publicKey}?${n.toString()}`),this.client=new Se({page:this.page}),this.client.on("queue",c=>{this.queue=c}),await this.client.waitUntilReady(),this.session=await X(()=>this.client.startSession({...t}),{retries:5,timeout:3e4,predicate:(c,a)=>c instanceof Error&&c.message.match(/too many requests/)?(console.warn(`Too many session requests. Retrying in 30 seconds... (attempt #${a})`),!0):!1})}}let _,W;const L=U.test.extend({_useConfig:[{},{option:!0}],config:[async({_useConfig:i},e,t)=>{await e({...t.project.use.config,...i,autoplay:!1})},{auto:!0}],_autoSnapshotSuffix:[async({},i,e)=>{e.snapshotSuffix="",await i()},{auto:!0}],appetizePage:async({browser:i,config:e},t)=>{if(_&&JSON.stringify(W)!==JSON.stringify(e)&&(await _.page.close(),await _.page.context().close(),_=null),!_){const s=await i.newContext();if(_=new _e(await s.newPage()),W=e,!e.publicKey)throw new Error('Appetize public key not set. Make sure you have run test.use({ config: { publicKey: "..." } })')}await t(_)},session:async({appetizePage:i,config:e,baseURL:t,_doSetupAndTeardown:s},n,r)=>{if(i.session||await V.step("Start Appetize session",async()=>{await i.start({config:e,baseURL:t})}),!i.session)throw new Error("Appetize session failed to start");i.session.testInfo=r,await n(i.session)},client:async({appetizePage:i},e)=>{await e(i.client)},_doSetupAndTeardown:[async({appetizePage:i},e,t)=>{var s,n;S.clearLogHistory(),i.session&&(i.session.actionLogs=[],await i.session.window.resetVideoFrames(),await i.session.window.resetAudioFrames()),await e(),await J(i,t),t.status==="timedOut"&&((n=(s=i.queue)==null?void 0:s.position)!=null?n:0)>0&&S.warn("Test timed out while in queue for a device. You may increase your test timeout to account for higher queue times.")},{auto:!0}]}),Te=L.use,j=L.afterEach;Object.assign(L,{setup(i){return{}.__APPETIZE__SETUP_WARNED||S.warn("test.setup() is deprecated and will be removed in a future release. Use test.use({ config: {...} }) instead"),V.use({config:i})},use(i){const{config:e,...t}=i;return Te({...t,_useConfig:e})},afterEach(i){return j(async({appetizePage:e},t)=>{e&&await J(e,t)}),j(i)}});const V=L,B=new WeakMap;async function J(i,e){if(B.get(e.fn))return;B.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 T.default.promises.mkdir(e.outputPath(s));const c=async()=>{try{const u=await t.screenshot("buffer");e.attach("screenshot",{body:u.data,contentType:"image/png"});const l=e.outputPath(s,"screenshot.png");await T.default.promises.writeFile(l,Buffer.from(u.data))}catch{}},a=async()=>{try{const u=o.map(f=>{if("screenshot"in f){const{screenshot:v,...m}=f;return m}return f});if(!u.length)return;const l=JSON.stringify(u,null,2);e.attach("actions",{body:l,contentType:"application/json"});const w=e.outputPath(s,"actions.json");await T.default.promises.writeFile(w,l)}catch{}};await Promise.all([c(),a()])}await(async()=>{if(!!S.logHistory.length)try{const o=`${S.logHistory.map(a=>`[${a.method}] ${JSON.stringify(a.data).slice(2,-2)}`).join(` | ||
`)}`;e.attach("sdk-logs",{body:o,contentType:"application/text"});const c=e.outputPath(s,"sdk-logs.txt");await T.default.promises.writeFile(c,o)}catch{}})()}}Object.defineProperty(y,"expect",{enumerable:!0,get:function(){return U.expect}}),y.test=V,Object.defineProperties(y,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
//# sourceMappingURL=index.umd.js.map |
{ | ||
"name": "@appetize/playwright", | ||
"version": "0.4.0-beta.4", | ||
"version": "0.4.0-beta.5", | ||
"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
543519
4128