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.1.0 to 0.1.1

dist/core/waitFor.d.ts

2

dist/core/session.d.ts
/// <reference types="node" />
/// <reference types="node" />
import { EventEmitter } from 'events';
import { WaitForEventOptions } from '../core/util';
import { WaitForEventOptions } from '../core/waitFor';
import { Action, ActionPlaybackResult, Element } from '../core/actions';

@@ -6,0 +6,0 @@ import { SocketProtocol } from './socket';

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

/// <reference types="node" />
import { EventEmitter } from 'events';
export declare function waitFor<T>(fn: () => T | Promise<T>, timeout?: number | null): Promise<T>;
export declare function waitForTimeout(ms: number): Promise<unknown>;
export interface WaitForEventOptions<T> {
timeout?: number | null;
predicate?: (data: T) => boolean;
}
export declare function waitForEvent<T>(emitter: EventEmitter, event: string, options?: WaitForEventOptions<T>): Promise<T>;
export declare function retry<T>(fn: () => T | Promise<T>, { retries, timeout, predicate, }: {
retries?: number;
timeout?: number;
predicate?: (e: unknown, attempt: number) => boolean | undefined;
}): Promise<T>;
export declare function convertLegacyEvent(event: string, data: any): {

@@ -11,0 +7,0 @@ type: string;

@@ -5,41 +5,143 @@ import { expect, test as test$1 } from "@playwright/test";

import fs from "fs";
async function waitFor(fn, timeout = 5e3) {
const start = Date.now();
while (true) {
class SwipeGesture {
constructor(args) {
this.path = [];
this.easing = (t) => t;
this.steps = 20;
this.stepDuration = 25;
if (args == null ? void 0 : args.easing) {
if (typeof args.easing === "string") {
this.easing = Easings[args.easing];
} else {
this.easing = args.easing;
}
}
if (args == null ? void 0 : args.steps) {
this.steps = args.steps;
}
if (args == null ? void 0 : args.stepDuration) {
this.stepDuration = args.stepDuration;
}
}
from(xOrElement, y) {
if (typeof xOrElement === "object") {
this.element = xOrElement;
this.path[0] = { type: "move", x: 0, y: 0 };
} else {
this.path[0] = { type: "move", x: xOrElement, y };
}
return this;
}
to(x, y, relative = true) {
if (relative) {
this.path.push({
type: "move",
x,
y
});
} else {
this.path.push({ type: "move", x, y });
}
return this;
}
wait(duration) {
this.path.push({ type: "wait", value: duration });
return this;
}
toAction() {
const interpolated = this.interpolatePath();
return {
type: "swipe",
xPos: interpolated.map((p) => p.x.toString()),
yPos: interpolated.map((p) => p.y.toString()),
ts: interpolated.map(
(_, i) => this.easing(i * (this.stepDuration / 1e3))
),
element: this.element
};
}
previousCommand(path, type) {
return path.slice().reverse().find((p) => p.type === type);
}
interpolatePath() {
const interpolatedPath = [];
for (let i = 0; i < this.path.length - 1; i++) {
const previousMove = this.previousCommand(
this.path.slice(0, i + 1),
"move"
);
const start = previousMove;
const end = this.path[i + 1];
if (start) {
if (end.type === "move") {
const xUnit = getSwipeUnit(end.x) || getSwipeUnit(start.x);
const yUnit = getSwipeUnit(end.y) || getSwipeUnit(start.y);
const numSteps = Math.floor(
this.steps / (this.path.length - 1)
);
const stepX = (getSwipeValue(end.x) - getSwipeValue(start.x)) / numSteps;
const stepY = (getSwipeValue(end.y) - getSwipeValue(start.y)) / numSteps;
const canInterpolateX = getSwipeUnit(end.x) === getSwipeUnit(start.x) || getSwipeValue(end.x) === 0 || getSwipeValue(start.x) === 0;
const canInterpolateY = getSwipeUnit(end.y) === getSwipeUnit(start.y) || getSwipeValue(end.y) === 0 || getSwipeValue(start.y) === 0;
for (let j = 0; j <= numSteps; j++) {
interpolatedPath.push({
x: canInterpolateX ? `${getSwipeValue(start.x) + stepX * j}${xUnit}` : start.x,
y: canInterpolateY ? `${getSwipeValue(start.y) + stepY * j}${yUnit}` : start.y
});
}
} else if (end.type === "wait") {
const amt = Math.floor(end.value / this.stepDuration);
for (let j = 0; j <= amt; j++) {
interpolatedPath.push({
x: start.x,
y: start.y
});
}
}
}
}
if (interpolatedPath.length) {
interpolatedPath.unshift(interpolatedPath[0]);
interpolatedPath.push(
interpolatedPath[interpolatedPath.length - 1]
);
}
return interpolatedPath;
}
}
const getSwipeUnit = (n) => {
if (typeof n === "number" || n.endsWith("px")) {
return "";
}
return n.replace(/^-?\d+/, "");
};
const getSwipeValue = (n) => {
if (typeof n === "number") {
return n;
}
return parseInt(n);
};
const Easings = {
linear: (t) => t,
easeIn: (t) => t * t,
easeOut: (t) => t * (2 - t),
easeInOut: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
};
async function retry(fn, {
retries = 3,
timeout = 1e3,
predicate = () => true
}) {
for (let i = 1; i <= retries; i++) {
try {
const result = await fn();
return result;
return await fn();
} catch (e) {
await new Promise((resolve) => setTimeout(resolve, 100));
if (timeout !== null && Date.now() - start > timeout) {
if (i === retries || !predicate(e, i)) {
throw e;
}
await new Promise((resolve) => setTimeout(resolve, timeout));
}
}
throw null;
}
async function waitForTimeout(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function waitForEvent(emitter, event, options = {}) {
const { timeout = 1e4, predicate = () => true } = options;
return new Promise((resolve, reject) => {
const listener = (data) => {
if (predicate(data)) {
emitter.off(event, listener);
resolve(data);
}
};
emitter.on(event, listener);
if (timeout !== null) {
setTimeout(() => {
emitter.off(event, listener);
reject(
new Error(
`Timed out after ${timeout}ms waiting for "${event}" event`
)
);
}, timeout);
}
});
}
function convertLegacyEvent(event, data) {

@@ -147,2 +249,44 @@ switch (event) {

}
async function waitFor(fn, timeout = 5e3) {
const start = Date.now();
while (true) {
try {
const result = await fn();
return result;
} catch (e) {
await new Promise((resolve) => setTimeout(resolve, 100));
if (timeout !== null && Date.now() - start > timeout) {
throw e;
}
}
}
}
async function waitForTimeout(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function waitForEvent(emitter, event, optionsOrPredicate) {
var _a;
const options = typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate;
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate == null ? void 0 : optionsOrPredicate.predicate;
const timeout = (_a = options == null ? void 0 : options.timeout) != null ? _a : 1e4;
return new Promise((resolve, reject) => {
const listener = (data) => {
if (!predicate || predicate(data)) {
emitter.off(event, listener);
resolve(data);
}
};
emitter.on(event, listener);
if (timeout !== null) {
setTimeout(() => {
emitter.off(event, listener);
reject(
new Error(
`Timed out after ${timeout}ms waiting for "${event}" event`
)
);
}, timeout);
}
});
}
function uint8ArrayToString(uint8Arr) {

@@ -168,126 +312,2 @@ const length = uint8Arr.length;

}
class SwipeGesture {
constructor(args) {
this.path = [];
this.easing = (t) => t;
this.steps = 20;
this.stepDuration = 25;
if (args == null ? void 0 : args.easing) {
if (typeof args.easing === "string") {
this.easing = Easings[args.easing];
} else {
this.easing = args.easing;
}
}
if (args == null ? void 0 : args.steps) {
this.steps = args.steps;
}
if (args == null ? void 0 : args.stepDuration) {
this.stepDuration = args.stepDuration;
}
}
from(xOrElement, y) {
if (typeof xOrElement === "object") {
this.element = xOrElement;
this.path[0] = { type: "move", x: 0, y: 0 };
} else {
this.path[0] = { type: "move", x: xOrElement, y };
}
return this;
}
to(x, y, relative = true) {
if (relative) {
this.path.push({
type: "move",
x,
y
});
} else {
this.path.push({ type: "move", x, y });
}
return this;
}
wait(duration) {
this.path.push({ type: "wait", value: duration });
return this;
}
toAction() {
const interpolated = this.interpolatePath();
return {
type: "swipe",
xPos: interpolated.map((p) => p.x.toString()),
yPos: interpolated.map((p) => p.y.toString()),
ts: interpolated.map(
(_, i) => this.easing(i * (this.stepDuration / 1e3))
),
element: this.element
};
}
previousCommand(path, type) {
return path.slice().reverse().find((p) => p.type === type);
}
interpolatePath() {
const interpolatedPath = [];
for (let i = 0; i < this.path.length - 1; i++) {
const previousMove = this.previousCommand(
this.path.slice(0, i + 1),
"move"
);
const start = previousMove;
const end = this.path[i + 1];
if (start) {
if (end.type === "move") {
const xUnit = getSwipeUnit(end.x) || getSwipeUnit(start.x);
const yUnit = getSwipeUnit(end.y) || getSwipeUnit(start.y);
const numSteps = Math.floor(
this.steps / (this.path.length - 1)
);
const stepX = (getSwipeValue(end.x) - getSwipeValue(start.x)) / numSteps;
const stepY = (getSwipeValue(end.y) - getSwipeValue(start.y)) / numSteps;
const canInterpolateX = getSwipeUnit(end.x) === getSwipeUnit(start.x) || getSwipeValue(end.x) === 0 || getSwipeValue(start.x) === 0;
const canInterpolateY = getSwipeUnit(end.y) === getSwipeUnit(start.y) || getSwipeValue(end.y) === 0 || getSwipeValue(start.y) === 0;
for (let j = 0; j <= numSteps; j++) {
interpolatedPath.push({
x: canInterpolateX ? `${getSwipeValue(start.x) + stepX * j}${xUnit}` : start.x,
y: canInterpolateY ? `${getSwipeValue(start.y) + stepY * j}${yUnit}` : start.y
});
}
} else if (end.type === "wait") {
const amt = Math.floor(end.value / this.stepDuration);
for (let j = 0; j <= amt; j++) {
interpolatedPath.push({
x: start.x,
y: start.y
});
}
}
}
}
if (interpolatedPath.length) {
interpolatedPath.unshift(interpolatedPath[0]);
interpolatedPath.push(
interpolatedPath[interpolatedPath.length - 1]
);
}
return interpolatedPath;
}
}
const getSwipeUnit = (n) => {
if (typeof n === "number" || n.endsWith("px")) {
return "";
}
return n.replace(/^-?\d+/, "");
};
const getSwipeValue = (n) => {
if (typeof n === "number") {
return n;
}
return parseInt(n);
};
const Easings = {
linear: (t) => t,
easeIn: (t) => t * t,
easeOut: (t) => t * (2 - t),
easeInOut: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
};
const digitCharsWithShift = [

@@ -1351,2 +1371,18 @@ ")",

}
async waitForEvent(event, options) {
return waitForEvent(this.socket, event, options);
}
async waitForTimeout(ms) {
return waitForTimeout(ms);
}
async waitForElement(element, options) {
const elements = await this.findElements(element, options);
const pass = typeof (options == null ? void 0 : options.matches) === "number" ? elements.length === options.matches : elements.length > 0;
if (!pass) {
throw new Error(`Element not found:
${JSON.stringify(element)}`);
} else {
return elements;
}
}
}

@@ -1525,3 +1561,4 @@ class Client extends EventEmitter {

pass,
message: () => `Element not found: ${JSON.stringify(selector)}`
message: () => `Element not found:
${JSON.stringify(selector)}`
};

@@ -1576,11 +1613,10 @@ } catch (e) {

test.afterEach(async ({ session: session2 }, testInfo) => {
if (testInfo.error) {
console.log(
"actionHistory",
JSON.stringify(session2.actionHistory, null, 2)
);
}
});
test.beforeAll(async ({ browser, baseURL }, testInfo) => {
var _a, _b;
if (testInfo.config.fullyParallel) {
throw new Error(
`fullyParallel is not allowed when running Appetize tests. Please set \`fullyParallel: false\` in your Playwright config`
);
}
test.setTimeout(6e4 * 5);

@@ -1616,6 +1652,21 @@ if (!page) {

});
session = await client.startSession({
...options,
testInfo
});
session = await retry(
() => client.startSession({
...options,
testInfo
}),
{
retries: 5,
timeout: 3e4,
predicate: (e, attempt) => {
if (e instanceof Error && e.message.match(/too many requests/)) {
console.warn(
`Too many session requests. Retrying in 30 seconds... (attempt #${attempt})`
);
return true;
}
return false;
}
}
);
});

@@ -1629,6 +1680,3 @@ test.afterAll(async ({ session: session2 }, testInfo) => {

const test = _test;
function getClient(page2) {
return new PlaywrightClient({ page: page2 });
}
export { SwipeGesture, getClient, getSwipeUnit, getSwipeValue, test };
export { SwipeGesture, getSwipeUnit, getSwipeValue, test };
//# sourceMappingURL=index.es.js.map

@@ -1,5 +0,5 @@

(function(p,k){typeof exports=="object"&&typeof module!="undefined"?k(exports,require("@playwright/test"),require("events"),require("fs")):typeof define=="function"&&define.amd?define(["exports","@playwright/test","events","fs"],k):(p=typeof globalThis!="undefined"?globalThis:p||self,k(p.playwright={},p.test$1,p.events,p.fs))})(this,function(p,k,I,z){"use strict";function j(s){return s&&typeof s=="object"&&"default"in s?s:{default:s}}var R=j(z);async function C(s,e=5e3){const t=Date.now();for(;;)try{return await s()}catch(n){if(await new Promise(i=>setTimeout(i,100)),e!==null&&Date.now()-t>e)throw n}}async function T(s){return new Promise(e=>setTimeout(e,s))}async function m(s,e,t={}){const{timeout:n=1e4,predicate:i=()=>!0}=t;return new Promise((o,r)=>{const c=a=>{i(a)&&(s.off(e,c),o(a))};s.on(e,c),n!==null&&setTimeout(()=>{s.off(e,c),r(new Error(`Timed out after ${n}ms waiting for "${e}" event`))},n)})}function O(s,e){switch(s){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{type:"action",value:e};case"deleteEvent":return{type:"deletedAction",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"}}}}function W(s){const e=s.length;let t="";for(let n=0;n<e;n+=65535){let i=65535;n+65535>e&&(i=e-n),t+=String.fromCharCode.apply(null,s.subarray(n,n+i))}return t}function H(s,e){const t=W(s),n=btoa(t);return`data:${e};base64,`+n}class D{constructor(e){this.path=[],this.easing=t=>t,this.steps=20,this.stepDuration=25,e!=null&&e.easing&&(typeof e.easing=="string"?this.easing=V[e.easing]:this.easing=e.easing),e!=null&&e.steps&&(this.steps=e.steps),e!=null&&e.stepDuration&&(this.stepDuration=e.stepDuration)}from(e,t){return typeof e=="object"?(this.element=e,this.path[0]={type:"move",x:0,y:0}):this.path[0]={type:"move",x:e,y:t},this}to(e,t,n=!0){return n?this.path.push({type:"move",x:e,y:t}):this.path.push({type:"move",x:e,y:t}),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,n)=>this.easing(n*(this.stepDuration/1e3))),element:this.element}}previousCommand(e,t){return e.slice().reverse().find(n=>n.type===t)}interpolatePath(){const e=[];for(let t=0;t<this.path.length-1;t++){const i=this.previousCommand(this.path.slice(0,t+1),"move"),o=this.path[t+1];if(i){if(o.type==="move"){const r=y(o.x)||y(i.x),c=y(o.y)||y(i.y),a=Math.floor(this.steps/(this.path.length-1)),u=(h(o.x)-h(i.x))/a,l=(h(o.y)-h(i.y))/a,d=y(o.x)===y(i.x)||h(o.x)===0||h(i.x)===0,f=y(o.y)===y(i.y)||h(o.y)===0||h(i.y)===0;for(let w=0;w<=a;w++)e.push({x:d?`${h(i.x)+u*w}${r}`:i.x,y:f?`${h(i.y)+l*w}${c}`:i.y})}else if(o.type==="wait"){const r=Math.floor(o.value/this.stepDuration);for(let c=0;c<=r;c++)e.push({x:i.x,y:i.y})}}}return e.length&&(e.unshift(e[0]),e.push(e[e.length-1])),e}}const y=s=>typeof s=="number"||s.endsWith("px")?"":s.replace(/^-?\d+/,""),h=s=>typeof s=="number"?s:parseInt(s),V={linear:s=>s,easeIn:s=>s*s,easeOut:s=>s*(2-s),easeInOut:s=>s<.5?2*s*s:-1+(4-2*s)*s},U=[")","!","@","#","$","%","^","&","*","("],_={47:"?",44:"<",45:"_",46:">",91:"{",92:"|",93:"}",96:"~",59:":",61:"+",39:'"'},Y={191:"?",188:"<",189:"_",190:">",219:"{",220:"|",221:"}",192:"~",186:":",187:"+",222:'"'};function J(s){let e;for(const n in _)if(s===_[n])return e={key:String.fromCharCode(n),shiftKey:"true"},e;const t=U.indexOf(s);return t>-1?e={key:String.fromCharCode(t+48).toLowerCase(),shiftKey:!0}:s!==s.toLowerCase()?e={key:s.toLowerCase(),shiftKey:!0}:e={key:s,shiftKey:!1},e}function $(s){return s.type==="keypress"&&s.which&&s.key?s.which>=65&&s.which<=90?s.shiftKey?s.key:s.key.toLowerCase():s.shiftKey?s.which>=48&&s.which<=57?U[s.which-48]:Y[s.which]:s.key:null}function B(s){switch(s){case"HOME":return"home";case"VOLUME_UP":return"volumeUp";case"VOLUME_DOWN":return"volumeDown"}return s}class Q extends Error{constructor(e){var i,o,r,c;let t=e.message;const n=(i=e.playback)==null?void 0:i.event;switch(e.errorId){case"unknown":t=`${e.message}`;break;case"notFound":if(n&&"element"in n){let a=!1;const u=typeof n.element=="object"?{...n.element}:n.element;typeof u=="object"&&"allowMultipleMatches"in u&&(a=!!u.allowMultipleMatches,delete u.allowMultipleMatches),t=`No element${a?"s":""} found for selector
${JSON.stringify(u,null," ")}`}break;case"ambiguousMatch":{const a=(o=e.matchedElements)==null?void 0:o.map(({frame:u,address:l,frameInWindow:d,bounds:f,windowType:w,...x})=>x);a&&(t=`More than 1 element matched the selector. Please specify more attributes to narrow down the matches to a single element, or provide a \`matchIndex\` attribute to select one of the following results
(function(d,k){typeof exports=="object"&&typeof module!="undefined"?k(exports,require("@playwright/test"),require("events"),require("fs")):typeof define=="function"&&define.amd?define(["exports","@playwright/test","events","fs"],k):(d=typeof globalThis!="undefined"?globalThis:d||self,k(d.playwright={},d.test$1,d.events,d.fs))})(this,function(d,k,$,L){"use strict";function z(s){return s&&typeof s=="object"&&"default"in s?s:{default:s}}var R=z(L);class C{constructor(e){this.path=[],this.easing=t=>t,this.steps=20,this.stepDuration=25,e!=null&&e.easing&&(typeof e.easing=="string"?this.easing=j[e.easing]:this.easing=e.easing),e!=null&&e.steps&&(this.steps=e.steps),e!=null&&e.stepDuration&&(this.stepDuration=e.stepDuration)}from(e,t){return typeof e=="object"?(this.element=e,this.path[0]={type:"move",x:0,y:0}):this.path[0]={type:"move",x:e,y:t},this}to(e,t,n=!0){return n?this.path.push({type:"move",x:e,y:t}):this.path.push({type:"move",x:e,y:t}),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,n)=>this.easing(n*(this.stepDuration/1e3))),element:this.element}}previousCommand(e,t){return e.slice().reverse().find(n=>n.type===t)}interpolatePath(){const e=[];for(let t=0;t<this.path.length-1;t++){const i=this.previousCommand(this.path.slice(0,t+1),"move"),o=this.path[t+1];if(i){if(o.type==="move"){const r=y(o.x)||y(i.x),c=y(o.y)||y(i.y),a=Math.floor(this.steps/(this.path.length-1)),u=(h(o.x)-h(i.x))/a,l=(h(o.y)-h(i.y))/a,p=y(o.x)===y(i.x)||h(o.x)===0||h(i.x)===0,f=y(o.y)===y(i.y)||h(o.y)===0||h(i.y)===0;for(let w=0;w<=a;w++)e.push({x:p?`${h(i.x)+u*w}${r}`:i.x,y:f?`${h(i.y)+l*w}${c}`:i.y})}else if(o.type==="wait"){const r=Math.floor(o.value/this.stepDuration);for(let c=0;c<=r;c++)e.push({x:i.x,y:i.y})}}}return e.length&&(e.unshift(e[0]),e.push(e[e.length-1])),e}}const y=s=>typeof s=="number"||s.endsWith("px")?"":s.replace(/^-?\d+/,""),h=s=>typeof s=="number"?s:parseInt(s),j={linear:s=>s,easeIn:s=>s*s,easeOut:s=>s*(2-s),easeInOut:s=>s<.5?2*s*s:-1+(4-2*s)*s};async function W(s,{retries:e=3,timeout:t=1e3,predicate:n=()=>!0}){for(let i=1;i<=e;i++)try{return await s()}catch(o){if(i===e||!n(o,i))throw o;await new Promise(r=>setTimeout(r,t))}throw null}function U(s,e){switch(s){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{type:"action",value:e};case"deleteEvent":return{type:"deletedAction",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"}}}}async function D(s,e=5e3){const t=Date.now();for(;;)try{return await s()}catch(n){if(await new Promise(i=>setTimeout(i,100)),e!==null&&Date.now()-t>e)throw n}}async function I(s){return new Promise(e=>setTimeout(e,s))}async function m(s,e,t){var r;const n=typeof t=="function"?{}:t,i=typeof t=="function"?t:t==null?void 0:t.predicate,o=(r=n==null?void 0:n.timeout)!=null?r:1e4;return new Promise((c,a)=>{const u=l=>{(!i||i(l))&&(s.off(e,u),c(l))};s.on(e,u),o!==null&&setTimeout(()=>{s.off(e,u),a(new Error(`Timed out after ${o}ms waiting for "${e}" event`))},o)})}function H(s){const e=s.length;let t="";for(let n=0;n<e;n+=65535){let i=65535;n+65535>e&&(i=e-n),t+=String.fromCharCode.apply(null,s.subarray(n,n+i))}return t}function V(s,e){const t=H(s),n=btoa(t);return`data:${e};base64,`+n}const F=[")","!","@","#","$","%","^","&","*","("],_={47:"?",44:"<",45:"_",46:">",91:"{",92:"|",93:"}",96:"~",59:":",61:"+",39:'"'},Y={191:"?",188:"<",189:"_",190:">",219:"{",220:"|",221:"}",192:"~",186:":",187:"+",222:'"'};function J(s){let e;for(const n in _)if(s===_[n])return e={key:String.fromCharCode(n),shiftKey:"true"},e;const t=F.indexOf(s);return t>-1?e={key:String.fromCharCode(t+48).toLowerCase(),shiftKey:!0}:s!==s.toLowerCase()?e={key:s.toLowerCase(),shiftKey:!0}:e={key:s,shiftKey:!1},e}function M(s){return s.type==="keypress"&&s.which&&s.key?s.which>=65&&s.which<=90?s.shiftKey?s.key:s.key.toLowerCase():s.shiftKey?s.which>=48&&s.which<=57?F[s.which-48]:Y[s.which]:s.key:null}function B(s){switch(s){case"HOME":return"home";case"VOLUME_UP":return"volumeUp";case"VOLUME_DOWN":return"volumeDown"}return s}class Q extends Error{constructor(e){var i,o,r,c;let t=e.message;const n=(i=e.playback)==null?void 0:i.event;switch(e.errorId){case"unknown":t=`${e.message}`;break;case"notFound":if(n&&"element"in n){let a=!1;const u=typeof n.element=="object"?{...n.element}:n.element;typeof u=="object"&&"allowMultipleMatches"in u&&(a=!!u.allowMultipleMatches,delete u.allowMultipleMatches),t=`No element${a?"s":""} found for selector
${JSON.stringify(u,null," ")}`}break;case"ambiguousMatch":{const a=(o=e.matchedElements)==null?void 0:o.map(({frame:u,address:l,frameInWindow:p,bounds:f,windowType:w,...x})=>x);a&&(t=`More than 1 element matched the selector. Please specify more attributes to narrow down the matches to a single element, or provide a \`matchIndex\` attribute to select one of the following results
${a.map((u,l)=>`${l}: ${JSON.stringify(u,null," ")}`).join(`,
`)}`);break}default:{const a=e.playback;a!=null&&a.event.id?t=`Action (id: "${a==null?void 0:a.event.id}") failed: ${(r=e.message)!=null?r:e.errorId}`:t=`Action (type: "${a==null?void 0:a.event.type}") failed: ${(c=e.message)!=null?c:e.errorId}`}}super(t),this.name="PlayActionError"}}class M extends Error{constructor(e){super(`App Recorder must be enabled to use ${e}. Please set "record" to true in the config.`),this.name="RecorderRequiredError"}}class X extends I.EventEmitter{constructor({socket:e,config:t}){super(),this.actionHistory=[],this.isEnding=!1,this.debug={printActions:({xdoc:i=!1}={})=>{console.log(this.actionHistory.reduce((o,{action:r})=>i?`let actions = ${JSON.stringify(this.actionHistory.map(c=>c.action),null,2)}
`)}`);break}default:{const a=e.playback;a!=null&&a.event.id?t=`Action (id: "${a==null?void 0:a.event.id}") failed: ${(r=e.message)!=null?r:e.errorId}`:t=`Action (type: "${a==null?void 0:a.event.type}") failed: ${(c=e.message)!=null?c:e.errorId}`}}super(t),this.name="PlayActionError"}}class P extends Error{constructor(e){super(`App Recorder must be enabled to use ${e}. Please set "record" to true in the config.`),this.name="RecorderRequiredError"}}class X extends $.EventEmitter{constructor({socket:e,config:t}){super(),this.actionHistory=[],this.isEnding=!1,this.debug={printActions:({xdoc:i=!1}={})=>{console.log(this.actionHistory.reduce((o,{action:r})=>i?`let actions = ${JSON.stringify(this.actionHistory.map(c=>c.action),null,2)}

@@ -22,3 +22,3 @@ let nextAction = actions.shift()

`:" "+o+`
`+JSON.stringify(r,null,2),""))}},this.config=t,this.socket=e;const n=({type:i,value:o})=>{const r=O(i,o);switch(i){case"adbOverTcp":{this.adbConnectionInfo={...o,command:G(o)};break}case"networkInspectorUrl":this.networkInspectorUrl=o;break}r?(this.emit(r.type,r.value),this.emit("*",r)):(this.emit(i,o),this.emit("*",{type:i,value:o}))};this.socket.on("*",n),this.on("disconnect",()=>{if(this.socket.off("*",n),!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(r,null,2),""))}},this.config=t,this.socket=e;const n=({type:i,value:o})=>{const r=U(i,o);switch(i){case"adbOverTcp":{this.adbConnectionInfo={...o,command:G(o)};break}case"networkInspectorUrl":this.networkInspectorUrl=o;break}r?(this.emit(r.type,r.value),this.emit("*",r)):(this.emit(i,o),this.emit("*",{type:i,value:o}))};this.socket.on("*",n),this.on("disconnect",()=>{if(this.socket.off("*",n),!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

@@ -31,7 +31,7 @@ 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({ proxy: "intercept" })`);return this.networkInspectorUrl?this.networkInspectorUrl:C(()=>{if(this.networkInspectorUrl)return this.networkInspectorUrl;throw new Error("Timed out waiting for network inspector URL")})}async getAdbInfo(){if(this.config.platform&&this.config.platform!=="android")throw new Error("Session must be connected to an Android device to use adb");if(!this.config.enableAdb)throw new Error(`Session must be configured to use adb in order to get the adb command. You can do this with
startSession({ proxy: "intercept" })`);return this.networkInspectorUrl?this.networkInspectorUrl:D(()=>{if(this.networkInspectorUrl)return this.networkInspectorUrl;throw new Error("Timed out waiting for network inspector URL")})}async getAdbInfo(){if(this.config.platform&&this.config.platform!=="android")throw new Error("Session must be connected to an Android device to use adb");if(!this.config.enableAdb)throw new Error(`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:C(()=>{if(this.adbConnectionInfo)return this.adbConnectionInfo;throw new Error("Timed out waiting for adb connection")})}async rotate(e){const[t]=await Promise.all([this.waitForEvent("orientationChanged"),this.socket.send("userInteraction",{type:"keypress",key:e==="left"?"rotateLeft":"rotateRight",timeStamp:Date.now()})]);return t}async screenshot(e="buffer"){this.socket.send("getScreenshot");const t=await m(this.socket,"screenshot",{timeout:6e4});if(!t.success)throw new Error("Screenshot failed");return{data:e==="buffer"?(o=>typeof window=="undefined"?Buffer.from(o):o)(t.data):H(new Uint8Array(t.data),t.mimeType),mimeType:t.mimeType}}async heartbeat(){return this.socket.send("heartbeat")}async type(e){this.config.platform==="ios"&&await T(1e3);const t=[...e].map(J);if(this.config.record)return this.playAction({type:"keypress",keypress:{type:"keypressArray",shiftKeyArray:t.map(n=>n.shiftKey),keyArray:t.map(n=>n.key),value:e}});await Promise.all([this.socket.send("userInteraction",{type:"keypressArray",shiftKeyArray:t.map(n=>n.shiftKey),keyArray:t.map(n=>n.key),value:e}),this.waitForEvent("interaction",{predicate:n=>n.type==="keypressArray"})])}async keypress(e,t){if(e==="ANDROID_KEYCODE_MENU")return this.socket.send("androidKeycodeMenu");e=B(e);const n=Date.now(),[i]=await Promise.all([this.waitForEvent("interaction",{predicate:o=>o.type==="keypress"&&o.timeStamp===n}),this.socket.send("userInteraction",{type:"keypress",key:e,timeStamp:n,...t})]);return i}async setLanguage(e){this.config.language=e,await this.socket.send("setLanguage",{language:e,timeStamp:Date.now()})}async setLocation(e,t){const n=[e.toString(),t.toString()];return this.config.location=n,this.socket.send("setLocation",{location:n,timeStamp:Date.now()})}async openUrl(e){return this.socket.send("openUrl",{url:e,timeStamp:Date.now()})}async shake(){return this.socket.send("shakeDevice")}async biometry({match:e}){return this.socket.send(e?"biometryMatch":"biometryNoMatch")}async allowInteractions(e){return this.socket.send(e?"enableInteractions":"disableInteractions")}async restartApp(){this.socket.send("restartApp");const{platform:e}=this.config;e==="ios"?await this.waitForEvent("appLaunch",{timeout:6e4}):await T(1e3)}async reinstallApp(){this.socket.send("reinstallApp"),await this.waitForEvent("appLaunch",{timeout:6e4})}async playAction(e,t={}){if(!this.config.record)throw new M("playAction()");return this.playActions([e],t).then(n=>n[0])}async playActions(e,t={}){const{timeout:n=3e4,strictMatch:i}=t,o=[];if(!this.config.record)throw new M("playActions()");for(const r of e){const{id:c,...a}=r;let u=a.type;switch(u){case"tap":u="click";break}const[l]=await Promise.all([new Promise((d,f)=>{const w=()=>{this.off("playbackFoundAndSent",x),this.off("playbackError",v)},x=async b=>{this.actionHistory.push({action:r,result:b}),w(),d(b)},v=b=>{this.actionHistory.push({action:r,error:b}),w(),f(new Q(b))};this.once("playbackFoundAndSent",x),this.once("playbackError",v)}),this.socket.send("playEvent",{event:{...a,type:u},timeout:Math.round(n/1e3),strictMatch:i,id:c})]);o.push(l)}return o}async getUI({timeout:e=3e4}={}){this.socket.send("dumpUi");const t=await m(this.socket,"uiDump",{timeout:e});return"ui"in t?t.ui:t.result}async findElement(e,{timeout:t,...n}={}){const i=await this.playAction({type:"assert",element:e},{timeout:t,...n});if(i.matchedElements)return i.matchedElements[0];if(i.matchedElement)return i.matchedElement}async findElements(e,t){const n=await this.playAction({type:"assert",element:{...e,allowMultipleMatches:!0}},t);return n.matchedElements?n.matchedElements:n.matchedElement?[n.matchedElement]:[]}async tap(e){if("element"in e){if(!this.config.record)throw new M('"element" selector');return this.playAction({type:"click",element:e.element})}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:t=>t.type==="mouseup"})}async swipe(e){if(!this.config.record)throw new M("swipe()");let t;if(e instanceof D)t=e.toAction();else{const{duration:n=300,direction:i}=e;let o=e.distance;const r=new D({steps:25,stepDuration:n/25});if("element"in e){o||(o=i==="up"||i==="down"?"50vh":"50vw"),r.from(e.element);const c=h(o),a=y(o),u=i==="up"||i==="left"?-1:1;switch(i){case"up":case"down":r.to(0,u*c+a);break;case"left":case"right":r.to(u*c+a,0);break}}else{const c=h(e.x),a=y(e.x),u=h(e.y),l=y(e.y);o||(i==="left"||i==="right"?a?o=50+a:o=300:l?o=50+l:o=300);const d=h(o),f=y(o);if((i==="up"||i==="down")&&l!==f)throw new Error(`Distance unit (${f||"px"}) does not match y unit (${l||"px"})`);if((i==="left"||i==="right")&&a!==f)throw new Error(`Distance unit (${f||"px"}) does not match x unit (${a||"px"})`);switch(r.from(e.x,e.y),i){case"up":r.to(e.x,`${u-d}${l}`);break;case"down":r.to(e.x,`${u+d}${l}`);break;case"left":r.to(`${c-d}${a}`,e.y);break;case"right":r.to(`${c+d}${a}`,e.y);break}}t=r.toAction()}return this.playAction(t)}}function G(s){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(!s||!s.forwards[0])return;let t=e;return t=t.replace(/SERVER_PORT/,s.port.toString()),t=t.replace(/USERNAME/,s.user),t=t.replace(/HOSTNAME/,s.hostname),t=t.replace(/FORWARD_DESTINATION/,s.forwards[0].destination),t=t.replace(/FORWARD_PORT/,s.forwards[0].port.toString()),t}function Z({type:s,value:e}){switch(s){case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}const F="0.1.4";class ee extends I.EventEmitter{constructor({page:e}){super(),this.ready=!1,this.page=e,this.page.exposeFunction("__appetize_on",t=>{const n=t==="string"?t:t.type;this.emit(n,t.value),this.emit("*",{type:n,value:t.value})})}async init(){this.ready=!1,await this.page.evaluate(async([e])=>new Promise(t=>{const n=setTimeout(()=>{throw new Error("Unable to find Appetize device on page (timed out after 60 seconds)")},6e4),i=setInterval(()=>{const o=new MessageChannel;o.port1.onmessage=()=>{clearInterval(i),clearTimeout(n),t(!1)},window.postMessage({type:"init",appetizeClient:!0,version:e},"*",[o.port2]),window.__appetize_postMessage=async(r,c=!1)=>{const a=new MessageChannel;if(window.postMessage(r,"*",[a.port2]),c)return new Promise((u,l)=>{const d=setTimeout(()=>{l(new Error("Timed out waiting for postMessage response"))},6e4);a.port1.onmessage=f=>{clearTimeout(d),u(f.data)}})}},100)}),[F]),await this.page.evaluate(()=>{window.addEventListener("message",e=>{e.source===window&&window.__appetize_on(e.data)})},[]),this.ready=!0}async waitUntilReady(){return C(async()=>{if(!this.ready)throw new Error("Timed out waiting for Appetize window to be ready.")},3e4)}async postMessage(e,t=!1){return await this.waitUntilReady(),this.page.evaluate(async([n,i])=>window.__appetize_postMessage(n,i),[e,t])}}class N extends I.EventEmitter{constructor({page:e,type:t,window:n}){super(),this.page=e,this.type=t,this.window=n,this.window.on("*",({type:i,value:o})=>{switch(i){case"socketEvent":o.socket===this.type&&(this.emit(o.type,o.value),this.emit("*",{type:o.type,value:o.value}));break;case"disconnect":this.emit("disconnect"),this.emit("*",{type:"disconnect"});break;case"sessionInfo":case"chromeDevToolsUrl":case"orientationChanged":if(this.type==="appetizer"){const r=Z({type:i,value:o});r&&(this.emit(r.type,r.value),this.emit("*",r))}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 m(this,e,t)}}async function te(s){await s.pause()}const g=function(){const s="[Appetize]";return Function.prototype.bind.call(console.log,console,s)}();class se{constructor({testInfo:e,session:t}){this.currentRecord=0,this.session=t,this.testInfo=e}async record(){const e=this.testInfo.file,t=await R.default.promises.readFile(e,"utf8"),i=t.split(`
`).map((o,r)=>({line:o,num:r+1})).slice(this.testInfo.line).filter(({line:o})=>o.includes("session.record()"))[this.currentRecord].num;if(i!==void 0){g(`\u{1F534} Recording at line ${i}`);let o=[];const r=u=>{ie(u),ne(o,u),g(K(u))},c=u=>{o=o.filter(l=>l.id!==u.id)};this.session.on("action",r),this.session.on("deletedAction",c),await te(this.session.page),await T(2e3),this.session.off("action",r);const a=t.split(`
`).map((u,l)=>{var d,f;if(l===i-1){const w=(f=(d=u.match(/^\s*/))==null?void 0:d[0])!=null?f:0;return`${o.map(v=>K(v)).reduce((v,b,ce)=>`${v}
startSession({ enableAdb: true })`);return this.adbConnectionInfo?this.adbConnectionInfo:D(()=>{if(this.adbConnectionInfo)return this.adbConnectionInfo;throw new Error("Timed out waiting for adb connection")})}async rotate(e){const[t]=await Promise.all([this.waitForEvent("orientationChanged"),this.socket.send("userInteraction",{type:"keypress",key:e==="left"?"rotateLeft":"rotateRight",timeStamp:Date.now()})]);return t}async screenshot(e="buffer"){this.socket.send("getScreenshot");const t=await m(this.socket,"screenshot",{timeout:6e4});if(!t.success)throw new Error("Screenshot failed");return{data:e==="buffer"?(o=>typeof window=="undefined"?Buffer.from(o):o)(t.data):V(new Uint8Array(t.data),t.mimeType),mimeType:t.mimeType}}async heartbeat(){return this.socket.send("heartbeat")}async type(e){this.config.platform==="ios"&&await I(1e3);const t=[...e].map(J);if(this.config.record)return this.playAction({type:"keypress",keypress:{type:"keypressArray",shiftKeyArray:t.map(n=>n.shiftKey),keyArray:t.map(n=>n.key),value:e}});await Promise.all([this.socket.send("userInteraction",{type:"keypressArray",shiftKeyArray:t.map(n=>n.shiftKey),keyArray:t.map(n=>n.key),value:e}),this.waitForEvent("interaction",{predicate:n=>n.type==="keypressArray"})])}async keypress(e,t){if(e==="ANDROID_KEYCODE_MENU")return this.socket.send("androidKeycodeMenu");e=B(e);const n=Date.now(),[i]=await Promise.all([this.waitForEvent("interaction",{predicate:o=>o.type==="keypress"&&o.timeStamp===n}),this.socket.send("userInteraction",{type:"keypress",key:e,timeStamp:n,...t})]);return i}async setLanguage(e){this.config.language=e,await this.socket.send("setLanguage",{language:e,timeStamp:Date.now()})}async setLocation(e,t){const n=[e.toString(),t.toString()];return this.config.location=n,this.socket.send("setLocation",{location:n,timeStamp:Date.now()})}async openUrl(e){return this.socket.send("openUrl",{url:e,timeStamp:Date.now()})}async shake(){return this.socket.send("shakeDevice")}async biometry({match:e}){return this.socket.send(e?"biometryMatch":"biometryNoMatch")}async allowInteractions(e){return this.socket.send(e?"enableInteractions":"disableInteractions")}async restartApp(){this.socket.send("restartApp");const{platform:e}=this.config;e==="ios"?await this.waitForEvent("appLaunch",{timeout:6e4}):await I(1e3)}async reinstallApp(){this.socket.send("reinstallApp"),await this.waitForEvent("appLaunch",{timeout:6e4})}async playAction(e,t={}){if(!this.config.record)throw new P("playAction()");return this.playActions([e],t).then(n=>n[0])}async playActions(e,t={}){const{timeout:n=3e4,strictMatch:i}=t,o=[];if(!this.config.record)throw new P("playActions()");for(const r of e){const{id:c,...a}=r;let u=a.type;switch(u){case"tap":u="click";break}const[l]=await Promise.all([new Promise((p,f)=>{const w=()=>{this.off("playbackFoundAndSent",x),this.off("playbackError",v)},x=async b=>{this.actionHistory.push({action:r,result:b}),w(),p(b)},v=b=>{this.actionHistory.push({action:r,error:b}),w(),f(new Q(b))};this.once("playbackFoundAndSent",x),this.once("playbackError",v)}),this.socket.send("playEvent",{event:{...a,type:u},timeout:Math.round(n/1e3),strictMatch:i,id:c})]);o.push(l)}return o}async getUI({timeout:e=3e4}={}){this.socket.send("dumpUi");const t=await m(this.socket,"uiDump",{timeout:e});return"ui"in t?t.ui:t.result}async findElement(e,{timeout:t,...n}={}){const i=await this.playAction({type:"assert",element:e},{timeout:t,...n});if(i.matchedElements)return i.matchedElements[0];if(i.matchedElement)return i.matchedElement}async findElements(e,t){const n=await this.playAction({type:"assert",element:{...e,allowMultipleMatches:!0}},t);return n.matchedElements?n.matchedElements:n.matchedElement?[n.matchedElement]:[]}async tap(e){if("element"in e){if(!this.config.record)throw new P('"element" selector');return this.playAction({type:"click",element:e.element})}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:t=>t.type==="mouseup"})}async swipe(e){if(!this.config.record)throw new P("swipe()");let t;if(e instanceof C)t=e.toAction();else{const{duration:n=300,direction:i}=e;let o=e.distance;const r=new C({steps:25,stepDuration:n/25});if("element"in e){o||(o=i==="up"||i==="down"?"50vh":"50vw"),r.from(e.element);const c=h(o),a=y(o),u=i==="up"||i==="left"?-1:1;switch(i){case"up":case"down":r.to(0,u*c+a);break;case"left":case"right":r.to(u*c+a,0);break}}else{const c=h(e.x),a=y(e.x),u=h(e.y),l=y(e.y);o||(i==="left"||i==="right"?a?o=50+a:o=300:l?o=50+l:o=300);const p=h(o),f=y(o);if((i==="up"||i==="down")&&l!==f)throw new Error(`Distance unit (${f||"px"}) does not match y unit (${l||"px"})`);if((i==="left"||i==="right")&&a!==f)throw new Error(`Distance unit (${f||"px"}) does not match x unit (${a||"px"})`);switch(r.from(e.x,e.y),i){case"up":r.to(e.x,`${u-p}${l}`);break;case"down":r.to(e.x,`${u+p}${l}`);break;case"left":r.to(`${c-p}${a}`,e.y);break;case"right":r.to(`${c+p}${a}`,e.y);break}}t=r.toAction()}return this.playAction(t)}}function G(s){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(!s||!s.forwards[0])return;let t=e;return t=t.replace(/SERVER_PORT/,s.port.toString()),t=t.replace(/USERNAME/,s.user),t=t.replace(/HOSTNAME/,s.hostname),t=t.replace(/FORWARD_DESTINATION/,s.forwards[0].destination),t=t.replace(/FORWARD_PORT/,s.forwards[0].port.toString()),t}function Z({type:s,value:e}){switch(s){case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}const O="0.1.4";class ee extends $.EventEmitter{constructor({page:e}){super(),this.ready=!1,this.page=e,this.page.exposeFunction("__appetize_on",t=>{const n=t==="string"?t:t.type;this.emit(n,t.value),this.emit("*",{type:n,value:t.value})})}async init(){this.ready=!1,await this.page.evaluate(async([e])=>new Promise(t=>{const n=setTimeout(()=>{throw new Error("Unable to find Appetize device on page (timed out after 60 seconds)")},6e4),i=setInterval(()=>{const o=new MessageChannel;o.port1.onmessage=()=>{clearInterval(i),clearTimeout(n),t(!1)},window.postMessage({type:"init",appetizeClient:!0,version:e},"*",[o.port2]),window.__appetize_postMessage=async(r,c=!1)=>{const a=new MessageChannel;if(window.postMessage(r,"*",[a.port2]),c)return new Promise((u,l)=>{const p=setTimeout(()=>{l(new Error("Timed out waiting for postMessage response"))},6e4);a.port1.onmessage=f=>{clearTimeout(p),u(f.data)}})}},100)}),[O]),await this.page.evaluate(()=>{window.addEventListener("message",e=>{e.source===window&&window.__appetize_on(e.data)})},[]),this.ready=!0}async waitUntilReady(){return D(async()=>{if(!this.ready)throw new Error("Timed out waiting for Appetize window to be ready.")},3e4)}async postMessage(e,t=!1){return await this.waitUntilReady(),this.page.evaluate(async([n,i])=>window.__appetize_postMessage(n,i),[e,t])}}class N extends $.EventEmitter{constructor({page:e,type:t,window:n}){super(),this.page=e,this.type=t,this.window=n,this.window.on("*",({type:i,value:o})=>{switch(i){case"socketEvent":o.socket===this.type&&(this.emit(o.type,o.value),this.emit("*",{type:o.type,value:o.value}));break;case"disconnect":this.emit("disconnect"),this.emit("*",{type:"disconnect"});break;case"sessionInfo":case"chromeDevToolsUrl":case"orientationChanged":if(this.type==="appetizer"){const r=Z({type:i,value:o});r&&(this.emit(r.type,r.value),this.emit("*",r))}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 m(this,e,t)}}async function te(s){await s.pause()}const g=function(){const s="[Appetize]";return Function.prototype.bind.call(console.log,console,s)}();class se{constructor({testInfo:e,session:t}){this.currentRecord=0,this.session=t,this.testInfo=e}async record(){const e=this.testInfo.file,t=await R.default.promises.readFile(e,"utf8"),i=t.split(`
`).map((o,r)=>({line:o,num:r+1})).slice(this.testInfo.line).filter(({line:o})=>o.includes("session.record()"))[this.currentRecord].num;if(i!==void 0){g(`\u{1F534} Recording at line ${i}`);let o=[];const r=u=>{ie(u),ne(o,u),g(K(u))},c=u=>{o=o.filter(l=>l.id!==u.id)};this.session.on("action",r),this.session.on("deletedAction",c),await te(this.session.page),await I(2e3),this.session.off("action",r);const a=t.split(`
`).map((u,l)=>{var p,f;if(l===i-1){const w=(f=(p=u.match(/^\s*/))==null?void 0:p[0])!=null?f:0;return`${o.map(v=>K(v)).reduce((v,b,ce)=>`${v}
// ${ce+1}. ${b}`,"// Recorded using session.record()")}

@@ -41,3 +41,5 @@ await session.playActions(${JSON.stringify(o,null," ")})`.split(`

`)}return u});await R.default.promises.writeFile(e,a.join(`
`)),g("\u{1F7E2} Finished"),this.currentRecord+=1}}}function ne(s,e){const t=s[s.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"?s[s.length-1]={...t,keypress:{type:"keypressArray",shiftKeyArray:[t.keypress.shiftKey,e.keypress.shiftKey],keyArray:[t.keypress.key,e.keypress.key],value:$(t.keypress)+$(e.keypress)}}:s[s.length-1]={...t,keypress:{type:"keypressArray",shiftKeyArray:[...t.keypress.shiftKeyArray,e.keypress.shiftKey],keyArray:[...t.keypress.keyArray,e.keypress.key],value:t.keypress.value+$(e.keypress)}}:s.push(e);break;default:s.push(e)}else s.push(e)}function K(s){let e="";switch(s.type){case"swipe":case"click":{const t=s.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}"`:s.xPos&&s.yPos&&(e=`position ${s.xPos}, ${s.yPos}`),e?`${s.type} on ${e}`:s.type}case"keypress":return s.keypress.type==="keypress"?`type "${$(s.keypress)}"`:`type "${s.keypress.value}"`;default:return s.type}}function ie(s){switch(s.type){case"click":case"swipe":delete s.ui;break}return s}class oe extends X{constructor({page:e,config:t,window:n,testInfo:i}){const o=new N({page:e,window:n,type:"appetizer"});super({socket:o,config:t}),this.window=n,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){const[t]=await Promise.all([m(this.window,"orientationChanged"),await this.window.postMessage(e==="left"?"rotateLeft":"rotateRight")]);return this.window.page.waitForTimeout(1e3),t}async screenshot(e="buffer"){const[t]=await Promise.all([m(this.socket,"screenshot",{timeout:6e4}),this.socket.send("getScreenshot")]),n=new Uint8Array(Object.values(t.data).map(Number));return{data:e==="buffer"?Buffer.from(n):t.data,mimeType:t.mimeType}}async record(){if(!this.config.record)throw new Error("Recording is not enabled, please enable it by setting `record: true` in session config");if(!this.testInfo)throw new Error("session.record() requires using `session` from the test() arguments");return new se({session:this,testInfo:this.testInfo}).record()}}class re extends I.EventEmitter{constructor({socket:e}){super(),this.socket=e,this.socket.on("*",({type:t,value:n})=>{const i=O(t,n);if(i)this.emit(i.type,i.value),this.emit("*",i);else switch(t){case"newSession":break;default:this.emit(t,n),this.emit("*",{type:t,value:n})}}),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,n)=>{const i=()=>{n(new Error("Session failed to start - client disconnected"))},o=c=>{n(new Error(`Session failed to start - ${typeof c.message=="object"?JSON.stringify(c.message):c.message}`))},r=c=>{c.message.match(/Too many requests/)&&n(new Error("Session failed to start - too many requests"))};try{this.on("error",r),e.on("disconnect",i),e.on("error",o),await e.waitForEvent("ready",{timeout:null})}finally{this.off("error",r),e.off("disconnect",i),e.off("error",o)}t(e)})}}const L=new WeakMap;class q extends re{constructor({page:e}){var o;const t=(o=L.get(e))!=null?o:new ee({page:e});L.set(e,t),t.init();const n=new N({type:"webserver",page:e,window:t});super({socket:n}),this.page=e,this.window=t,this.window.on("*",({type:r,value:c})=>{switch(r){case"config":this.currentConfig=c;break}});let i=!1;this.on("queue",({type:r,position:c})=>{r==="account"&&i!=="account"?(i="account",g("All slots for this account are currently in use. Please wait until a slot becomes available.")):(i==="account"?g("You are now in the regular queue."):i||g("All devices are currently in use. Please wait until requested device becomes available."),i="session"),c>0&&g(`Position in queue: ${c}`)}),this.on("session",()=>{i&&(g("Session is ready to start"),i=!1)})}async startSession({testInfo:e,...t}={}){var r;await this.window.waitUntilReady();const n=(r=this.page.context().browser())==null?void 0:r.browserType().name(),i=await this.config({codec:n==="chromium"?"jpeg":"h264",record:!0,...t,apiVersion:F}),o=new oe({config:i,page:this.page,window:this.window,testInfo:e});return this.window.postMessage({type:"requestSession"}),await this.waitForSessionStart(o),o}async config({publicKey:e,...t}){return e&&await Promise.all([this.window.postMessage({type:"loadApp",value:e}),m(this.window,"app",{predicate(i){return i.publicKey===e}})]),this.window.postMessage({type:"setConfig",value:t}),await m(this.window,"config")}}k.expect.extend({toHaveElement:async(s,e,t={})=>{try{const n=await s.findElements(e,t);return{pass:typeof t.matches=="number"?n.length===t.matches:n.length>0,message:()=>`Element not found: ${JSON.stringify(e)}`}}catch(n){return{pass:!1,message:()=>n.message}}}});let S,E,P;const A=Object.assign(k.test.extend({_autoSnapshotSuffix:[async({},s,e)=>{e.snapshotSuffix="",await s()},{auto:!0}],page:async({},s)=>{if(!E)throw new Error("Appetize not initialized. Make sure you have run test.setup()");await s(E.page)},session:async({},s)=>{if(!P)throw new Error("Session was not started. Make sure you have run test.setup()");await s(P)},client:async({},s)=>{if(!E)throw new Error("Appetize not initialized. Make sure you have run test.setup()");await s(E)}}),{setup(s){A.afterEach(async({session:e},t)=>{t.error&&console.log("actionHistory",JSON.stringify(e.actionHistory,null,2))}),A.beforeAll(async({browser:e,baseURL:t},n)=>{var i,o;if(A.setTimeout(6e4*5),S||(S=await e.newPage(),S.on("close",()=>{S=null,E=null,P=null})),"url"in s)await S.goto(s.url);else{const r=new URLSearchParams;s.device&&r.set("device",s.device),s.deviceColor&&r.set("deviceColor",s.deviceColor),s.screenOnly&&r.set("screenOnly",s.screenOnly.toString()),r.set("scale",(o=(i=s.scale)==null?void 0:i.toString())!=null?o:"auto"),await S.goto(`${t!=null?t:"https://appetize.io"}/embed/${s.publicKey}?${r.toString()}`)}E=new q({page:S}),P=await E.startSession({...s,testInfo:n})}),A.afterAll(async({session:e},t)=>{await e.page.close()})}});function ae(s){return new q({page:s})}Object.defineProperty(p,"expect",{enumerable:!0,get:function(){return k.expect}}),p.SwipeGesture=D,p.getClient=ae,p.getSwipeUnit=y,p.getSwipeValue=h,p.test=A,Object.defineProperties(p,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
`)),g("\u{1F7E2} Finished"),this.currentRecord+=1}}}function ne(s,e){const t=s[s.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"?s[s.length-1]={...t,keypress:{type:"keypressArray",shiftKeyArray:[t.keypress.shiftKey,e.keypress.shiftKey],keyArray:[t.keypress.key,e.keypress.key],value:M(t.keypress)+M(e.keypress)}}:s[s.length-1]={...t,keypress:{type:"keypressArray",shiftKeyArray:[...t.keypress.shiftKeyArray,e.keypress.shiftKey],keyArray:[...t.keypress.keyArray,e.keypress.key],value:t.keypress.value+M(e.keypress)}}:s.push(e);break;default:s.push(e)}else s.push(e)}function K(s){let e="";switch(s.type){case"swipe":case"click":{const t=s.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}"`:s.xPos&&s.yPos&&(e=`position ${s.xPos}, ${s.yPos}`),e?`${s.type} on ${e}`:s.type}case"keypress":return s.keypress.type==="keypress"?`type "${M(s.keypress)}"`:`type "${s.keypress.value}"`;default:return s.type}}function ie(s){switch(s.type){case"click":case"swipe":delete s.ui;break}return s}class oe extends X{constructor({page:e,config:t,window:n,testInfo:i}){const o=new N({page:e,window:n,type:"appetizer"});super({socket:o,config:t}),this.window=n,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){const[t]=await Promise.all([m(this.window,"orientationChanged"),await this.window.postMessage(e==="left"?"rotateLeft":"rotateRight")]);return this.window.page.waitForTimeout(1e3),t}async screenshot(e="buffer"){const[t]=await Promise.all([m(this.socket,"screenshot",{timeout:6e4}),this.socket.send("getScreenshot")]),n=new Uint8Array(Object.values(t.data).map(Number));return{data:e==="buffer"?Buffer.from(n):t.data,mimeType:t.mimeType}}async record(){if(!this.config.record)throw new Error("Recording is not enabled, please enable it by setting `record: true` in session config");if(!this.testInfo)throw new Error("session.record() requires using `session` from the test() arguments");return new se({session:this,testInfo:this.testInfo}).record()}async waitForEvent(e,t){return m(this.socket,e,t)}async waitForTimeout(e){return I(e)}async waitForElement(e,t){const n=await this.findElements(e,t);if(typeof(t==null?void 0:t.matches)=="number"?n.length===t.matches:n.length>0)return n;throw new Error(`Element not found:
${JSON.stringify(e)}`)}}class re extends $.EventEmitter{constructor({socket:e}){super(),this.socket=e,this.socket.on("*",({type:t,value:n})=>{const i=U(t,n);if(i)this.emit(i.type,i.value),this.emit("*",i);else switch(t){case"newSession":break;default:this.emit(t,n),this.emit("*",{type:t,value:n})}}),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,n)=>{const i=()=>{n(new Error("Session failed to start - client disconnected"))},o=c=>{n(new Error(`Session failed to start - ${typeof c.message=="object"?JSON.stringify(c.message):c.message}`))},r=c=>{c.message.match(/Too many requests/)&&n(new Error("Session failed to start - too many requests"))};try{this.on("error",r),e.on("disconnect",i),e.on("error",o),await e.waitForEvent("ready",{timeout:null})}finally{this.off("error",r),e.off("disconnect",i),e.off("error",o)}t(e)})}}const q=new WeakMap;class ae extends re{constructor({page:e}){var o;const t=(o=q.get(e))!=null?o:new ee({page:e});q.set(e,t),t.init();const n=new N({type:"webserver",page:e,window:t});super({socket:n}),this.page=e,this.window=t,this.window.on("*",({type:r,value:c})=>{switch(r){case"config":this.currentConfig=c;break}});let i=!1;this.on("queue",({type:r,position:c})=>{r==="account"&&i!=="account"?(i="account",g("All slots for this account are currently in use. Please wait until a slot becomes available.")):(i==="account"?g("You are now in the regular queue."):i||g("All devices are currently in use. Please wait until requested device becomes available."),i="session"),c>0&&g(`Position in queue: ${c}`)}),this.on("session",()=>{i&&(g("Session is ready to start"),i=!1)})}async startSession({testInfo:e,...t}={}){var r;await this.window.waitUntilReady();const n=(r=this.page.context().browser())==null?void 0:r.browserType().name(),i=await this.config({codec:n==="chromium"?"jpeg":"h264",record:!0,...t,apiVersion:O}),o=new oe({config:i,page:this.page,window:this.window,testInfo:e});return this.window.postMessage({type:"requestSession"}),await this.waitForSessionStart(o),o}async config({publicKey:e,...t}){return e&&await Promise.all([this.window.postMessage({type:"loadApp",value:e}),m(this.window,"app",{predicate(i){return i.publicKey===e}})]),this.window.postMessage({type:"setConfig",value:t}),await m(this.window,"config")}}k.expect.extend({toHaveElement:async(s,e,t={})=>{try{const n=await s.findElements(e,t);return{pass:typeof t.matches=="number"?n.length===t.matches:n.length>0,message:()=>`Element not found:
${JSON.stringify(e)}`}}catch(n){return{pass:!1,message:()=>n.message}}}});let E,S,T;const A=Object.assign(k.test.extend({_autoSnapshotSuffix:[async({},s,e)=>{e.snapshotSuffix="",await s()},{auto:!0}],page:async({},s)=>{if(!S)throw new Error("Appetize not initialized. Make sure you have run test.setup()");await s(S.page)},session:async({},s)=>{if(!T)throw new Error("Session was not started. Make sure you have run test.setup()");await s(T)},client:async({},s)=>{if(!S)throw new Error("Appetize not initialized. Make sure you have run test.setup()");await s(S)}}),{setup(s){A.afterEach(async({session:e},t)=>{}),A.beforeAll(async({browser:e,baseURL:t},n)=>{var i,o;if(n.config.fullyParallel)throw new Error("fullyParallel is not allowed when running Appetize tests. Please set `fullyParallel: false` in your Playwright config");if(A.setTimeout(6e4*5),E||(E=await e.newPage(),E.on("close",()=>{E=null,S=null,T=null})),"url"in s)await E.goto(s.url);else{const r=new URLSearchParams;s.device&&r.set("device",s.device),s.deviceColor&&r.set("deviceColor",s.deviceColor),s.screenOnly&&r.set("screenOnly",s.screenOnly.toString()),r.set("scale",(o=(i=s.scale)==null?void 0:i.toString())!=null?o:"auto"),await E.goto(`${t!=null?t:"https://appetize.io"}/embed/${s.publicKey}?${r.toString()}`)}S=new ae({page:E}),T=await W(()=>S.startSession({...s,testInfo:n}),{retries:5,timeout:3e4,predicate:(r,c)=>r instanceof Error&&r.message.match(/too many requests/)?(console.warn(`Too many session requests. Retrying in 30 seconds... (attempt #${c})`),!0):!1})}),A.afterAll(async({session:e},t)=>{await e.page.close()})}});Object.defineProperty(d,"expect",{enumerable:!0,get:function(){return k.expect}}),d.SwipeGesture=C,d.getSwipeUnit=y,d.getSwipeValue=h,d.test=A,Object.defineProperties(d,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
//# sourceMappingURL=index.umd.js.map

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

import { expect, Page, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType } from '@playwright/test';
import { SessionConfig } from '../../core/session';
import { PlaywrightClient } from './client';
import './expect';
import { PlaywrightSession } from './session';
export * from '../../core/SwipeGesture';
export * from '../../core/actions';
export { expect };
export type { PlaywrightClient, PlaywrightSession };
interface TestArgs extends PlaywrightTestArgs, PlaywrightTestOptions {
session: PlaywrightSession;
client: PlaywrightClient;
}
interface WorkerArgs extends PlaywrightWorkerArgs, PlaywrightWorkerOptions {
session: PlaywrightSession;
client: PlaywrightClient;
}
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 function getClient(page: Page): PlaywrightClient;
export { expect } from '@playwright/test';
export { test } from './fixture';
export type { PlaywrightClient } from './client';
export type { PlaywrightSession } from './session';
/// <reference types="node" />
import { Page, TestInfo } from '@playwright/test';
import { Session, SessionConfig } from '../../core/session';
import { PlayActionOptions, Session, SessionConfig, SessionEvents } from '../../core/session';
import { WaitForEventOptionsOrPredicate } from '../../core/waitFor';
import type { Element } from '../../core/actions';
import { AppetizeWindow } from './socket';

@@ -27,2 +29,9 @@ export declare class PlaywrightSession extends Session {

record(): Promise<void>;
waitForEvent<K extends keyof SessionEvents>(event: K, options?: WaitForEventOptionsOrPredicate<SessionEvents[K]>): Promise<SessionEvents[K]>;
waitForTimeout(ms: number): Promise<unknown>;
waitForElement(element: Element, options?: WaitForElementOptions): Promise<Element[]>;
}
interface WaitForElementOptions extends PlayActionOptions {
matches?: number;
}
export {};

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

import { SocketProtocol } from '../../core/socket';
import { WaitForEventOptions } from '../../core/util';
import { WaitForEventOptions } from '../../core/waitFor';
export declare class AppetizeWindow extends EventEmitter {

@@ -8,0 +8,0 @@ page: Page;

{
"name": "@appetize/playwright",
"version": "0.1.0",
"version": "0.1.1",
"description": "Test your mobile apps on Appetize.io with Playwright",
"files": [
"dist"
"dist",
"README.md"
],

@@ -8,0 +9,0 @@ "keywords": [

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