@appetize/playwright
Advanced tools
Comparing version 0.3.0 to 0.3.1
@@ -16,3 +16,4 @@ /// <reference types="node" /> | ||
private networkInspectorUrl?; | ||
private isEnding; | ||
private isEndingManually; | ||
private countdownWarning; | ||
constructor({ socket, config, deviceInfo, }: { | ||
@@ -19,0 +20,0 @@ socket: SocketProtocol; |
@@ -544,3 +544,4 @@ import { EventEmitter } from "events"; | ||
this.actionHistory = []; | ||
this.isEnding = false; | ||
this.isEndingManually = false; | ||
this.countdownWarning = false; | ||
this.debug = { | ||
@@ -596,2 +597,8 @@ printActions: ({ xdoc = false } = {}) => { | ||
break; | ||
case "countdownWarning": | ||
this.countdownWarning = true; | ||
break; | ||
case "timeoutReset": | ||
this.countdownWarning = false; | ||
break; | ||
} | ||
@@ -609,4 +616,10 @@ if (converted) { | ||
this.socket.off("*", handleSocketEvent); | ||
if (!this.isEnding) { | ||
throw new Error(`Session disconnected unexpectedly`); | ||
if (!this.isEndingManually) { | ||
if (this.countdownWarning) { | ||
console.warn( | ||
`Appetize session has ended due to inactivity` | ||
); | ||
} else { | ||
console.warn(`Session disconnected`); | ||
} | ||
} | ||
@@ -642,3 +655,3 @@ }); | ||
async end() { | ||
this.isEnding = true; | ||
this.isEndingManually = true; | ||
await this.socket.disconnect(); | ||
@@ -1203,5 +1216,2 @@ } | ||
}); | ||
this.socket.on("disconnect", () => { | ||
throw new Error("Client disconnected unexpectedly"); | ||
}); | ||
} | ||
@@ -1252,3 +1262,3 @@ on(event, listener) { | ||
} | ||
const version = "0.3.0"; | ||
const version = "0.3.1"; | ||
const VERSION = version; | ||
@@ -1255,0 +1265,0 @@ class HeadfulClient extends Client { |
@@ -1,2 +0,2 @@ | ||
(function(p,y){typeof exports=="object"&&typeof module!="undefined"?y(exports,require("events"),require("@playwright/test"),require("fs")):typeof define=="function"&&define.amd?define(["exports","events","@playwright/test","fs"],y):(p=typeof globalThis!="undefined"?globalThis:p||self,y(p.playwright={},p.events,p.test$1,p.fs))})(this,function(p,y,R,W){"use strict";function j(n){return n&&typeof n=="object"&&"default"in n?n:{default:n}}var $=j(W);async function H(n,{retries:e=3,timeout:t=1e3,predicate:s=()=>!0}){for(let i=1;i<=e;i++)try{return await n()}catch(r){if(i===e||!s(r,i))throw r;await new Promise(o=>setTimeout(o,t))}throw null}function U(n,e){switch(n){case"accountQueuedPosition":case"accountQueue":return{type:"queue",value:{type:"account",position:e.position}};case"sessionQueuedPosition":case"queue":return{type:"queue",value:{type:"session",position:e.position}};case"debug":return{type:"log",value:e};case"interceptResponse":return{type:"network",value:{type:"response",...e}};case"interceptRequest":return{type:"network",value:{type:"request",...e}};case"interceptError":return{type:"network",value:{type:"error",...e}};case"userError":return{type:"error",value:e};case"recordedEvent":return e.type==="scrollToElement"?null:{type:"action",value:e};case"userInteractionReceived":return{type:"interaction",value:e};case"countdownWarning":return{type:"inactivityWarning",value:e};case"h264Data":return{type:"video",value:{...e,codec:"h264"}};case"frameData":return{type:"video",value:{...e,codec:"jpeg"}};case"audioData":return{type:"audio",value:{...e,codec:"aac"}};case"deleteEvent":return null;case"newSession":return null}}function D(n){return typeof n=="object"&&n!==null?Object.entries(n).reduce((e,[t,s])=>{const i=D(s);return i!=null&&(e[t]=i),e},{}):n}function J(n,e){if("captureStackTrace"in Error)Error.captureStackTrace(n,e);else{const t=new Error;Object.defineProperty(n,"stack",{configurable:!0,get(){const{stack:s}=t;return Object.defineProperty(this,"stack",{value:s}),s}})}}async function h(n,e){n instanceof f&&J(n,e)}class f extends Error{constructor(e){super(e),this.name=this.constructor.name,this.isOperational=!0,Error.captureStackTrace(this,this.constructor)}}class Y extends f{constructor(e){var i,r,o;const t=(i=e.playback)==null?void 0:i.event;let s=(r=e.message)!=null?r:e.errorId;switch(e.errorId){case"notFound":if(t&&"element"in t){let a=!1;const c=typeof t.element=="object"?{...t.element}:t.element;typeof c=="object"&&"allowMultipleMatches"in c&&(a=!!c.allowMultipleMatches,delete c.allowMultipleMatches),s=`No element${a?"s":""} found for selector | ||
(function(p,w){typeof exports=="object"&&typeof module!="undefined"?w(exports,require("events"),require("@playwright/test"),require("fs")):typeof define=="function"&&define.amd?define(["exports","events","@playwright/test","fs"],w):(p=typeof globalThis!="undefined"?globalThis:p||self,w(p.playwright={},p.events,p.test$1,p.fs))})(this,function(p,w,R,q){"use strict";function j(n){return n&&typeof n=="object"&&"default"in n?n:{default:n}}var $=j(q);async function H(n,{retries:e=3,timeout:t=1e3,predicate:s=()=>!0}){for(let i=1;i<=e;i++)try{return await n()}catch(r){if(i===e||!s(r,i))throw r;await new Promise(o=>setTimeout(o,t))}throw null}function U(n,e){switch(n){case"accountQueuedPosition":case"accountQueue":return{type:"queue",value:{type:"account",position:e.position}};case"sessionQueuedPosition":case"queue":return{type:"queue",value:{type:"session",position:e.position}};case"debug":return{type:"log",value:e};case"interceptResponse":return{type:"network",value:{type:"response",...e}};case"interceptRequest":return{type:"network",value:{type:"request",...e}};case"interceptError":return{type:"network",value:{type:"error",...e}};case"userError":return{type:"error",value:e};case"recordedEvent":return e.type==="scrollToElement"?null:{type:"action",value:e};case"userInteractionReceived":return{type:"interaction",value:e};case"countdownWarning":return{type:"inactivityWarning",value:e};case"h264Data":return{type:"video",value:{...e,codec:"h264"}};case"frameData":return{type:"video",value:{...e,codec:"jpeg"}};case"audioData":return{type:"audio",value:{...e,codec:"aac"}};case"deleteEvent":return null;case"newSession":return null}}function D(n){return typeof n=="object"&&n!==null?Object.entries(n).reduce((e,[t,s])=>{const i=D(s);return i!=null&&(e[t]=i),e},{}):n}function J(n,e){if("captureStackTrace"in Error)Error.captureStackTrace(n,e);else{const t=new Error;Object.defineProperty(n,"stack",{configurable:!0,get(){const{stack:s}=t;return Object.defineProperty(this,"stack",{value:s}),s}})}}async function h(n,e){n instanceof f&&J(n,e)}class f extends Error{constructor(e){super(e),this.name=this.constructor.name,this.isOperational=!0,Error.captureStackTrace(this,this.constructor)}}class Y extends f{constructor(e){var i,r,o;const t=(i=e.playback)==null?void 0:i.event;let s=(r=e.message)!=null?r:e.errorId;switch(e.errorId){case"notFound":if(t&&"element"in t){let a=!1;const c=typeof t.element=="object"?{...t.element}:t.element;typeof c=="object"&&"allowMultipleMatches"in c&&(a=!!c.allowMultipleMatches,delete c.allowMultipleMatches),s=`No element${a?"s":""} found for selector | ||
${JSON.stringify(c,null," ")}`}break;case"ambiguousMatch":{const a=(o=e.matchedElements)==null?void 0:o.map(({frame:c,address:l,frameInWindow:u,bounds:d,windowType:g,...A})=>A);a&&(s=`Selector matched more than 1 element. Please specify more attributes to match a single element, or provide a \`matchIndex\` attribute to choose an element below. | ||
@@ -6,3 +6,3 @@ | ||
`)}async function F(n,e=5e3){const t=Date.now();for(;;)try{return await n()}catch(s){if(await new Promise(i=>setTimeout(i,100)),e!==null&&Date.now()-t>e)throw s}}async function T(n){return new Promise(e=>setTimeout(e,n))}async function b(n,e,t){const s=typeof t=="function"?{}:t,i=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=l=>{(!i||i(l))&&(n.off(e,c),o(l))};n.on(e,c),r!==null&&setTimeout(()=>{n.off(e,c),a(new S(`Timeout ${r}ms exceeded while waiting for event "${e}"`))},r)})}const N=[")","!","@","#","$","%","^","&","*","("],K={47:"?",44:"<",45:"_",46:">",91:"{",92:"|",93:"}",96:"~",59:":",61:"+",39:'"'},Q={191:"?",188:"<",189:"_",190:">",219:"{",220:"|",221:"}",192:"~",186:":",187:"+",222:'"'};function X(n){let e;for(const s in K)if(n===K[s])return e={key:String.fromCharCode(s),shiftKey:"true"},e;const t=N.indexOf(n);return t>-1?e={key:String.fromCharCode(t+48).toLowerCase(),shiftKey:!0}:n!==n.toLowerCase()?e={key:n.toLowerCase(),shiftKey:!0}:e={key:n,shiftKey:!1},e}function C(n){return n.type==="keypress"&&n.which&&n.key?n.which>=65&&n.which<=90?n.shiftKey?n.key:n.key.toLowerCase():n.shiftKey?n.which>=48&&n.which<=57?N[n.which-48]:Q[n.which]:n.key:null}function G(n){switch(n){case"HOME":return"home";case"VOLUME_UP":return"volumeUp";case"VOLUME_DOWN":return"volumeDown"}return n}class z{constructor(e,{duration:t=500,...s}){var i,r;this.path=[],this.easing=o=>o,this.session=e,this.element=s.element,s!=null&&s.easing&&(typeof s.easing=="string"?this.easing=Z[s.easing]:this.easing=s.easing),this.steps=Math.floor(t/16),this.stepDuration=t/this.steps,this.move((i=s==null?void 0:s.x)!=null?i:0,(r=s==null?void 0:s.y)!=null?r:0)}move(e,t){var c;const s=(c=this.previousCommand(this.path,"move"))!=null?c:{x:0,y:0},i=this.parseValue(s.x,"x"),r=this.parseValue(s.y,"y"),o=this.parseValue(e,"x"),a=this.parseValue(t,"y");return this.path.push({type:"move",x:i+o,y:r+a}),this}wait(e){return this.path.push({type:"wait",value:e}),this}toAction(){const e=this.interpolatePath();return{type:"swipe",xPos:e.map(t=>t.x.toString()),yPos:e.map(t=>t.y.toString()),ts:e.map((t,s)=>this.easing(s*(this.stepDuration/1e3))),element:D(this.element)}}interpolatePath(){const e=[];for(let t=0;t<this.path.length-1;t++){const i=this.previousCommand(this.path.slice(0,t+1),"move"),r=this.path[t+1];if(i){if(r.type==="move"){const o=this.parseValue(r.x,"x"),a=this.parseValue(r.y,"y"),c=Math.floor(this.steps/this.path.length-1);if(c<1)e.push({x:o,y:a});else{const l=(o-this.parseValue(i.x,"x"))/c,u=(a-this.parseValue(i.y,"y"))/c;for(let d=0;d<=c;d++){const g=this.parseValue(i.x,"x")+l*d,A=this.parseValue(i.y,"y")+u*d;e.push({x:g,y:A})}}}else if(r.type==="wait"){const o=Math.floor(r.value/this.stepDuration);for(let a=0;a<=o;a++)e.push({x:i.x,y:i.y})}}}return e.length&&(e.unshift(e[0]),e.push(e[e.length-1])),e}previousCommand(e,t){return e.slice().reverse().find(s=>s.type===t)}parseValue(e,t){var o;const s=(o=this.session.deviceInfo.screen.devicePixelRatio)!=null?o:1,i=this.session.deviceInfo.screen.width*s,r=this.session.deviceInfo.screen.height*s;if(typeof e=="number")return e*s;if(e.endsWith("px"))return parseInt(e)*s;if(e.endsWith("%")){const a=parseInt(e)/100,c=this.element?this.getElementBounds(this.element):{width:i,height:r};return a*(t==="x"?c.width:c.height)}if(parseInt(e)===0)return 0;throw new Error(`Invalid value: ${e}. Must be a number or string that ends with 'px' or '%'.`)}getElementBounds(e){return e.bounds?{x:e.bounds.left,y:e.bounds.top,width:e.bounds.right-e.bounds.left,height:e.bounds.bottom-e.bounds.top}:{x:e.frameInWindow.x,y:e.frameInWindow.y,width:e.frameInWindow.width,height:e.frameInWindow.height}}}const Z={linear:n=>n,easeIn:n=>n*n,easeOut:n=>n*(2-n),easeInOut:n=>n<.5?2*n*n:-1+(4-2*n)*n};function ee(n){const e=n.length;let t="";for(let s=0;s<e;s+=65535){let i=65535;s+65535>e&&(i=e-s),t+=String.fromCharCode.apply(null,n.subarray(s,s+i))}return t}function te(n,e){const t=ee(n),s=btoa(t);return`data:${e};base64,`+s}class se extends y.EventEmitter{constructor({socket:e,config:t,deviceInfo:s}){super(),this.actionHistory=[],this.isEnding=!1,this.debug={printActions:({xdoc:r=!1}={})=>{console.log(this.actionHistory.reduce((o,{action:a})=>r?`let actions = ${JSON.stringify(this.actionHistory.map(c=>c.action),null,2)} | ||
`)}async function F(n,e=5e3){const t=Date.now();for(;;)try{return await n()}catch(s){if(await new Promise(i=>setTimeout(i,100)),e!==null&&Date.now()-t>e)throw s}}async function T(n){return new Promise(e=>setTimeout(e,n))}async function b(n,e,t){const s=typeof t=="function"?{}:t,i=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=l=>{(!i||i(l))&&(n.off(e,c),o(l))};n.on(e,c),r!==null&&setTimeout(()=>{n.off(e,c),a(new S(`Timeout ${r}ms exceeded while waiting for event "${e}"`))},r)})}const N=[")","!","@","#","$","%","^","&","*","("],K={47:"?",44:"<",45:"_",46:">",91:"{",92:"|",93:"}",96:"~",59:":",61:"+",39:'"'},Q={191:"?",188:"<",189:"_",190:">",219:"{",220:"|",221:"}",192:"~",186:":",187:"+",222:'"'};function X(n){let e;for(const s in K)if(n===K[s])return e={key:String.fromCharCode(s),shiftKey:"true"},e;const t=N.indexOf(n);return t>-1?e={key:String.fromCharCode(t+48).toLowerCase(),shiftKey:!0}:n!==n.toLowerCase()?e={key:n.toLowerCase(),shiftKey:!0}:e={key:n,shiftKey:!1},e}function C(n){return n.type==="keypress"&&n.which&&n.key?n.which>=65&&n.which<=90?n.shiftKey?n.key:n.key.toLowerCase():n.shiftKey?n.which>=48&&n.which<=57?N[n.which-48]:Q[n.which]:n.key:null}function G(n){switch(n){case"HOME":return"home";case"VOLUME_UP":return"volumeUp";case"VOLUME_DOWN":return"volumeDown"}return n}class W{constructor(e,{duration:t=500,...s}){var i,r;this.path=[],this.easing=o=>o,this.session=e,this.element=s.element,s!=null&&s.easing&&(typeof s.easing=="string"?this.easing=Z[s.easing]:this.easing=s.easing),this.steps=Math.floor(t/16),this.stepDuration=t/this.steps,this.move((i=s==null?void 0:s.x)!=null?i:0,(r=s==null?void 0:s.y)!=null?r:0)}move(e,t){var c;const s=(c=this.previousCommand(this.path,"move"))!=null?c:{x:0,y:0},i=this.parseValue(s.x,"x"),r=this.parseValue(s.y,"y"),o=this.parseValue(e,"x"),a=this.parseValue(t,"y");return this.path.push({type:"move",x:i+o,y:r+a}),this}wait(e){return this.path.push({type:"wait",value:e}),this}toAction(){const e=this.interpolatePath();return{type:"swipe",xPos:e.map(t=>t.x.toString()),yPos:e.map(t=>t.y.toString()),ts:e.map((t,s)=>this.easing(s*(this.stepDuration/1e3))),element:D(this.element)}}interpolatePath(){const e=[];for(let t=0;t<this.path.length-1;t++){const i=this.previousCommand(this.path.slice(0,t+1),"move"),r=this.path[t+1];if(i){if(r.type==="move"){const o=this.parseValue(r.x,"x"),a=this.parseValue(r.y,"y"),c=Math.floor(this.steps/this.path.length-1);if(c<1)e.push({x:o,y:a});else{const l=(o-this.parseValue(i.x,"x"))/c,u=(a-this.parseValue(i.y,"y"))/c;for(let d=0;d<=c;d++){const g=this.parseValue(i.x,"x")+l*d,A=this.parseValue(i.y,"y")+u*d;e.push({x:g,y:A})}}}else if(r.type==="wait"){const o=Math.floor(r.value/this.stepDuration);for(let a=0;a<=o;a++)e.push({x:i.x,y:i.y})}}}return e.length&&(e.unshift(e[0]),e.push(e[e.length-1])),e}previousCommand(e,t){return e.slice().reverse().find(s=>s.type===t)}parseValue(e,t){var o;const s=(o=this.session.deviceInfo.screen.devicePixelRatio)!=null?o:1,i=this.session.deviceInfo.screen.width*s,r=this.session.deviceInfo.screen.height*s;if(typeof e=="number")return e*s;if(e.endsWith("px"))return parseInt(e)*s;if(e.endsWith("%")){const a=parseInt(e)/100,c=this.element?this.getElementBounds(this.element):{width:i,height:r};return a*(t==="x"?c.width:c.height)}if(parseInt(e)===0)return 0;throw new Error(`Invalid value: ${e}. Must be a number or string that ends with 'px' or '%'.`)}getElementBounds(e){return e.bounds?{x:e.bounds.left,y:e.bounds.top,width:e.bounds.right-e.bounds.left,height:e.bounds.bottom-e.bounds.top}:{x:e.frameInWindow.x,y:e.frameInWindow.y,width:e.frameInWindow.width,height:e.frameInWindow.height}}}const Z={linear:n=>n,easeIn:n=>n*n,easeOut:n=>n*(2-n),easeInOut:n=>n<.5?2*n*n:-1+(4-2*n)*n};function ee(n){const e=n.length;let t="";for(let s=0;s<e;s+=65535){let i=65535;s+65535>e&&(i=e-s),t+=String.fromCharCode.apply(null,n.subarray(s,s+i))}return t}function te(n,e){const t=ee(n),s=btoa(t);return`data:${e};base64,`+s}class se extends w.EventEmitter{constructor({socket:e,config:t,deviceInfo:s}){super(),this.actionHistory=[],this.isEndingManually=!1,this.countdownWarning=!1,this.debug={printActions:({xdoc:r=!1}={})=>{console.log(this.actionHistory.reduce((o,{action:a})=>r?`let actions = ${JSON.stringify(this.actionHistory.map(c=>c.action),null,2)} | ||
@@ -25,3 +25,3 @@ let nextAction = actions.shift() | ||
`:" "+o+` | ||
`+JSON.stringify(a,null,2),""))}},this.config=t,this.socket=e,this.deviceInfo=s;const i=({type:r,value:o})=>{const a=U(r,o);switch(r){case"adbOverTcp":{this.adbConnectionInfo={...o,command:ne(o)};break}case"networkInspectorUrl":this.networkInspectorUrl=o;break}a?(this.emit(a.type,a.value),this.emit("*",a)):a!==null&&(this.emit(r,o),this.emit("*",{type:r,value:o}))};this.socket.on("*",i),this.on("disconnect",()=>{if(this.socket.off("*",i),!this.isEnding)throw new Error("Session disconnected unexpectedly")})}on(e,t){return e==="network"&&this.config.proxy!=="intercept"&&console.warn(`Session must be configured to use a proxy in order to listen to network events. You can do this with | ||
`+JSON.stringify(a,null,2),""))}},this.config=t,this.socket=e,this.deviceInfo=s;const i=({type:r,value:o})=>{const a=U(r,o);switch(r){case"adbOverTcp":{this.adbConnectionInfo={...o,command:ne(o)};break}case"networkInspectorUrl":this.networkInspectorUrl=o;break;case"countdownWarning":this.countdownWarning=!0;break;case"timeoutReset":this.countdownWarning=!1;break}a?(this.emit(a.type,a.value),this.emit("*",a)):a!==null&&(this.emit(r,o),this.emit("*",{type:r,value:o}))};this.socket.on("*",i),this.on("disconnect",()=>{this.socket.off("*",i),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 to use a proxy in order to listen to network events. You can do this with | ||
@@ -32,9 +32,9 @@ startSession({ proxy: "intercept" })`),e==="log"&&this.config.debug!==!0&&console.warn(`Session must be configured to use debug mode in order to listen to log events. You can do this with | ||
startSession({ record: true })`),super.on(e,t)}async waitForEvent(e,t){try{return await b(this,e,t)}catch(s){throw h(s,this.waitForEvent),s}}async end(){this.isEnding=!0,await this.socket.disconnect()}async getNetworkInspectorUrl(){try{if(this.config.proxy!=="intercept")throw new f(`Session must be configured to use a proxy in order to get the network inspector URL. You can do this with | ||
startSession({ record: true })`),super.on(e,t)}async waitForEvent(e,t){try{return await b(this,e,t)}catch(s){throw h(s,this.waitForEvent),s}}async end(){this.isEndingManually=!0,await this.socket.disconnect()}async getNetworkInspectorUrl(){try{if(this.config.proxy!=="intercept")throw new f(`Session must be configured to use a proxy in order to get the network inspector URL. You can do this with | ||
startSession({ proxy: "intercept" })`);return this.networkInspectorUrl?this.networkInspectorUrl:F(()=>{if(this.networkInspectorUrl)return this.networkInspectorUrl;throw new S("Timed out after 60000ms waiting for network inspector URL")},6e4)}catch(e){throw h(e,this.getNetworkInspectorUrl),e}}async getAdbInfo(){try{if(this.config.platform&&this.config.platform!=="android")throw new f("Session must be connected to an Android device to use adb");if(!this.config.enableAdb)throw new f(`Session must be configured to use adb in order to get the adb command. You can do this with | ||
startSession({ enableAdb: true })`);return this.adbConnectionInfo?this.adbConnectionInfo:F(()=>{if(this.adbConnectionInfo)return this.adbConnectionInfo;throw new S("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 b(this.socket,"screenshot",{timeout:6e4});if(!t.success)throw new f("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 T(1e3);const t=[...e].map(X);if(this.config.record)return await this.playAction({type:"keypress",keypress:{type:"keypressArray",shiftKeyArray:t.map(s=>s.shiftKey),keyArray:t.map(s=>s.key),value:e}});await Promise.all([this.socket.send("userInteraction",{type:"keypressArray",shiftKeyArray:t.map(s=>s.shiftKey),keyArray:t.map(s=>s.key),value:e}),this.waitForEvent("interaction",{predicate:s=>s.type==="keypressArray"})])}catch(t){throw h(t,this.type),t}}async keypress(e,t){var s,i,r,o;try{if(e==="ANDROID_KEYCODE_MENU")return await this.socket.send("androidKeycodeMenu");e=G(e);const a=Date.now();if(this.config.record)return this.playAction({type:"keypress",keypress:{type:"keypress",key:e,timeStamp:a,altKey:(s=t==null?void 0:t.alt)!=null?s:!1,shiftKey:(i=t==null?void 0:t.shift)!=null?i:!1}});{const[c]=await Promise.all([this.waitForEvent("interaction",{predicate:l=>l.type==="keypress"&&l.timeStamp===a}),this.socket.send("userInteraction",{type:"keypress",key:e,timeStamp:a,altKey:(r=t==null?void 0:t.alt)!=null?r:!1,shiftKey:(o=t==null?void 0:t.shift)!=null?o:!1})]);return c}}catch(a){throw h(a,this.keypress),a}}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{const s=[e.toString(),t.toString()];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 T(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={}){try{if(!this.config.record)throw new _("playAction()");return await this.playActions([e],t).then(s=>s[0])}catch(s){throw h(s,this.playAction),s}}async playActions(e,t={}){try{const{timeout:s=3e4,strictMatch:i}=t,r=[];if(!this.config.record)throw new _("playActions()");for(const o of e){const{id:a,...c}=o;let l=c.type,u="element"in c?c.element:void 0;switch(typeof u=="object"&&(u=D(u)),l){case"tap":l="click";break}const[d]=await Promise.all([new Promise((g,A)=>{const v=()=>{this.off("playbackFoundAndSent",M),this.off("playbackError",P)},M=async x=>{this.actionHistory.push({action:o,result:x}),v(),g(x)},P=x=>{this.actionHistory.push({action:o,error:x}),v(),A(new Y(x))};this.once("playbackFoundAndSent",M),this.once("playbackError",P)}),this.socket.send("playEvent",{event:{...c,element:u,type:l},timeout:Math.round(s/1e3),strictMatch:i,id:a})]);r.push(d)}return r}catch(s){throw h(s,this.playActions),s}}async getUI({timeout:e=3e4}={}){try{this.socket.send("dumpUi");const t=await b(this.socket,"uiDump",{timeout:e});return"ui"in t?t.ui:t.result}catch(t){throw h(t,this.getUI),t}}async findElement(e,{timeout:t,...s}={}){try{const i=await this.playAction({type:"assert",element:e,bundleId:s==null?void 0:s.bundleId},{timeout:t});return i.matchedElements?i.matchedElements[0]:i.matchedElement}catch(i){throw h(i,this.findElement),i}}async findElements(e,t){try{const s=await this.playAction({type:"assert",element:{...e,allowMultipleMatches:!0},bundleId:t==null?void 0:t.bundleId},{timeout:t==null?void 0:t.timeout});return s.matchedElements?s.matchedElements:s.matchedElement?[s.matchedElement]:[]}catch(s){throw h(s,this.findElements),s}}async tap(e,t){try{if("element"in e){if(!this.config.record)throw new _('"element" selector');return await this.playAction({type:"click",element:e.element,bundleId:e.bundleId},{timeout:t==null?void 0:t.timeout})}await this.socket.send("userInteraction",{type:"mousedown",timeStamp:Date.now(),xPos:e.x,yPos:e.y}).then(()=>{this.socket.send("userInteraction",{type:"mouseup",timeStamp:Date.now(),xPos:e.x,yPos:e.y})}),await this.waitForEvent("interaction",{predicate:s=>s.type==="mouseup",timeout:t==null?void 0:t.timeout})}catch(s){throw h(s,this.tap),s}}async swipe(e,t){var s,i;try{if(!this.config.record)throw new _("swipe()");let r;const o=e.element?await this.findElement(e.element):void 0;if(typeof e.gesture=="function"){const a=new z(this,{...e,element:o});e.gesture(a),r=a.toAction()}else{const a=new z(this,{...e,element:o}),c=this.deviceInfo.screen.width*((s=this.deviceInfo.screen.devicePixelRatio)!=null?s:1),l=this.deviceInfo.screen.height*((i=this.deviceInfo.screen.devicePixelRatio)!=null?i:1);switch(e.gesture){case"up":case"down":{const u=l*.5;if(typeof u=="number")a.move(0,u*(e.gesture==="up"?-1:1));else{const d=e.gesture==="up"?"-":"";a.move(0,d+u)}break}case"left":case"right":{const u=c*.5;if(typeof u=="number")a.move(u*(e.gesture==="left"?-1:1),0);else{const d=e.gesture==="left"?"-":"";a.move(d+u,0)}break}}r=a.toAction()}return this.playAction({...r,bundleId:e.bundleId},{timeout:t==null?void 0:t.timeout})}catch(r){throw h(r,this.swipe),r}}}function ne(n){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(!n||!n.forwards[0])return;let t=e;return t=t.replace(/SERVER_PORT/,n.port.toString()),t=t.replace(/USERNAME/,n.user),t=t.replace(/HOSTNAME/,n.hostname),t=t.replace(/FORWARD_DESTINATION/,n.forwards[0].destination),t=t.replace(/FORWARD_PORT/,n.forwards[0].port.toString()),t}const w=function(){const n="[Appetize]";return Function.prototype.bind.call(console.log,console,n)}();class ie extends y.EventEmitter{constructor({socket:e}){super(),this.socket=e,this.socket.on("*",({type:t,value:s})=>{const i=U(t,s);if(i)this.emit(i.type,i.value),this.emit("*",i);else if(i!==null)switch(t){default:this.emit(t,s),this.emit("*",{type:t,value:s})}}),this.socket.on("disconnect",()=>{throw new Error("Client disconnected unexpectedly")})}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 i=()=>{s(new Error("Session failed to start - client disconnected"))},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 - too many requests"))};try{this.on("error",o),e.on("disconnect",i),e.on("error",r),await e.waitForEvent("ready",{timeout:null})}finally{this.off("error",o),e.off("disconnect",i),e.off("error",r)}t(e)})}}const O="0.3.0";class re extends ie{constructor({socket:e,window:t}){super({socket:e}),this.window=t,this.window.on("*",async({type:s,value:i})=>{switch(s){case"app":this.app=i,this.emit(s,i);break;case"deviceInfo":this.deviceInfo=i,this.emit(s,i);break;case"config":this._config=this.mapConfig(i);break;case"sessionConnecting":{this.session||(this.session=await this.createSession(this._config));try{await this.waitForSessionStart(this.session),this.emit("session",this.session)}catch{}}}}),this.window.waitUntilReady().then(()=>{this.config({record:!0,apiVersion:O})}),this.getDeviceInfo()}async startSession(e,t){this.deviceInfo||await this.getDeviceInfo();const s=await this.config(e!=null?e:{}),i=await this.createSession(s,t);return await Promise.all([this.window.postMessage({type:"requestSession"},!0),this.waitForSessionStart(i)]),i}async config({publicKey:e,...t}){if(e){const i=await this.window.postMessage({type:"loadApp",value:e},!0);if(i&&"error"in i)throw new Error(i.error)}const s=await this.window.postMessage({type:"setConfig",value:this.validateConfig(t!=null?t:{})},!0).then(this.mapConfig);return this._config=s,s}mapConfig(e){return{...e,device:e.deviceType||e.device}}getDeviceInfo(){return this.window.postMessage({type:"getDeviceInfo"},!0).then(e=>{this.deviceInfo=e})}validateConfig(e){return e}async createSession(e,t){throw new Error("Not implemented")}}function oe({type:n,value:e}){switch(n){case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}class ae extends y.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=>{const s=setTimeout(()=>{throw new S("Timed out after 60000ms waiting for connection to Appetize page")},6e4),i=setInterval(()=>{const r=new MessageChannel;r.port1.onmessage=()=>{clearInterval(i),clearTimeout(s),t(!1)},window.postMessage({type:"init",appetizeClient:!0,version:e},"*",[r.port2]),window.__appetize_postMessage=async(o,a=!1)=>{const c=new MessageChannel;if(window.postMessage(o,"*",[c.port2]),a)return new Promise((l,u)=>{const d=setTimeout(()=>{u(new S("Timed out after 60000ms while waiting for postMessage response"))},6e4);c.port1.onmessage=g=>{clearTimeout(d),l(g.data)}})}},100)}),[O]),await this.page.evaluate(()=>{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_emit_video)return;break;case"audioData":if(!window.__appetize_emit_audio)return;break;case"recordedEvent":if(!window.__appetize_emit_record)return;break}else switch(s){case"frameData":case"recordedEvent":case"playbackFoundAndSent":case"playbackNotFound":case"deleteEvent":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 S("Timed out after 60000ms while waiting for Appetize window to be ready.")},6e4)}enableVideoEvents(){return this.page.evaluate(()=>{window.__appetize_emit_video=!0})}enableAudioEvents(){return this.page.evaluate(()=>{window.__appetize_emit_audio=!0})}enableRecordEvents(){return this.page.evaluate(()=>{window.__appetize_emit_record=!0})}async postMessage(e,t=!1){return await this.waitUntilReady(),this.page.evaluate(async([s,i])=>window.__appetize_postMessage(s,i),[e,t])}}class L extends y.EventEmitter{constructor({page:e,type:t,window:s}){super(),this.page=e,this.type=t,this.window=s,this.window.on("*",({type:i,value:r})=>{switch(i){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=oe({type:i,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 b(this,e,t)}}async function ce(n){await n.pause()}class he{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"),i=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(i!==void 0){w(`\u{1F534} Recording at line ${i}`);const r=[],o=c=>{ue(r,c),w(V(c))};this.session.on("action",o),await ce(this.session.page),await T(2e3),this.session.off("action",o);const a=t.split(` | ||
`).map((c,l)=>{var u,d;if(l===i-1){const g=(d=(u=c.match(/^\s*/))==null?void 0:u[0])!=null?d:0;return`${r.map(v=>V(v)).reduce((v,M,P)=>`${v} | ||
startSession({ enableAdb: true })`);return this.adbConnectionInfo?this.adbConnectionInfo:F(()=>{if(this.adbConnectionInfo)return this.adbConnectionInfo;throw new S("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 b(this.socket,"screenshot",{timeout:6e4});if(!t.success)throw new f("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 T(1e3);const t=[...e].map(X);if(this.config.record)return await this.playAction({type:"keypress",keypress:{type:"keypressArray",shiftKeyArray:t.map(s=>s.shiftKey),keyArray:t.map(s=>s.key),value:e}});await Promise.all([this.socket.send("userInteraction",{type:"keypressArray",shiftKeyArray:t.map(s=>s.shiftKey),keyArray:t.map(s=>s.key),value:e}),this.waitForEvent("interaction",{predicate:s=>s.type==="keypressArray"})])}catch(t){throw h(t,this.type),t}}async keypress(e,t){var s,i,r,o;try{if(e==="ANDROID_KEYCODE_MENU")return await this.socket.send("androidKeycodeMenu");e=G(e);const a=Date.now();if(this.config.record)return this.playAction({type:"keypress",keypress:{type:"keypress",key:e,timeStamp:a,altKey:(s=t==null?void 0:t.alt)!=null?s:!1,shiftKey:(i=t==null?void 0:t.shift)!=null?i:!1}});{const[c]=await Promise.all([this.waitForEvent("interaction",{predicate:l=>l.type==="keypress"&&l.timeStamp===a}),this.socket.send("userInteraction",{type:"keypress",key:e,timeStamp:a,altKey:(r=t==null?void 0:t.alt)!=null?r:!1,shiftKey:(o=t==null?void 0:t.shift)!=null?o:!1})]);return c}}catch(a){throw h(a,this.keypress),a}}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{const s=[e.toString(),t.toString()];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 T(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={}){try{if(!this.config.record)throw new _("playAction()");return await this.playActions([e],t).then(s=>s[0])}catch(s){throw h(s,this.playAction),s}}async playActions(e,t={}){try{const{timeout:s=3e4,strictMatch:i}=t,r=[];if(!this.config.record)throw new _("playActions()");for(const o of e){const{id:a,...c}=o;let l=c.type,u="element"in c?c.element:void 0;switch(typeof u=="object"&&(u=D(u)),l){case"tap":l="click";break}const[d]=await Promise.all([new Promise((g,A)=>{const v=()=>{this.off("playbackFoundAndSent",M),this.off("playbackError",P)},M=async x=>{this.actionHistory.push({action:o,result:x}),v(),g(x)},P=x=>{this.actionHistory.push({action:o,error:x}),v(),A(new Y(x))};this.once("playbackFoundAndSent",M),this.once("playbackError",P)}),this.socket.send("playEvent",{event:{...c,element:u,type:l},timeout:Math.round(s/1e3),strictMatch:i,id:a})]);r.push(d)}return r}catch(s){throw h(s,this.playActions),s}}async getUI({timeout:e=3e4}={}){try{this.socket.send("dumpUi");const t=await b(this.socket,"uiDump",{timeout:e});return"ui"in t?t.ui:t.result}catch(t){throw h(t,this.getUI),t}}async findElement(e,{timeout:t,...s}={}){try{const i=await this.playAction({type:"assert",element:e,bundleId:s==null?void 0:s.bundleId},{timeout:t});return i.matchedElements?i.matchedElements[0]:i.matchedElement}catch(i){throw h(i,this.findElement),i}}async findElements(e,t){try{const s=await this.playAction({type:"assert",element:{...e,allowMultipleMatches:!0},bundleId:t==null?void 0:t.bundleId},{timeout:t==null?void 0:t.timeout});return s.matchedElements?s.matchedElements:s.matchedElement?[s.matchedElement]:[]}catch(s){throw h(s,this.findElements),s}}async tap(e,t){try{if("element"in e){if(!this.config.record)throw new _('"element" selector');return await this.playAction({type:"click",element:e.element,bundleId:e.bundleId},{timeout:t==null?void 0:t.timeout})}await this.socket.send("userInteraction",{type:"mousedown",timeStamp:Date.now(),xPos:e.x,yPos:e.y}).then(()=>{this.socket.send("userInteraction",{type:"mouseup",timeStamp:Date.now(),xPos:e.x,yPos:e.y})}),await this.waitForEvent("interaction",{predicate:s=>s.type==="mouseup",timeout:t==null?void 0:t.timeout})}catch(s){throw h(s,this.tap),s}}async swipe(e,t){var s,i;try{if(!this.config.record)throw new _("swipe()");let r;const o=e.element?await this.findElement(e.element):void 0;if(typeof e.gesture=="function"){const a=new W(this,{...e,element:o});e.gesture(a),r=a.toAction()}else{const a=new W(this,{...e,element:o}),c=this.deviceInfo.screen.width*((s=this.deviceInfo.screen.devicePixelRatio)!=null?s:1),l=this.deviceInfo.screen.height*((i=this.deviceInfo.screen.devicePixelRatio)!=null?i:1);switch(e.gesture){case"up":case"down":{const u=l*.5;if(typeof u=="number")a.move(0,u*(e.gesture==="up"?-1:1));else{const d=e.gesture==="up"?"-":"";a.move(0,d+u)}break}case"left":case"right":{const u=c*.5;if(typeof u=="number")a.move(u*(e.gesture==="left"?-1:1),0);else{const d=e.gesture==="left"?"-":"";a.move(d+u,0)}break}}r=a.toAction()}return this.playAction({...r,bundleId:e.bundleId},{timeout:t==null?void 0:t.timeout})}catch(r){throw h(r,this.swipe),r}}}function ne(n){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(!n||!n.forwards[0])return;let t=e;return t=t.replace(/SERVER_PORT/,n.port.toString()),t=t.replace(/USERNAME/,n.user),t=t.replace(/HOSTNAME/,n.hostname),t=t.replace(/FORWARD_DESTINATION/,n.forwards[0].destination),t=t.replace(/FORWARD_PORT/,n.forwards[0].port.toString()),t}const y=function(){const n="[Appetize]";return Function.prototype.bind.call(console.log,console,n)}();class ie extends w.EventEmitter{constructor({socket:e}){super(),this.socket=e,this.socket.on("*",({type:t,value:s})=>{const i=U(t,s);if(i)this.emit(i.type,i.value),this.emit("*",i);else if(i!==null)switch(t){default:this.emit(t,s),this.emit("*",{type:t,value:s})}})}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 i=()=>{s(new Error("Session failed to start - client disconnected"))},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 - too many requests"))};try{this.on("error",o),e.on("disconnect",i),e.on("error",r),await e.waitForEvent("ready",{timeout:null})}finally{this.off("error",o),e.off("disconnect",i),e.off("error",r)}t(e)})}}const O="0.3.1";class re extends ie{constructor({socket:e,window:t}){super({socket:e}),this.window=t,this.window.on("*",async({type:s,value:i})=>{switch(s){case"app":this.app=i,this.emit(s,i);break;case"deviceInfo":this.deviceInfo=i,this.emit(s,i);break;case"config":this._config=this.mapConfig(i);break;case"sessionConnecting":{this.session||(this.session=await this.createSession(this._config));try{await this.waitForSessionStart(this.session),this.emit("session",this.session)}catch{}}}}),this.window.waitUntilReady().then(()=>{this.config({record:!0,apiVersion:O})}),this.getDeviceInfo()}async startSession(e,t){this.deviceInfo||await this.getDeviceInfo();const s=await this.config(e!=null?e:{}),i=await this.createSession(s,t);return await Promise.all([this.window.postMessage({type:"requestSession"},!0),this.waitForSessionStart(i)]),i}async config({publicKey:e,...t}){if(e){const i=await this.window.postMessage({type:"loadApp",value:e},!0);if(i&&"error"in i)throw new Error(i.error)}const s=await this.window.postMessage({type:"setConfig",value:this.validateConfig(t!=null?t:{})},!0).then(this.mapConfig);return this._config=s,s}mapConfig(e){return{...e,device:e.deviceType||e.device}}getDeviceInfo(){return this.window.postMessage({type:"getDeviceInfo"},!0).then(e=>{this.deviceInfo=e})}validateConfig(e){return e}async createSession(e,t){throw new Error("Not implemented")}}function oe({type:n,value:e}){switch(n){case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}class ae extends w.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=>{const s=setTimeout(()=>{throw new S("Timed out after 60000ms waiting for connection to Appetize page")},6e4),i=setInterval(()=>{const r=new MessageChannel;r.port1.onmessage=()=>{clearInterval(i),clearTimeout(s),t(!1)},window.postMessage({type:"init",appetizeClient:!0,version:e},"*",[r.port2]),window.__appetize_postMessage=async(o,a=!1)=>{const c=new MessageChannel;if(window.postMessage(o,"*",[c.port2]),a)return new Promise((l,u)=>{const d=setTimeout(()=>{u(new S("Timed out after 60000ms while waiting for postMessage response"))},6e4);c.port1.onmessage=g=>{clearTimeout(d),l(g.data)}})}},100)}),[O]),await this.page.evaluate(()=>{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_emit_video)return;break;case"audioData":if(!window.__appetize_emit_audio)return;break;case"recordedEvent":if(!window.__appetize_emit_record)return;break}else switch(s){case"frameData":case"recordedEvent":case"playbackFoundAndSent":case"playbackNotFound":case"deleteEvent":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 S("Timed out after 60000ms while waiting for Appetize window to be ready.")},6e4)}enableVideoEvents(){return this.page.evaluate(()=>{window.__appetize_emit_video=!0})}enableAudioEvents(){return this.page.evaluate(()=>{window.__appetize_emit_audio=!0})}enableRecordEvents(){return this.page.evaluate(()=>{window.__appetize_emit_record=!0})}async postMessage(e,t=!1){return await this.waitUntilReady(),this.page.evaluate(async([s,i])=>window.__appetize_postMessage(s,i),[e,t])}}class z extends w.EventEmitter{constructor({page:e,type:t,window:s}){super(),this.page=e,this.type=t,this.window=s,this.window.on("*",({type:i,value:r})=>{switch(i){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=oe({type:i,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 b(this,e,t)}}async function ce(n){await n.pause()}class he{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"),i=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(i!==void 0){y(`\u{1F534} Recording at line ${i}`);const r=[],o=c=>{ue(r,c),y(L(c))};this.session.on("action",o),await ce(this.session.page),await T(2e3),this.session.off("action",o);const a=t.split(` | ||
`).map((c,l)=>{var u,d;if(l===i-1){const g=(d=(u=c.match(/^\s*/))==null?void 0:u[0])!=null?d:0;return`${r.map(v=>L(v)).reduce((v,M,P)=>`${v} | ||
// ${P+1}. ${M}`,"// Recorded using session.record()")} | ||
@@ -44,5 +44,5 @@ await session.playActions(${JSON.stringify(r,null," ")})`.split(` | ||
`)}return c});await $.default.promises.writeFile(e,a.join(` | ||
`)),w("\u{1F7E2} Finished"),this.currentRecord+=1}}}function ue(n,e){const t=n[n.length-1];if(t)switch(e.type){case"keypress":(t==null?void 0:t.type)==="keypress"&&e.type==="keypress"&&e.keypress.type==="keypress"?t.keypress.type==="keypress"?n[n.length-1]={...t,keypress:{type:"keypressArray",shiftKeyArray:[t.keypress.shiftKey,e.keypress.shiftKey],keyArray:[t.keypress.key,e.keypress.key],value:C(t.keypress)+C(e.keypress)}}:n[n.length-1]={...t,keypress:{type:"keypressArray",shiftKeyArray:[...t.keypress.shiftKeyArray,e.keypress.shiftKey],keyArray:[...t.keypress.keyArray,e.keypress.key],value:t.keypress.value+C(e.keypress)}}:n.push(e);break;default:n.push(e)}else n.push(e)}function V(n){let e="";switch(n.type){case"swipe":case"click":{const t=n.element;return typeof t=="string"?e=` on element "${t}"`:t!=null&&t.accessibilityIdentifier?e=`element with accessibilityIdentifier "${t.accessibilityIdentifier}"`:t!=null&&t.class?e=`element with class "${t.class}"`:n.xPos&&n.yPos&&(e=`position ${n.xPos}, ${n.yPos}`),e?`${n.type} on ${e}`:n.type}case"keypress":return n.keypress.type==="keypress"?`type "${C(n.keypress)}"`:`type "${n.keypress.value}"`;default:return n.type}}class le extends se{constructor({page:e,config:t,window:s,testInfo:i,deviceInfo:r}){const o=new L({page:e,window:s,type:"appetizer"});super({socket:o,config:t,deviceInfo:r}),this.window=s,this.page=e,this.config=t,this.testInfo=i,this.page.on("load",()=>{this.emit("disconnect")}),t.record&&this.on("disconnect",()=>{this.teardownUi()})}async getUI(e={}){return await super.getUI(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([b(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([b(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 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 he({session:this,testInfo:this.testInfo}).record()}catch(e){throw h(e,this.record),e}}async waitForEvent(e,t){try{return await b(this,e,t)}catch(s){throw h(s,this.waitForEvent),s}}async waitForTimeout(e){try{return await T(e)}catch(t){throw h(t,this.waitForTimeout),t}}async waitForElement(e,t){const s=await this.findElements(e,t);if(typeof(t==null?void 0:t.matches)=="number"?s.length===t.matches:s.length>0)return s;throw new Error(`Element not found: | ||
${JSON.stringify(e)}`)}on(e,t){return e==="video"&&this.window.enableVideoEvents(),e==="audio"&&this.window.enableAudioEvents(),e==="action"&&this.window.enableRecordEvents(),super.on(e,t)}once(e,t){return e==="video"&&this.window.enableVideoEvents(),e==="audio"&&this.window.enableAudioEvents(),e==="action"&&this.window.enableRecordEvents(),super.once(e,t)}}const q=new WeakMap;class de extends re{constructor({page:e}){var r;const t=(r=q.get(e))!=null?r:new ae({page:e});q.set(e,t),t.init();const s=new L({type:"webserver",page:e,window:t});super({socket:s,window:t}),this.window=t,this.page=e;let i=!1;this.on("queue",({type:o,position:a})=>{i||(i=!0,w(o==="account"?"All slots for this account are currently in use. Please wait until a slot becomes available.":"All devices are currently in use. Please wait until requested device becomes available.")),a>0&&w(o==="account"?`Position in account-level queue: ${a}`:`Position in queue: ${a}`)}),this.on("session",()=>{i&&(w("Session started"),i=!1)})}validateConfig(e){var s;return{codec:((s=this.page.context().browser())==null?void 0:s.browserType().name())==="chromium"?"jpeg":"h264",record:!0,apiVersion:O,...e}}async startSession({testInfo:e,...t}){return super.startSession(t,{testInfo:e})}async createSession(e,t){return this.session=new le({config:e,page:this.page,window:this.window,testInfo:t.testInfo,deviceInfo:this.deviceInfo}),this.session}}R.expect.extend({toHaveElement:async(n,e,t={})=>{try{const s=await n.findElements(e,t);return{pass:typeof t.matches=="number"?s.length===t.matches:s.length>0,message:()=>`Element not found: | ||
`)),y("\u{1F7E2} Finished"),this.currentRecord+=1}}}function ue(n,e){const t=n[n.length-1];if(t)switch(e.type){case"keypress":(t==null?void 0:t.type)==="keypress"&&e.type==="keypress"&&e.keypress.type==="keypress"?t.keypress.type==="keypress"?n[n.length-1]={...t,keypress:{type:"keypressArray",shiftKeyArray:[t.keypress.shiftKey,e.keypress.shiftKey],keyArray:[t.keypress.key,e.keypress.key],value:C(t.keypress)+C(e.keypress)}}:n[n.length-1]={...t,keypress:{type:"keypressArray",shiftKeyArray:[...t.keypress.shiftKeyArray,e.keypress.shiftKey],keyArray:[...t.keypress.keyArray,e.keypress.key],value:t.keypress.value+C(e.keypress)}}:n.push(e);break;default:n.push(e)}else n.push(e)}function L(n){let e="";switch(n.type){case"swipe":case"click":{const t=n.element;return typeof t=="string"?e=` on element "${t}"`:t!=null&&t.accessibilityIdentifier?e=`element with accessibilityIdentifier "${t.accessibilityIdentifier}"`:t!=null&&t.class?e=`element with class "${t.class}"`:n.xPos&&n.yPos&&(e=`position ${n.xPos}, ${n.yPos}`),e?`${n.type} on ${e}`:n.type}case"keypress":return n.keypress.type==="keypress"?`type "${C(n.keypress)}"`:`type "${n.keypress.value}"`;default:return n.type}}class le extends se{constructor({page:e,config:t,window:s,testInfo:i,deviceInfo:r}){const o=new z({page:e,window:s,type:"appetizer"});super({socket:o,config:t,deviceInfo:r}),this.window=s,this.page=e,this.config=t,this.testInfo=i,this.page.on("load",()=>{this.emit("disconnect")}),t.record&&this.on("disconnect",()=>{this.teardownUi()})}async getUI(e={}){return await super.getUI(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([b(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([b(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 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 he({session:this,testInfo:this.testInfo}).record()}catch(e){throw h(e,this.record),e}}async waitForEvent(e,t){try{return await b(this,e,t)}catch(s){throw h(s,this.waitForEvent),s}}async waitForTimeout(e){try{return await T(e)}catch(t){throw h(t,this.waitForTimeout),t}}async waitForElement(e,t){const s=await this.findElements(e,t);if(typeof(t==null?void 0:t.matches)=="number"?s.length===t.matches:s.length>0)return s;throw new Error(`Element not found: | ||
${JSON.stringify(e)}`)}on(e,t){return e==="video"&&this.window.enableVideoEvents(),e==="audio"&&this.window.enableAudioEvents(),e==="action"&&this.window.enableRecordEvents(),super.on(e,t)}once(e,t){return e==="video"&&this.window.enableVideoEvents(),e==="audio"&&this.window.enableAudioEvents(),e==="action"&&this.window.enableRecordEvents(),super.once(e,t)}}const V=new WeakMap;class de extends re{constructor({page:e}){var r;const t=(r=V.get(e))!=null?r:new ae({page:e});V.set(e,t),t.init();const s=new z({type:"webserver",page:e,window:t});super({socket:s,window:t}),this.window=t,this.page=e;let i=!1;this.on("queue",({type:o,position:a})=>{i||(i=!0,y(o==="account"?"All slots for this account are currently in use. Please wait until a slot becomes available.":"All devices are currently in use. Please wait until requested device becomes available.")),a>0&&y(o==="account"?`Position in account-level queue: ${a}`:`Position in queue: ${a}`)}),this.on("session",()=>{i&&(y("Session started"),i=!1)})}validateConfig(e){var s;return{codec:((s=this.page.context().browser())==null?void 0:s.browserType().name())==="chromium"?"jpeg":"h264",record:!0,apiVersion:O,...e}}async startSession({testInfo:e,...t}){return super.startSession(t,{testInfo:e})}async createSession(e,t){return this.session=new le({config:e,page:this.page,window:this.window,testInfo:t.testInfo,deviceInfo:this.deviceInfo}),this.session}}R.expect.extend({toHaveElement:async(n,e,t={})=>{try{const s=await n.findElements(e,t);return{pass:typeof t.matches=="number"?s.length===t.matches:s.length>0,message:()=>`Element not found: | ||
${JSON.stringify(e)}`}}catch(s){return{pass:!1,message:()=>s.message}}}});let k,E,m;const I=Object.assign(R.test.extend({_autoSnapshotSuffix:[async({},n,e)=>{e.snapshotSuffix="",await n()},{auto:!0}],page:async({},n)=>{if(!E)throw new Error("Appetize not initialized. Make sure you have run test.setup()");await n(E.page)},session:async({},n)=>{if(!m)throw new Error("Session was not started. Make sure you have run test.setup()");await n(m)},client:async({},n)=>{if(!E)throw new Error("Appetize not initialized. Make sure you have run test.setup()");await n(E)}}),{setup(n){I.afterEach(async({session:e},t)=>{}),I.beforeAll(async({browser:e,baseURL:t},s)=>{var i,r;if(s.config.fullyParallel)throw new Error("fullyParallel is not allowed when running Appetize tests. Please set `fullyParallel: false` in your Playwright config");if(I.setTimeout(6e4*5),k||(k=await e.newPage(),k.on("close",()=>{k=null,E=null,m=null})),"url"in n)await k.goto(n.url);else{const o=new URLSearchParams;n.device&&o.set("device",n.device),n.deviceColor&&o.set("deviceColor",n.deviceColor),n.screenOnly&&o.set("screenOnly",n.screenOnly.toString()),o.set("scale",(r=(i=n.scale)==null?void 0:i.toString())!=null?r:"auto"),await k.goto(`${t!=null?t:"https://appetize.io"}/embed/${n.publicKey}?${o.toString()}`)}E=new de({page:k}),m=await H(()=>E.startSession({...n,testInfo:s}),{retries:5,timeout:3e4,predicate:(o,a)=>o instanceof Error&&o.message.match(/too many requests/)?(console.warn(`Too many session requests. Retrying in 30 seconds... (attempt #${a})`),!0):!1})}),I.afterAll(async({},e)=>{m!=null&&m.page&&await m.page.context().close()})}});Object.defineProperty(p,"expect",{enumerable:!0,get:function(){return R.expect}}),p.test=I,Object.defineProperties(p,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
//# sourceMappingURL=index.umd.js.map |
{ | ||
"name": "@appetize/playwright", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "Test your mobile apps on Appetize.io with Playwright", | ||
@@ -5,0 +5,0 @@ "files": [ |
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
409735
2972