"use strict";
/**
 * Copyright (c) Microsoft Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.Waiter = void 0;
const stackTrace_1 = require("../utils/stackTrace");
const errors_1 = require("../utils/errors");
const debugLogger_1 = require("../utils/debugLogger");
class Waiter {
    constructor() {
        this._dispose = [];
        this._failures = [];
        // TODO: can/should we move these logs into wrapApiCall?
        this._logs = [];
    }
    async waitForEvent(emitter, event, predicate) {
        const { promise, dispose } = waitForEvent(emitter, event, predicate);
        return this.waitForPromise(promise, dispose);
    }
    rejectOnEvent(emitter, event, error, predicate) {
        const { promise, dispose } = waitForEvent(emitter, event, predicate);
        this._rejectOn(promise.then(() => { throw error; }), dispose);
    }
    rejectOnTimeout(timeout, message) {
        if (!timeout)
            return;
        const { promise, dispose } = waitForTimeout(timeout);
        this._rejectOn(promise.then(() => { throw new errors_1.TimeoutError(message); }), dispose);
    }
    dispose() {
        for (const dispose of this._dispose)
            dispose();
    }
    async waitForPromise(promise, dispose) {
        try {
            const result = await Promise.race([promise, ...this._failures]);
            if (dispose)
                dispose();
            return result;
        }
        catch (e) {
            if (dispose)
                dispose();
            this.dispose();
            stackTrace_1.rewriteErrorMessage(e, e.message + formatLogRecording(this._logs) + kLoggingNote);
            throw e;
        }
    }
    log(s) {
        this._logs.push(s);
        debugLogger_1.debugLogger.log('api', s);
    }
    _rejectOn(promise, dispose) {
        this._failures.push(promise);
        if (dispose)
            this._dispose.push(dispose);
    }
}
exports.Waiter = Waiter;
function waitForEvent(emitter, event, predicate) {
    let listener;
    const promise = new Promise((resolve, reject) => {
        listener = (eventArg) => {
            try {
                if (predicate && !predicate(eventArg))
                    return;
                emitter.removeListener(event, listener);
                resolve(eventArg);
            }
            catch (e) {
                emitter.removeListener(event, listener);
                reject(e);
            }
        };
        emitter.addListener(event, listener);
    });
    const dispose = () => emitter.removeListener(event, listener);
    return { promise, dispose };
}
function waitForTimeout(timeout) {
    let timeoutId;
    const promise = new Promise(resolve => timeoutId = setTimeout(resolve, timeout));
    const dispose = () => clearTimeout(timeoutId);
    return { promise, dispose };
}
const kLoggingNote = `\nNote: use DEBUG=pw:api environment variable and rerun to capture Playwright logs.`;
function formatLogRecording(log) {
    if (!log.length)
        return '';
    const header = ` logs `;
    const headerLength = 60;
    const leftLength = (headerLength - header.length) / 2;
    const rightLength = headerLength - header.length - leftLength;
    return `\n${'='.repeat(leftLength)}${header}${'='.repeat(rightLength)}\n${log.join('\n')}\n${'='.repeat(headerLength)}`;
}
//# sourceMappingURL=waiter.js.map