"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSelfCallSiteFromStacktrace = exports.puppeteerAddEventReturnDisposer = exports.isPromise = exports.extractCodeLocationDetailsSync = exports.arrayFlat = exports.removeStringFromStringAndParse = exports.readJsonTestSnapshotFile = exports.removeStringFromString = exports.jsonReduceNoiseReviver = exports.jsonRemoveNoiseReviver = exports.unknownValueThatIsProbablyErrorToStepError = exports.isAbortError = exports.AbortError = exports.sleep = exports.nonNullable = exports.assertNotNullOrUndefined = exports.guessOperatingSystemUserAgent = exports.getSystemInfoForPuppeteerPage = exports.getSystemInfoForPlaywrightChromiumPage = exports.getSystemInfoForPlaywrightPage = exports.getSystemInfoForPage = exports.isPlaywrightChromiumBrowserContext = exports.isNotPlaywrightPage = exports.testUniqueIdentifierFromStartParams = exports.constructResultDir = exports.constructTestResultDir = exports.constructTestInvocationResultDir = exports.testResultDirFromStartParams = exports.captureStacktraceDetails = void 0;
const crypto_1 = __importDefault(require("crypto"));
const path_1 = __importDefault(require("path"));
const consts_1 = require("./consts");
const fs_extra_1 = __importDefault(require("fs-extra"));
const stack_utils_1 = __importDefault(require("stack-utils"));
const USER_CODE_BEFORE_AFTER_TO_SHOW = 3;
const stackUtils = new stack_utils_1.default({ cwd: process.cwd(), internals: stack_utils_1.default.nodeInternals() });
function captureStacktraceDetails() {
    const stacktrace = stackUtils.captureString(captureStacktraceDetails);
    const stackLines = stackUtils
        .captureString(captureStacktraceDetails)
        .split(/\r?\n/)
        .map((line) => stackUtils.parseLine(line))
        .filter(nonNullable);
    return {
        stackLines,
        stacktrace,
    };
}
exports.captureStacktraceDetails = captureStacktraceDetails;
function testResultDirFromStartParams(startParams) {
    const uniqueTestId = testUniqueIdentifierFromStartParams(startParams);
    return path_1.default.resolve(constructTestInvocationResultDir(startParams.projectRoot, startParams.runId), uniqueTestId);
}
exports.testResultDirFromStartParams = testResultDirFromStartParams;
function constructTestInvocationResultDir(projectRoot, runId) {
    return path_1.default.resolve(constructResultDir(projectRoot), consts_1.RUNS_DIR_NAME, runId);
}
exports.constructTestInvocationResultDir = constructTestInvocationResultDir;
function constructTestResultDir(projectRoot, runId, testId) {
    return path_1.default.resolve(constructResultDir(projectRoot), consts_1.RUNS_DIR_NAME, runId, testId);
}
exports.constructTestResultDir = constructTestResultDir;
function constructResultDir(projectRoot) {
    return path_1.default.resolve(projectRoot, consts_1.RESULTS_DIR_NAME);
}
exports.constructResultDir = constructResultDir;
function testUniqueIdentifierFromStartParams(startParams) {
    const onlyInterestingPath = path_1.default.relative(startParams.projectRoot, startParams.fullSuitePath);
    const str = `${onlyInterestingPath}-${startParams.fullName}`;
    return crypto_1.default.createHash('md5').update(str).digest('hex');
}
exports.testUniqueIdentifierFromStartParams = testUniqueIdentifierFromStartParams;
function isNotPlaywrightPage(page) {
    return !('exposeBinding' in page);
}
exports.isNotPlaywrightPage = isNotPlaywrightPage;
function isPlaywrightChromiumBrowserContext(context) {
    return 'newCDPSession' in context;
}
exports.isPlaywrightChromiumBrowserContext = isPlaywrightChromiumBrowserContext;
async function getSystemInfoForPage(page) {
    if (isNotPlaywrightPage(page)) {
        return getSystemInfoForPuppeteerPage(page);
    }
    return getSystemInfoForPlaywrightPage(page);
}
exports.getSystemInfoForPage = getSystemInfoForPage;
async function getSystemInfoForPlaywrightPage(page) {
    const context = page.context();
    if (isPlaywrightChromiumBrowserContext(context)) {
        return getSystemInfoForPlaywrightChromiumPage(page, context);
    }
    const browserPlatform = await page.evaluate(() => navigator.platform);
    const pageViewport = page.viewportSize() ||
        (await page.evaluate(() => ({
            width: window.innerWidth,
            height: window.innerHeight,
        })));
    const userAgent = await page.evaluate(() => navigator.userAgent);
    return {
        automationFramework: 'playwright',
        browser: context.constructor.name === 'FFBrowserContext' ? 'firefox' : 'webkit',
        pageViewport,
        userAgent,
        browserPlatform,
        modelName: 'N/A',
        modelVersion: 'N/A',
        browserVersion: 'N/A',
    };
}
exports.getSystemInfoForPlaywrightPage = getSystemInfoForPlaywrightPage;
async function getSystemInfoForPlaywrightChromiumPage(page, context) {
    const crBrowser = context._browser;
    const cdpSession = await crBrowser.newBrowserCDPSession();
    const systemInfo = (await cdpSession.send('SystemInfo.getInfo'));
    const { modelName, modelVersion } = systemInfo;
    const browserPlatform = await page.evaluate(() => navigator.platform);
    const pageViewport = page.viewportSize() ||
        (await page.evaluate(() => ({
            width: window.innerWidth,
            height: window.innerHeight,
        })));
    const userAgent = await page.evaluate(() => navigator.userAgent);
    const browserVersion = 'N/A';
    return {
        automationFramework: 'playwright',
        browser: 'chromium',
        pageViewport,
        userAgent,
        browserVersion,
        modelName,
        modelVersion,
        browserPlatform,
    };
}
exports.getSystemInfoForPlaywrightChromiumPage = getSystemInfoForPlaywrightChromiumPage;
async function getSystemInfoForPuppeteerPage(page) {
    const browser = page.browser();
    const browserPlatform = await page.evaluate(() => navigator.platform);
    const pageViewport = page.viewport();
    const userAgent = await browser.userAgent();
    const browserVersion = await browser.version();
    const browserTarget = browser.target();
    const cdpSession = await browserTarget.createCDPSession();
    const systemInfo = (await cdpSession.send('SystemInfo.getInfo'));
    const { modelName, modelVersion } = systemInfo;
    return {
        automationFramework: 'puppeteer',
        browser: 'chromium',
        pageViewport,
        userAgent,
        browserVersion,
        modelName,
        modelVersion,
        browserPlatform,
    };
}
exports.getSystemInfoForPuppeteerPage = getSystemInfoForPuppeteerPage;
function guessOperatingSystemUserAgent() {
    return {
        name: 'N/A',
        version: 'N/A',
    };
}
exports.guessOperatingSystemUserAgent = guessOperatingSystemUserAgent;
function assertNotNullOrUndefined(value) {
    if (value === undefined || value === null) {
        throw new Error('value is nullable');
    }
}
exports.assertNotNullOrUndefined = assertNotNullOrUndefined;
function nonNullable(value) {
    return value !== undefined && value !== null;
}
exports.nonNullable = nonNullable;
function sleep(time, abortSignal) {
    return new Promise((res, rej) => {
        if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
            rej(new AbortError());
            return;
        }
        if (abortSignal) {
            abortSignal.addEventListener('abort', onAbort);
        }
        const timeoutHandle = setTimeout(timeoutCallback, time);
        function timeoutCallback() {
            if (abortSignal) {
                abortSignal.removeEventListener('abort', onAbort);
            }
            res();
        }
        function onAbort() {
            clearTimeout(timeoutHandle);
            if (abortSignal) {
                abortSignal.removeEventListener('abort', onAbort);
            }
            rej(new AbortError());
        }
    });
}
exports.sleep = sleep;
class AbortError extends Error {
    constructor() {
        super(...arguments);
        this.name = 'AbortError';
    }
}
exports.AbortError = AbortError;
function isAbortError(maybeAbortError) {
    return maybeAbortError instanceof Error && maybeAbortError.name === 'AbortError';
}
exports.isAbortError = isAbortError;
function unknownValueThatIsProbablyErrorToStepError(probablyError) {
    var _a;
    let errorToReturn;
    if (probablyError instanceof Error) {
        errorToReturn = {
            name: probablyError.name,
            message: probablyError.message,
            stack: probablyError.stack,
        };
    }
    else if (typeof probablyError === 'object' &&
        probablyError !== null &&
        'message' in probablyError &&
        'name' in probablyError) {
        errorToReturn = {
            name: probablyError.name,
            message: probablyError.message,
            stack: (_a = probablyError.stack) !== null && _a !== void 0 ? _a : undefined,
        };
    }
    else {
        errorToReturn = {
            name: 'Unknown error',
            message: 'Unknown error',
        };
    }
    return errorToReturn;
}
exports.unknownValueThatIsProbablyErrorToStepError = unknownValueThatIsProbablyErrorToStepError;
const noiseKeys = new Set([
    'userAgent',
    'modelName',
    'modelVersion',
    'browserPlatform',
    'branchInfo',
]);
function jsonRemoveNoiseReviver(key, value) {
    if (noiseKeys.has(key)) {
        return `noise_removed:${key}`;
    }
    return value;
}
exports.jsonRemoveNoiseReviver = jsonRemoveNoiseReviver;
const noiseReducers = {
    rect(rect) {
        return fromEntries(Object.entries(rect).map(([key]) => [key, 'possible noise removed']));
    },
};
function jsonReduceNoiseReviver(key, value) {
    if (key in noiseReducers) {
        return noiseReducers[key](value);
    }
    if (noiseKeys.has(key)) {
        return `noise_removed:${key}`;
    }
    return value;
}
exports.jsonReduceNoiseReviver = jsonReduceNoiseReviver;
function removeStringFromString(strToRemove, strToRemoveFrom) {
    return strToRemoveFrom.split(strToRemove).join('');
}
exports.removeStringFromString = removeStringFromString;
async function readJsonTestSnapshotFile(filePath) {
    const str = await fs_extra_1.default.readFile(filePath, 'utf8');
    return JSON.parse(str, jsonReduceNoiseReviver);
}
exports.readJsonTestSnapshotFile = readJsonTestSnapshotFile;
function removeStringFromStringAndParse(strToRemove, strToRemoveFrom, reviver) {
    return JSON.parse(removeStringFromString(strToRemove, strToRemoveFrom), reviver);
}
exports.removeStringFromStringAndParse = removeStringFromStringAndParse;
function fromEntries(iterable) {
    return [...iterable].reduce((obj, [key, val]) => {
        obj[key] = val;
        return obj;
    }, {});
}
function arrayFlat(arr) {
    return arr.reduce((prev, curr) => {
        prev.push(...curr);
        return prev;
    }, []);
}
exports.arrayFlat = arrayFlat;
function extractCodeLocationDetailsSync(userTestFile, workingDirectory) {
    const { stackLines, stacktrace } = captureStacktraceDetails();
    stacktrace.toString();
    const callSitesInUserCode = stackLines.filter((site) => {
        const callSiteFileName = site.file;
        if (!callSiteFileName) {
            return false;
        }
        return userTestFile.endsWith(callSiteFileName);
    });
    const userTestCodeLine = callSitesInUserCode[0];
    assertNotNullOrUndefined(userTestCodeLine.line);
    assertNotNullOrUndefined(userTestCodeLine.column);
    assertNotNullOrUndefined(userTestCodeLine.file);
    const userTestFileContent = fs_extra_1.default.readFileSync(userTestFile, 'utf8');
    const userTestFileCodeLines = userTestFileContent.split(/\r?\n/);
    const fromRowNumber = Math.max(userTestCodeLine.line - USER_CODE_BEFORE_AFTER_TO_SHOW, 1);
    const toRowNumber = Math.min(userTestCodeLine.line + USER_CODE_BEFORE_AFTER_TO_SHOW, userTestFileCodeLines.length);
    const codeLines = userTestFileCodeLines.slice(fromRowNumber - 1, toRowNumber);
    const callstack = stackLines
        .filter((line) => {
        var _a, _b;
        if ((_a = line.function) === null || _a === void 0 ? void 0 : _a.includes('extractCodeLocationDetailsSync')) {
            return false;
        }
        if ((_b = line.function) === null || _b === void 0 ? void 0 : _b.includes('stacktraceHook')) {
            return false;
        }
        return true;
    })
        .map((line) => ({
        ...line,
        file: line.file ? path_1.default.relative(workingDirectory, line.file) : undefined,
    }));
    return {
        sourceFileRelativePath: userTestFile.substring(process.cwd().length + 1),
        codeLines,
        fromRowNumber,
        toRowNumber,
        row: userTestCodeLine.line,
        column: userTestCodeLine.column,
        callstack,
    };
}
exports.extractCodeLocationDetailsSync = extractCodeLocationDetailsSync;
function isPromise(maybePromise) {
    return (typeof maybePromise === 'object' &&
        maybePromise !== null &&
        typeof maybePromise.then === 'function');
}
exports.isPromise = isPromise;
function puppeteerAddEventReturnDisposer(page, eventName, handler) {
    page.on(eventName, handler);
    return function dispose() {
        page.off(eventName, handler);
    };
}
exports.puppeteerAddEventReturnDisposer = puppeteerAddEventReturnDisposer;
const moreNodeBuiltin = ['assert.js', 'internal/process'];
function getSelfCallSiteFromStacktrace(howManyBack = 1) {
    const noneInternalLines = stackUtils.capture(getSelfCallSiteFromStacktrace).filter((line) => {
        if (line.isNative()) {
            return false;
        }
        const fileName = line.getFileName() || '';
        return moreNodeBuiltin.every((builtin) => !fileName.includes(builtin));
    });
    if (howManyBack >= noneInternalLines.length) {
        return null;
    }
    return noneInternalLines[howManyBack];
}
exports.getSelfCallSiteFromStacktrace = getSelfCallSiteFromStacktrace;
