Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@appetize/playwright

Package Overview
Dependencies
Maintainers
4
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@appetize/playwright - npm Package Compare versions

Comparing version 0.3.2 to 0.4.0-beta.0

dist/core/api/mapper.d.ts

24

dist/core/client/headful.d.ts
import { Client, ClientEvents, DeviceInfo } from '../client';
import { Session, SessionConfig, SessionEvents, UserSessionConfig } from '../session';
import { Logger } from '../logger';
import { Session, SessionConfig, SessionEvents, SessionInfo, UserSessionConfig } from '../session';
import { SocketProtocol } from '../socket';
import { AppetizeWindowProtocol } from '../window';
export declare class HeadfulClient<TSocket extends SocketProtocol, TEvents extends HeadfulClientEvents<TSession>, TSession extends Session<SessionEvents>> extends Client<TSocket, TEvents, TSession> {
protected _config: SessionConfig | undefined;
protected session: TSession | undefined;
app?: AppetizeApp;
window: AppetizeWindowProtocol;
deviceInfo: HeadfulDeviceInfo;
constructor({ socket, window, }: {
protected app?: AppetizeApp;
protected deviceInfo: HeadfulDeviceInfo;
protected window: AppetizeWindowProtocol;
protected ready: boolean;
constructor({ socket, window, logger, }: {
socket: TSocket;
window: AppetizeWindowProtocol;
logger?: Logger;
});
startSession(config?: Partial<UserSessionConfig>, opts?: Record<string, any>): Promise<TSession>;
private init;
waitUntilReady(): Promise<void>;
startSession(config?: Partial<UserSessionConfig>): Promise<TSession>;
config({ publicKey, ...config }: Partial<UserSessionConfig>): Promise<UserSessionConfig>;
getConfig(): SessionConfig | undefined;
getApp(): AppetizeApp | undefined;
getDeviceInfo(): HeadfulDeviceInfo;
private mapConfig;
private getDeviceInfo;
protected validateConfig(config: Partial<UserSessionConfig>): Partial<UserSessionConfig>;
protected createSession(config: SessionConfig, opts?: Record<string, any>): Promise<TSession>;
protected createSession(config: SessionConfig, info: SessionInfo): TSession;
}

@@ -22,0 +28,0 @@ export interface HeadfulClientEvents<TSession> extends ClientEvents {

@@ -5,7 +5,11 @@ /// <reference types="node" />

import { SocketProtocol } from '../socket';
import { Logger } from '../logger';
export declare class Client<TSocket extends SocketProtocol, TEvents extends ClientEvents, TSession extends Session<SessionEvents>> extends EventEmitter {
socket: TSocket;
deviceInfo: DeviceInfo;
constructor({ socket }: {
logger: Logger;
protected deviceInfo: DeviceInfo;
protected _config: SessionConfig | undefined;
constructor({ socket, logger, }: {
socket: TSocket;
logger?: Logger;
});

@@ -12,0 +16,0 @@ on<K extends Extract<keyof TEvents, string>>(event: K, listener: (value: TEvents[K]) => void): this;

@@ -1,2 +0,3 @@

import { ActionPlaybackError } from './actions';
import { Element } from './api/recorder';
import { ElementSelector } from './api/recorder';
export declare function captureStackTrace(targetObject: any, constructorOpt: any): void;

@@ -8,6 +9,8 @@ export declare function captureOperationalError(e: unknown, constructorOpt: any): Promise<void>;

}
export declare class ActionError extends OperationalError {
details: ActionPlaybackError;
constructor(details: ActionPlaybackError);
export declare class ElementNotFoundError extends OperationalError {
constructor(selector: ElementSelector);
}
export declare class AmbiguousElementError extends OperationalError {
constructor(matchedElements: Element[]);
}
export declare class TimeoutError extends OperationalError {

@@ -14,0 +17,0 @@ }

@@ -1,2 +0,2 @@

import { KeypressValue } from './actions';
import { KeypressAction } from './api/recorder';
export declare const controlKeys: (number | null)[];

@@ -34,4 +34,6 @@ export declare const digitCharsWithShift: string[];

};
export declare function getCharFromKeypress(keypress: KeypressValue): any;
export declare function getKeyFromKeypress(keypress: KeypressAction): string;
export declare function isSpecialKey(key: string): boolean;
export declare function isSpecialChar(char: string): boolean;
export declare function mapLegacyKeyCode(key: string): string;
export declare function mapKeyCodeToLegacy(key: string): string;

@@ -1,1 +0,6 @@

export declare const log: (...data: any[]) => void;
export declare class Logger {
log: (...data: any[]) => void;
warn: (...data: any[]) => void;
error: (...data: any[]) => void;
debug: (...data: any[]) => void;
}

@@ -1,231 +0,1 @@

/// <reference types="node" />
/// <reference types="node" />
import { EventEmitter } from 'events';
import { WaitForEventOptions } from '../../core/waitFor';
import { Action, ActionPlaybackResult, Element } from '../../core/actions';
import { DeviceInfo } from '../client';
import { SocketProtocol } from '../socket';
import { SwipeGesture, SwipeGestureArgs } from './swipe-gesture';
export declare class Session<Events extends SessionEvents = SessionEvents> extends EventEmitter {
socket: SocketProtocol;
config: SessionConfig;
deviceInfo: DeviceInfo;
private actionHistory;
private adbConnectionInfo?;
private networkInspectorUrl?;
private isEndingManually;
private countdownWarning;
constructor({ socket, config, deviceInfo, }: {
socket: SocketProtocol;
config: SessionConfig;
deviceInfo: DeviceInfo;
});
on<K extends Extract<keyof Events, string>>(event: K, listener: (value: Events[K]) => void): this;
waitForEvent<K extends keyof SessionEvents>(event: K, options?: WaitForEventOptions<SessionEvents[K]>): Promise<SessionEvents[K]>;
end(): Promise<void>;
getNetworkInspectorUrl(): Promise<string>;
getAdbInfo(): Promise<AdbConnectionInfo>;
rotate(direction: 'left' | 'right'): Promise<'portrait' | 'landscape'>;
screenshot<T extends 'buffer' | 'base64', Data = T extends 'buffer' ? Buffer : string>(format?: T): Promise<{
data: Data;
mimeType: string;
}>;
heartbeat(): Promise<void>;
type(text: string): Promise<ActionPlaybackResult | undefined>;
keypress(key: KeyValue, options?: {
shift?: boolean;
alt?: boolean;
}): Promise<void | ActionPlaybackResult | {
timeStamp: number;
type: string;
altKey?: boolean | undefined;
shiftKey?: boolean | undefined;
xPos?: number | undefined;
yPos?: number | undefined;
}>;
setLanguage(language: string): Promise<void>;
setLocation(latitude: number, longitude: number): Promise<void>;
openUrl(url: string): Promise<void>;
shake(): Promise<void>;
biometry({ match }: {
match: boolean;
}): Promise<void>;
allowInteractions(allow: boolean): Promise<void>;
restartApp(): Promise<void>;
reinstallApp(): Promise<void>;
adbShellCommand(command: string): Promise<void>;
playAction(action: Action, options?: {
timeout?: number;
strictMatch?: boolean;
}): Promise<ActionPlaybackResult>;
playActions(actions: Action[], options?: {
timeout?: number;
strictMatch?: boolean;
}): Promise<ActionPlaybackResult[]>;
getUI({ timeout }?: {
timeout?: number;
}): Promise<string>;
findElement(element: Element, { timeout, ...options }?: ActionOptions & Omit<ElementActionArgs, 'element'>): Promise<Element>;
findElements(element: Element, options?: ActionOptions & Omit<ElementActionArgs, 'element'>): Promise<Element[]>;
tap(args: ElementActionArgs | {
x: number;
y: number;
}, options?: ActionOptions): Promise<ActionPlaybackResult | undefined>;
swipe(args: SwipeArgs, options?: ActionOptions): Promise<ActionPlaybackResult>;
private debug;
}
export interface SessionEvents {
log: {
message: string;
};
network: NetworkRequest | NetworkResponse;
error: {
message: string;
};
action: Action;
disconnect: void;
interaction: {
timeStamp: number;
type: string;
altKey?: boolean;
shiftKey?: boolean;
xPos?: number;
yPos?: number;
};
heartbeat: void;
orientationChanged: 'landscape' | 'portrait';
appLaunch: void;
firstFrameReceived: void;
inactivityWarning: {
secondsRemaining: number;
};
ready: void;
video: {
buffer: Uint8Array;
width: number;
height: number;
codec: 'h264' | 'jpeg';
};
audio: {
buffer: Uint8Array;
codec: 'aac';
duration: number;
};
}
export interface AdbConnectionInfo {
command: string;
forwards: Array<{
destination: string;
port: number;
}>;
hash: string;
hostname: string;
port: number;
user: string;
}
export interface SessionConfig {
device: string;
osVersion: string;
scale?: number | 'auto';
autoplay?: boolean;
adbShellCommand?: string;
androidPackageManager?: boolean;
appearance?: string;
audio?: boolean;
codec?: string;
debug?: boolean;
deviceColor?: string;
disableSessionStart?: boolean;
disableVirtualKeyboard?: boolean;
enableAdb?: boolean;
publicKey?: string;
grantPermissions?: boolean;
hidePasswords?: boolean;
iosKeyboard?: string;
language?: string;
launchUrl?: string;
launchArgs?: Array<string | number>;
locale?: string;
location?: number[];
loopback?: boolean;
noVideo?: boolean;
orientation?: string;
platform?: 'ios' | 'android';
payerCode?: string;
params?: Record<string, any>;
plistEdit?: Record<string, any>;
proxy?: string;
record?: boolean;
region?: string;
screenOnly?: boolean;
screenRecording?: boolean;
showRotateButtons?: boolean;
timezone?: string;
xdocMsg?: boolean;
endSessionRedirectUrl?: string;
userInteractionDisabled?: boolean;
volume?: number;
debugSession?: boolean;
}
export type UserSessionConfig = Omit<SessionConfig, 'platform' | 'xdocMsg'>;
export interface NetworkRequest {
type: 'request';
serverIPAddress: string;
requestId: string;
request: {
method: string;
url: string;
httpVersion: string;
cookies: string[];
headers: Array<{
name: string;
value: string;
}>;
queryString: string[];
headersSize: number;
bodySize: number;
};
cache: Record<string, any>;
}
export interface NetworkResponse extends Omit<NetworkRequest, 'type'> {
type: 'response';
response: {
status: number;
statusText: string;
httpVersion: string;
cookies: string[];
headers: Array<{
name: string;
value: string;
}>;
redirectURL: string;
headersSize: number;
bodySize: number;
content: {
size: number;
mimeType: string;
compression: number;
text: string;
};
postData?: {
mimeType: string;
text?: string;
};
};
}
type LiteralUnion<T extends U, U = string> = T | (U & {
zz_IGNORE_ME?: never;
});
export type KeyValue = LiteralUnion<'HOME' | 'VOLUME_UP' | 'VOLUME_DOWN' | 'ANDROID_KEYCODE_MENU', string>;
export interface ActionOptions {
timeout?: number;
}
export interface ElementActionArgs {
element: Element;
bundleId?: string;
}
type SimpleGesture = 'up' | 'down' | 'left' | 'right';
type SwipeArgs = {
gesture: SimpleGesture | ((gesture: SwipeGesture) => any);
} & Omit<SwipeGestureArgs, 'easing'>;
export {};
export * from './session';

@@ -8,2 +8,3 @@ /// <reference types="node" />

disconnect(): Promise<void>;
emit(eventName: string | symbol, args: any[]): boolean;
}

@@ -6,10 +6,15 @@ export declare function retry<T>(fn: () => T | Promise<T>, { retries, timeout, predicate, }: {

}): Promise<T>;
export declare function convertLegacyEvent(event: string, data: any): {
type: string;
value: any;
} | null | undefined;
export declare function omitUndefinedNull<T>(obj: T): T;
export declare function clamp(value: number, min: number, max: number): number;
export declare function uuid(): string;
type CleanedObject<T> = {
[P in keyof T]: T[P] extends object ? CleanedObject<T[P]> : T[P];
};
export declare function cleanObject<T>(obj: T): CleanedObject<T>;
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
export type StrictUnion<T> = StrictUnionHelper<T, T>;
export type LiteralUnion<T extends U, U = string> = T | (U & {
zz_IGNORE_ME?: never;
});
export type OmitUnion<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
export {};

@@ -1,44 +0,21 @@

(function(f,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):(f=typeof globalThis!="undefined"?globalThis:f||self,w(f.playwright={},f.events,f.test$1,f.fs))})(this,function(f,w,R,V){"use strict";function j(n){return n&&typeof n=="object"&&"default"in n?n:{default:n}}var $=j(V);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 u(n,e){n instanceof p&&J(n,e)}class p extends Error{constructor(e){super(e),this.name="Error",this.isOperational=!0,Error.captureStackTrace(this,this.constructor)}}class Y extends p{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:h,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.
(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.
${B(a)}`);break}}super(s),this.details=e}}class S extends p{}class _ extends p{constructor(e){super(`App Recorder must be enabled to use ${e}. Please set "record" to true in the config.`)}}function B(n){return n.map((e,t)=>`${t}: ${JSON.stringify(e,null,2)}`).join(`
${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}
${JSON.stringify(r,null,2)}`).join(`
`)}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,h=(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")+h*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)}
`)}${s?`
let nextAction = actions.shift()
window.addEventListener('message', (event) => {
if (event.data?.type === 'playbackFoundAndSent') {
console.log("Playback success", event.data.value)
nextAction = actions.shift()
if (nextAction) {
window.postMessage({ type: 'playEvent', value: { event: nextAction } }, '*')
}
} else if (event.data?.type === 'playbackError') {
console.error('Playback error:', event.data.value)
}
})
window.postMessage({ type: 'playEvent', value: { event: nextAction } }, '*')
`:" "+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;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
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({ debug: true })`),e==="action"&&this.config.record!==!0&&console.warn(`Session must configured to use app recorder in order to listen to action 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 u(s,this.waitForEvent),s}}async end(){this.isEndingManually=!0,await this.socket.disconnect()}async getNetworkInspectorUrl(){try{if(this.config.proxy!=="intercept")throw new p(`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 u(e,this.getNetworkInspectorUrl),e}}async getAdbInfo(){try{if(this.config.platform&&this.config.platform!=="android")throw new p("Session must be connected to an Android device to use adb");if(!this.config.enableAdb)throw new p(`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 u(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 u(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 p("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 u(t,this.screenshot),t}}async heartbeat(){try{return await this.socket.send("heartbeat")}catch(e){throw u(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 u(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 u(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 u(t,this.setLanguage),t}}async setLocation(e,t){try{if(typeof e!="number"||typeof t!="number")throw new p("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 u(s,this.setLocation),s}}async openUrl(e){try{return await this.socket.send("openUrl",{url:e,timeStamp:Date.now()})}catch(t){throw u(t,this.openUrl),t}}async shake(){try{return await this.socket.send("shakeDevice")}catch(e){throw u(e,this.swipe),e}}async biometry({match:e}){try{return await this.socket.send(e?"biometryMatch":"biometryNoMatch")}catch(t){throw u(t,this.biometry),t}}async allowInteractions(e){try{return await this.socket.send(e?"enableInteractions":"disableInteractions")}catch(t){throw u(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 u(e,this.restartApp),e}}async reinstallApp(){try{this.socket.send("reinstallApp"),await this.waitForEvent("appLaunch",{timeout:6e4})}catch(e){throw u(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 u(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 u(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,h="element"in c?c.element:void 0;switch(typeof h=="object"&&(h=D(h)),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:h,type:l},timeout:Math.round(s/1e3),strictMatch:i,id:a})]);r.push(d)}return r}catch(s){throw u(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 u(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 u(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 u(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 u(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 h=l*.5;if(typeof h=="number")a.move(0,h*(e.gesture==="up"?-1:1));else{const d=e.gesture==="up"?"-":"";a.move(0,d+h)}break}case"left":case"right":{const h=c*.5;if(typeof h=="number")a.move(h*(e.gesture==="left"?-1:1),0);else{const d=e.gesture==="left"?"-":"";a.move(d+h,0)}break}}r=a.toAction()}return this.playAction({...r,bundleId:e.bundleId},{timeout:t==null?void 0:t.timeout})}catch(r){throw u(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.2";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,h)=>{const d=setTimeout(()=>{h(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 ue{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=>{he(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 h,d;if(l===i-1){const g=(d=(h=c.match(/^\s*/))==null?void 0:h[0])!=null?d:0;return`${r.map(v=>L(v)).reduce((v,M,P)=>`${v}
// ${P+1}. ${M}`,"// Recorded using session.record()")}
...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],f=a===this.moves.length-2;for(let w=0;w<=r;w++){if(!f&&w===r)continue;const v=w/r,y=u.x+v*(l.x-u.x),p=u.y+v*(l.y-u.y),g=((a*r+w)*16+o)/1e3;n.push({x:y,y:p,t:g}),w===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){const{attributes:t,bounds:s,...n}=e,r=()=>{if(s){const{x:c,y:a,width:u,height:l}=s;return this.platform==="android"?{x:this.dipToPixel(c),y:this.dipToPixel(a),width:this.dipToPixel(u),height:this.dipToPixel(l)}:s}},o=()=>{if(t)return Object.keys(t).reduce((c,a)=>{if(this.platform==="ios")switch(a){case"userInteractionEnabled":case"isHidden":return{...c,[a]:t[a]?"1":"0"}}else this.platform;return{...c,[a]:t[a]}},{})};return k({...n,bounds:r(),attributes:o()})}}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:f}=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:f,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){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((c,a)=>{switch(a){case"userInteractionEnabled":case"isHidden":return{...c,[a]:t[a]==="1"};default:return{...c,[a]:t[a]}}},{})};return k({...n,bounds:r(),attributes:o()})}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,f)=>{const w=()=>{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}),w(),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}),w(),p.errorId){case"internalError":f(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){f(new U(p.playback.action.element));break}case"ambiguousMatch":if("matchedElements"in p&&p.matchedElements!==void 0){f(new G(p.matchedElements));break}default:f(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,f;if(u===n-1){const w=(f=(l=a.match(/^\s*/))==null?void 0:l[0])!=null?f:0;return`${r.map(y=>V(y)).reduce((y,p,g)=>`${y}
// ${g+1}. ${p}`,"// Recorded using session.record()")}
await session.playActions(${JSON.stringify(r,null," ")})`.split(`
`).map(v=>g+v).join(`
`)}return c});await $.default.promises.writeFile(e,a.join(`
`)),y("\u{1F7E2} Finished"),this.currentRecord+=1}}}function he(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 u(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 u(t,this.screenshot),t}}async record(){try{if(!this.config.record)throw new p("Recording is not enabled, please enable it by setting `record: true` in session config");if(!this.testInfo)throw new p("session.record() requires using `session` from the test() arguments");return new ue({session:this,testInfo:this.testInfo}).record()}catch(e){throw u(e,this.record),e}}async waitForEvent(e,t){try{return await b(this,e,t)}catch(s){throw u(s,this.waitForEvent),s}}async waitForTimeout(e){try{return await T(e)}catch(t){throw u(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 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(f,"expect",{enumerable:!0,get:function(){return R.expect}}),f.test=I,Object.defineProperties(f,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
`).map(y=>w+y).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.0";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,f)=>{const w=setTimeout(()=>{f(new P("Timed out after 60000ms while waiting for postMessage response"))},6e4);u.port1.onmessage=v=>{clearTimeout(w),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(w=>{if("screenshot"in w){const{screenshot:v,...y}=w;return y}return w});if(!u.length)return;const l=JSON.stringify(u,null,2);e.attach("actions",{body:l,contentType:"application/json"});const f=e.outputPath(s,"actions.json");await T.default.promises.writeFile(f,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"}})});
//# sourceMappingURL=index.umd.js.map

@@ -1,4 +0,4 @@

import { Page, TestInfo } from '@playwright/test';
import { SessionConfig, UserSessionConfig } from '../../core/session';
import { Page } from '@playwright/test';
import { HeadfulClient, HeadfulClientEvents } from '../../core/client/headful';
import { SessionConfig, SessionInfo, UserSessionConfig } from '../../core/session';
import { PlaywrightSession } from './session';

@@ -13,12 +13,19 @@ import { AppetizeWindow, PlaywrightSocket } from './socket';

protected validateConfig(config: UserSessionConfig): {
location?: number[] | undefined;
record?: boolean | undefined;
codec: string;
appearance?: string | undefined;
scale?: number | "auto" | undefined;
audio?: boolean | undefined;
orientation?: string | undefined;
language?: string | undefined;
region?: string | undefined;
autoplay?: boolean | undefined;
debug?: boolean | undefined;
proxy?: string | undefined;
device: string;
locale?: string | undefined;
osVersion: string;
scale?: number | "auto" | undefined;
autoplay?: boolean | undefined;
adbShellCommand?: string | undefined;
androidPackageManager?: boolean | undefined;
appearance?: string | undefined;
codec: string;
deviceColor?: string | undefined;

@@ -32,16 +39,9 @@ disableSessionStart?: boolean | undefined;

iosKeyboard?: string | undefined;
language?: string | undefined;
launchUrl?: string | undefined;
launchArgs?: (string | number)[] | undefined;
locale?: string | undefined;
location?: number[] | undefined;
loopback?: boolean | undefined;
noVideo?: boolean | undefined;
orientation?: string | undefined;
payerCode?: string | undefined;
params?: Record<string, any> | undefined;
plistEdit?: Record<string, any> | undefined;
proxy?: string | undefined;
record: boolean;
region?: string | undefined;
screenOnly?: boolean | undefined;

@@ -55,12 +55,6 @@ screenRecording?: boolean | undefined;

debugSession?: boolean | undefined;
apiVersion: string;
};
startSession({ testInfo, ...config }: Partial<UserSessionConfig> & {
testInfo?: TestInfo;
}): Promise<PlaywrightSession>;
protected createSession(config: SessionConfig, opts: {
testInfo?: TestInfo;
}): Promise<PlaywrightSession>;
protected createSession(config: SessionConfig, sessionInfo: SessionInfo): PlaywrightSession;
}
export interface PlaywrightClientEvents extends HeadfulClientEvents<PlaywrightSession> {
}

@@ -1,13 +0,12 @@

import { ActionOptions } from '../../core/session';
import { Element } from '../../core/actions';
import { ElementSelector, FindElementsAction, PlayActionOptions } from '../../core/api/recorder';
declare global {
namespace PlaywrightTest {
interface Matchers<R> {
toHaveElement(selector: Element, options?: ToHaveElementOptions): Promise<R>;
toHaveElement(selector: ElementSelector, options?: ToHaveElementOptions): Promise<R>;
}
}
}
interface ToHaveElementOptions extends ActionOptions {
interface ToHaveElementOptions extends PlayActionOptions<FindElementsAction> {
matches?: number;
}
export {};

@@ -6,3 +6,8 @@ import { PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType } from '@playwright/test';

import { PlaywrightSession } from './session';
interface TestArgs extends PlaywrightTestArgs, PlaywrightTestOptions {
import { AppetizePage } from './appetize-page';
export interface AppetizeTestOptions extends PlaywrightTestOptions {
config: Partial<SessionConfig>;
}
interface TestArgs extends PlaywrightTestArgs, AppetizeTestOptions {
appetizePage: AppetizePage;
session: PlaywrightSession;

@@ -12,15 +17,4 @@ client: PlaywrightClient;

interface WorkerArgs extends PlaywrightWorkerArgs, PlaywrightWorkerOptions {
session: PlaywrightSession;
client: PlaywrightClient;
}
export type SetupOptions = ({
publicKey: string;
} | {
url: string;
}) & {
selector?: string;
} & Partial<SessionConfig>;
export declare const test: TestType<TestArgs, WorkerArgs> & {
setup: (this: TestType<any, any>, options: SetupOptions) => void;
};
export declare const test: TestType<TestArgs, WorkerArgs>;
export {};

@@ -1,6 +0,7 @@

export * from '../../core/actions';
export { type NetworkRequest, type NetworkResponse } from '../../core/session';
export { expect } from '@playwright/test';
export { test, type SetupOptions } from './fixture';
export { test, type AppetizeTestOptions } from './fixture';
export type { PlaywrightClient } from './client';
export type { PlaywrightSession } from './session';
export { type Action, type ActionBase, type FindElementsAction, type KeypressAction, type RecordedAction, type RecordedActionBase, type RecordedKeypressAction, type RecordedSwipeAction, type RecordedTapAction, type SoftKeyboardReturnAction, type SoftKeyboardTextAction, type SwipeAction, type TapAction, type TouchAction, type TouchActionBase, type PlayActionError, type PlayActionOptions, type PlayActionResult, type PlayElementActionOptions, type Position, type RecordedTouchAction, type TypeAction, } from '../../core/api/recorder';
export { type Coordinates } from '../../core/api/recorder';
/// <reference types="node" />
import { Page, TestInfo } from '@playwright/test';
import { ActionOptions, ElementActionArgs, Session, SessionConfig, SessionEvents } from '../../core/session';
import { DeviceInfo } from '../../core/client';
import type { Action, ElementSelector, PlayActionError, PlayActionOptions, PlayActionResult, RecordedAction } from '../../core/api/recorder';
import { FindElementsAction } from '../../core/api/recorder/internal';
import { Session, SessionConfig, SessionEvents, SessionInfo } from '../../core/session';
import { WaitForEventOptionsOrPredicate } from '../../core/waitFor';
import type { Element } from '../../core/actions';
import { AppetizeWindow } from './socket';
import { DeviceInfo } from '../../core/client';
interface ActionLog {
action: Action | RecordedAction;
result?: PlayActionResult;
payload?: any;
error?: PlayActionError;
ui?: Record<string, any[]>;
}
export declare class PlaywrightSession extends Session {
page: Page;
config: SessionConfig;
window: AppetizeWindow;
testInfo?: TestInfo;
constructor({ page, config, window, testInfo, deviceInfo, }: {
protected config: SessionConfig;
protected actionLogs: Array<ActionLog>;
constructor({ page, config, window, testInfo, deviceInfo, sessionInfo, }: {
page: Page;

@@ -18,10 +27,8 @@ config: SessionConfig;

deviceInfo: DeviceInfo;
sessionInfo: SessionInfo;
testInfo?: TestInfo;
});
getUI(options?: {
debug?: boolean;
timeout?: number;
}): Promise<string>;
protected addActionLog(log: ActionLog): Promise<void>;
private teardownUi;
rotate(direction: 'left' | 'right'): Promise<"portrait" | "landscape">;
rotate(direction: 'left' | 'right'): Promise<"landscape" | "portrait">;
screenshot<T extends 'buffer' | 'base64', Data = T extends 'buffer' ? Buffer : string>(format?: T): Promise<{

@@ -34,9 +41,10 @@ data: Data;

waitForTimeout(ms: number): Promise<unknown>;
waitForElement(element: Element, options?: WaitForElementOptions): Promise<Element[]>;
waitForElement(element: ElementSelector, options?: PlayActionOptions<FindElementsAction> & {
matches?: number;
}): Promise<import("../../core/api/recorder").Element[]>;
on<K extends keyof SessionEvents>(event: K, listener: (value: SessionEvents[K]) => void): this;
once(event: string | symbol, listener: (...args: any[]) => void): this;
getVideoFrames(): Promise<any[]>;
getAudioFrames(): Promise<any[]>;
}
interface WaitForElementOptions extends ActionOptions, Omit<ElementActionArgs, 'element'> {
matches?: number;
}
export {};

@@ -15,6 +15,9 @@ /// <reference types="node" />

waitUntilReady(): Promise<void>;
enableVideoEvents(): Promise<void>;
enableAudioEvents(): Promise<void>;
enableRecordEvents(): Promise<void>;
enableVideoEvents(enabled?: boolean): Promise<void>;
enableAudioEvents(enabled?: boolean): Promise<void>;
postMessage<T>(data: any, waitForResponse?: boolean): Promise<T>;
getVideoFrames(): Promise<any[]>;
getAudioFrames(): Promise<any[]>;
resetVideoFrames(): Promise<void>;
resetAudioFrames(): Promise<void>;
}

@@ -21,0 +24,0 @@ export declare class PlaywrightSocket extends EventEmitter implements SocketProtocol {

{
"name": "@appetize/playwright",
"version": "0.3.2",
"version": "0.4.0-beta.0",
"description": "Test your mobile apps on Appetize.io with Playwright",

@@ -26,3 +26,4 @@ "files": [

"lint": "eslint . --ext .ts,.js",
"prepublishOnly": "npm run build"
"prepublishOnly": "npm run build",
"prepack": "npm run build"
},

@@ -36,9 +37,9 @@ "dependencies": {

"@types/node": "^18.11.18",
"@playwright/test": "^1.31.0",
"typescript": "^4.8.4",
"@playwright/test": "^1.32.0",
"typescript": "^5.0.0",
"vite": "^2.9.6"
},
"peerDependencies": {
"@playwright/test": "^1.30.0"
"@playwright/test": "^1.32.0"
}
}

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc