"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestExecutionService = void 0;
const constants = require("../constants");
const path = require("path");
const os = require("os");
const _ = require("lodash");
const yok_1 = require("../common/yok");
const package_path_helper_1 = require("../helpers/package-path-helper");
class TestExecutionService {
    constructor($runController, $httpClient, $config, $logger, $fs, $options, $pluginsService, $projectDataService, $childProcess) {
        this.$runController = $runController;
        this.$httpClient = $httpClient;
        this.$config = $config;
        this.$logger = $logger;
        this.$fs = $fs;
        this.$options = $options;
        this.$pluginsService = $pluginsService;
        this.$projectDataService = $projectDataService;
        this.$childProcess = $childProcess;
        this.allowedParameters = [];
    }
    async startKarmaServer(platform, liveSyncInfo, deviceDescriptors) {
        platform = platform.toLowerCase();
        this.platform = platform;
        const projectData = this.$projectDataService.getProjectData(liveSyncInfo.projectDir);
        await this.$pluginsService.ensureAllDependenciesAreInstalled(projectData);
        const karmaConfig = this.getKarmaConfiguration(platform, projectData);
        const karmaRunner = this.$childProcess.spawn(process.execPath, [path.join(__dirname, "karma-execution.js")], { stdio: ["inherit", "inherit", "inherit", "ipc"] });
        const launchKarmaTests = async (karmaData) => {
            this.$logger.trace("## Unit-testing: Parent process received message", karmaData);
            let port;
            if (karmaData.url) {
                port = karmaData.url.port;
                const socketIoJsUrl = `http://${karmaData.url.host}/socket.io/socket.io.js`;
                const socketIoJs = (await this.$httpClient.httpRequest(socketIoJsUrl))
                    .body;
                this.$fs.writeFile(path.join(liveSyncInfo.projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), JSON.parse(socketIoJs));
            }
            if (karmaData.launcherConfig) {
                const configOptions = JSON.parse(karmaData.launcherConfig);
                const configJs = this.generateConfig(port, configOptions);
                this.$fs.writeFile(path.join(liveSyncInfo.projectDir, TestExecutionService.CONFIG_FILE_NAME), configJs);
            }
            await this.$runController.run({
                liveSyncInfo,
                deviceDescriptors,
            });
        };
        karmaRunner.on("message", (karmaData) => {
            this.$logger.trace(`The received message from karma is: `, karmaData);
            if (!karmaData.launcherConfig && !karmaData.url) {
                return;
            }
            launchKarmaTests(karmaData).catch((result) => {
                this.$logger.error(result);
                process.exit(130);
            });
        });
        return new Promise((resolve, reject) => {
            karmaRunner.on("exit", (exitCode) => {
                if (exitCode !== 0) {
                    const testError = new Error("Test run failed.");
                    reject(testError);
                }
                else {
                    resolve();
                }
            });
            karmaRunner.send({ karmaConfig: karmaConfig });
        });
    }
    async canStartKarmaServer(projectData) {
        let canStartKarmaServer = true;
        const requiredDependencies = ["@nativescript/unit-test-runner"];
        _.each(requiredDependencies, (dep) => {
            if (!projectData.dependencies[dep] && !projectData.devDependencies[dep]) {
                canStartKarmaServer = false;
                return;
            }
        });
        const pathToKarma = (0, package_path_helper_1.resolvePackagePath)("karma", {
            paths: [projectData.projectDir],
        });
        canStartKarmaServer = canStartKarmaServer && !!pathToKarma;
        return canStartKarmaServer;
    }
    generateConfig(port, options) {
        const nics = os.networkInterfaces();
        const ips = Object.keys(nics)
            .map((nicName) => nics[nicName].filter((binding) => binding.family === "IPv4" || binding.family === 4)[0])
            .filter((binding) => !!binding)
            .map((binding) => binding.address);
        const config = {
            port,
            ips,
            options,
        };
        return "module.exports = " + JSON.stringify(config);
    }
    getKarmaConfiguration(platform, projectData) {
        const karmaConfig = {
            browsers: [platform],
            configFile: path.join(projectData.projectDir, "karma.conf.js"),
            _NS: {
                log: this.$logger.getLevel(),
                path: this.$options.path,
                tns: process.argv[1],
                node: process.execPath,
                env: this.$options.env,
                options: {
                    debugTransport: this.$options.debugTransport,
                    debugBrk: this.$options.debugBrk,
                    watch: !!this.$options.watch,
                    bundle: true,
                    appDirectoryRelativePath: projectData.getAppDirectoryRelativePath(),
                },
            },
        };
        if (this.$config.DEBUG || this.$logger.getLevel() === "TRACE") {
            karmaConfig.logLevel = "DEBUG";
        }
        if (!this.$options.watch) {
            karmaConfig.singleRun = true;
        }
        if (this.$options.debugBrk) {
            karmaConfig.browserNoActivityTimeout = 1000000000;
        }
        karmaConfig.projectDir = projectData.projectDir;
        karmaConfig.bundle = true;
        karmaConfig.debugBrk = this.$options.debugBrk;
        karmaConfig.appPath = projectData.getAppDirectoryRelativePath();
        karmaConfig.platform = platform.toLowerCase();
        this.$logger.trace(JSON.stringify(karmaConfig, null, 4));
        return karmaConfig;
    }
}
exports.TestExecutionService = TestExecutionService;
TestExecutionService.CONFIG_FILE_NAME = `node_modules/${constants.TEST_RUNNER_NAME}/config.js`;
TestExecutionService.SOCKETIO_JS_FILE_NAME = `node_modules/${constants.TEST_RUNNER_NAME}/socket.io.js`;
yok_1.injector.register("testExecutionService", TestExecutionService);
