"use strict";
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.uiComponentDocErrors = exports.expectedErrors = exports.fatalErrors = exports.ErrorExpectation = void 0;
exports.setupBrowserProcessIO = setupBrowserProcessIO;
exports.installPageErrorHandlers = installPageErrorHandlers;
exports.expectError = expectError;
exports.dumpCollectedErrors = dumpCollectedErrors;
/**
 * @file Functions and state to tie error reporting and console output of
 * the browser process and frontend pages together.
 */
/* eslint-disable no-console */
const path = __importStar(require("node:path"));
const ALLOWED_ASSERTION_FAILURES = [
    // Failure during shutdown. crbug.com/1145969
    'Session is unregistering, can\'t dispatch pending call to Debugger.setBlackboxPatterns',
    // Failure during shutdown. crbug.com/1199322
    'Session is unregistering, can\'t dispatch pending call to DOM.getDocument',
    // Expected failures in assertion.test.ts
    'expected failure 1',
    'expected failure 2',
    // A failing fetch isn't itself a real error.
    // TODO(https://crbug.com/124534) Remove once those messages are not printed anymore.
    'Failed to load resource: the server responded with a status of 404 (Not Found)',
    // Every letter "typed" into the console can trigger a preview `Runtime.evaluate` call.
    // There is no way for an e2e test to know whether all of them have resolved or if there are
    // still pending calls. If the test finishes too early, the JS context is destroyed and pending
    // evaluations will fail. We ignore these kinds of errors. Tests have to make sure themselves
    // that all assertions and success criteria are met (e.g. autocompletions etc).
    // See: https://crbug.com/1192052
    'Request Runtime.evaluate failed. {"code":-32602,"message":"uniqueContextId not found"}',
    'Session is unregistering, can\'t dispatch pending call to Runtime.evaluate', // same as above
    'uniqueContextId not found',
    'Request Storage.getStorageKey failed. {"code":-32602,"message":"Frame tree node for given frame not found"}',
    // Some left-over a11y calls show up in the logs.
    'Request Accessibility.getChildAXNodes failed. {"code":-32602,"message":"Invalid ID"}',
    'Unable to create texture',
    'Not allowed to load local resource: devtools://theme/colors.css',
    // neterror.js started serving sourcemaps and we're requesting it unnecessarily.
    'Request Network.loadNetworkResource failed. {"code":-32602,"message":"Unsupported URL scheme"}',
    'Fetch API cannot load chrome-error://chromewebdata/neterror.rollup.js.map. URL scheme "chrome-error" is not supported.',
    'Request Storage.getAffectedUrlsForThirdPartyCookieMetadata failed.',
    'Hash of blocked script',
];
const FILTERED_LOGS = ['Autofocus processing was blocked because a document already has a focused element'];
const logLevels = {
    log: 'I',
    info: 'I',
    warning: 'I',
    error: 'E',
    exception: 'E',
    assert: 'E',
};
let stdout = '', stderr = '';
let unhandledRejectionSet = false;
function setupBrowserProcessIO(browser) {
    const browserProcess = browser.process();
    if (!browserProcess) {
        throw new Error('browserProcess is unexpectedly not defined.');
    }
    if (browserProcess.stderr) {
        browserProcess.stderr.setEncoding('utf8');
        browserProcess.stderr.on('data', data => {
            stderr += data;
        });
    }
    if (browserProcess.stdout) {
        browserProcess.stdout.setEncoding('utf8');
        browserProcess.stdout.on('data', data => {
            stdout += data;
        });
    }
    if (!unhandledRejectionSet) {
        browserProcess.on('unhandledRejection', error => {
            throw new Error(`Unhandled rejection in Frontend: ${error}`);
        });
        unhandledRejectionSet = true;
    }
}
function installPageErrorHandlers(page) {
    page.on('error', error => {
        console.log('STDOUT:');
        console.log(stdout);
        console.log();
        console.log('STDERR:');
        console.log(stderr);
        console.log();
        throw new Error(`Error in Frontend: ${error}`);
    });
    page.on('pageerror', error => {
        if (!(error instanceof Error)) {
            throw new Error(`Page error in Frontend: ${error}`);
        }
        if (error.message.includes(path.join('ui', 'components', 'docs'))) {
            exports.uiComponentDocErrors.push(error);
        }
        const message = error.stack ?? error.message;
        if (isExpectedError(error)) {
            exports.expectedErrors.push(message);
            console.log('(expected) ' + message);
        }
        else {
            exports.fatalErrors.push(message);
            console.error(message);
        }
        throw new Error(`Page error in Frontend: ${error}`);
    });
    page.on('console', async (msg) => {
        const logLevel = logLevels[msg.type()];
        if (logLevel) {
            if (logLevel === 'E') {
                let message = `${logLevel}> `;
                if (msg.text() === 'JSHandle@error') {
                    const errorHandle = msg.args()[0];
                    message += await errorHandle.evaluate(error => {
                        return error.stack;
                    });
                    await errorHandle.dispose();
                }
                else {
                    message += msg.text();
                    for (const frame of msg.stackTrace()) {
                        message += '\n' + formatStackFrame(frame);
                    }
                }
                if (isExpectedError(msg)) {
                    exports.expectedErrors.push(message);
                    console.log('(expected) ' + message);
                }
                else {
                    exports.fatalErrors.push(message);
                    console.error(message);
                }
            }
            else if (!FILTERED_LOGS.some(log => msg.text().includes(log))) {
                console.log(`${logLevel}> ${formatStackFrame(msg.location())}: ${msg.text()}`);
            }
        }
    });
}
function isExpectedError(consoleMessage) {
    if (ALLOWED_ASSERTION_FAILURES.some(f => (consoleMessage instanceof Error ? consoleMessage.message : consoleMessage.text()).includes(f))) {
        return true;
    }
    for (const expectation of pendingErrorExpectations) {
        if (expectation.check(consoleMessage)) {
            pendingErrorExpectations.delete(expectation);
            return true;
        }
    }
    return false;
}
class ErrorExpectation {
    #caught;
    #msg;
    constructor(msg) {
        this.#msg = msg;
        pendingErrorExpectations.add(this);
    }
    drop() {
        pendingErrorExpectations.delete(this);
        return this.#caught;
    }
    get caught() {
        return this.#caught;
    }
    check(consoleMessage) {
        const text = consoleMessage instanceof Error ? consoleMessage.message : consoleMessage.text();
        let match = (this.#msg instanceof RegExp) ? Boolean(text.match(this.#msg)) : text.includes(this.#msg);
        // When console.assert(condition) fails (no second arg), the only message is
        // "console.assert". Check the stack trace for those cases. Don't do this
        // generally as checking the stack trace should be discouraged.
        if (!match && text === 'console.assert') {
            const stack = consoleMessage instanceof Error ? consoleMessage.stack ?? '' :
                consoleMessage.stackTrace().map(l => l.url ?? '').join('\n');
            match = (this.#msg instanceof RegExp) ? Boolean(stack.match(this.#msg)) : stack.includes(this.#msg);
        }
        if (match) {
            this.#caught = consoleMessage;
        }
        return match;
    }
}
exports.ErrorExpectation = ErrorExpectation;
function expectError(msg) {
    return new ErrorExpectation(msg);
}
function formatStackFrame(stackFrame) {
    if (!stackFrame?.url) {
        return '<unknown>';
    }
    const filename = stackFrame.url.replace(/^.*\//, '');
    return `${filename}:${stackFrame.lineNumber}:${stackFrame.columnNumber}`;
}
function dumpCollectedErrors() {
    if (!(exports.expectedErrors.length + exports.fatalErrors.length)) {
        return;
    }
    console.log('Expected errors: ' + exports.expectedErrors.length);
    console.log('   Fatal errors: ' + exports.fatalErrors.length);
    if (exports.uiComponentDocErrors.length) {
        console.log('\nErrors from component examples during test run:\n', exports.uiComponentDocErrors.map(e => e.message).join('\n  '));
    }
    const allFatalErrors = exports.fatalErrors.join('\n');
    exports.expectedErrors = [];
    exports.fatalErrors = [];
    if (allFatalErrors) {
        throw new Error('Fatal errors logged:\n' + allFatalErrors);
    }
}
const pendingErrorExpectations = new Set();
exports.fatalErrors = [];
exports.expectedErrors = [];
/**
 * Gathered separately so we can surface them during screenshot tests to help
 * give an idea of failures, rather than having to guess purely based on the
 * screenshot.
 **/
exports.uiComponentDocErrors = [];
//# sourceMappingURL=events.js.map