// Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import type { ElectronApplication, Page } from 'playwright'; import { _electron as electron } from 'playwright'; import type { IPCRequest as ChallengeRequestType, IPCResponse as ChallengeResponseType, } from '../challenge'; import type { ReceiptType } from '../types/Receipt'; export type AppLoadedInfoType = Readonly<{ loadTime: number; messagesPerSec: number; }>; export type MessageSendInfoType = Readonly<{ timestamp: number; delta: number; }>; export type ConversationOpenInfoType = Readonly<{ delta: number; }>; export type ReceiptsInfoType = Readonly<{ type: ReceiptType; timestamps: Array; }>; export type StorageServiceInfoType = Readonly<{ manifestVersion: number; }>; export type AppOptionsType = Readonly<{ main: string; args: ReadonlyArray; config: string; }>; export class App { private privApp: ElectronApplication | undefined; constructor(private readonly options: AppOptionsType) {} public async start(): Promise { this.privApp = await electron.launch({ executablePath: this.options.main, args: this.options.args.slice(), env: { ...process.env, SIGNAL_CI_CONFIG: this.options.config, }, locale: 'en', }); } public async waitForProvisionURL(): Promise { return this.waitForEvent('provisioning-url'); } public async waitUntilLoaded(): Promise { return this.waitForEvent('app-loaded'); } public async waitForMessageSend(): Promise { return this.waitForEvent('message:send-complete'); } public async waitForConversationOpen(): Promise { return this.waitForEvent('conversation:open'); } public async waitForChallenge(): Promise { return this.waitForEvent('challenge'); } public async waitForReceipts(): Promise { return this.waitForEvent('receipts'); } public async waitForStorageService(): Promise { return this.waitForEvent('storageServiceComplete'); } public async waitForManifestVersion(version: number): Promise { // eslint-disable-next-line no-constant-condition while (true) { // eslint-disable-next-line no-await-in-loop const { manifestVersion } = await this.waitForStorageService(); if (manifestVersion >= version) { break; } } } public async solveChallenge(response: ChallengeResponseType): Promise { const window = await this.getWindow(); await window.evaluate( `window.SignalCI.solveChallenge(${JSON.stringify(response)})` ); } public async close(): Promise { await this.app.close(); } public async getWindow(): Promise { return this.app.firstWindow(); } private async waitForEvent(event: string): Promise { const window = await this.getWindow(); const result = await window.evaluate( `window.SignalCI.waitForEvent(${JSON.stringify(event)})` ); return result as T; } private get app(): ElectronApplication { if (!this.privApp) { throw new Error('Call ElectronWrap.start() first'); } return this.privApp; } }