"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const constants = require("../constants");
const path = require("path");
const os = require("os");
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 = [];
    }
    startKarmaServer(platform, liveSyncInfo, deviceDescriptors) {
        return __awaiter(this, void 0, void 0, function* () {
            platform = platform.toLowerCase();
            this.platform = platform;
            const projectData = this.$projectDataService.getProjectData(liveSyncInfo.projectDir);
            yield 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 = (karmaData) => __awaiter(this, void 0, void 0, function* () {
                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 = (yield this.$httpClient.httpRequest(socketIoJsUrl)).body;
                    this.$fs.writeFile(path.join(liveSyncInfo.projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), 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);
                }
                yield 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.");
                        testError.suppressCommandHelp = true;
                        reject(testError);
                    }
                    else {
                        resolve();
                    }
                });
                karmaRunner.send({ karmaConfig: karmaConfig });
            });
        });
    }
    canStartKarmaServer(projectData) {
        return __awaiter(this, void 0, void 0, function* () {
            let canStartKarmaServer = true;
            const requiredDependencies = ["karma", "nativescript-unit-test-runner"];
            _.each(requiredDependencies, (dep) => {
                if (!projectData.dependencies[dep] && !projectData.devDependencies[dep]) {
                    canStartKarmaServer = false;
                    return;
                }
            });
            return canStartKarmaServer;
        });
    }
    generateConfig(port, options) {
        const nics = os.networkInterfaces();
        const ips = Object.keys(nics)
            .map(nicName => nics[nicName].filter((binding) => binding.family === 'IPv4')[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,
                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.platform = platform.toLowerCase();
        this.$logger.debug(JSON.stringify(karmaConfig, null, 4));
        return karmaConfig;
    }
}
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`;
exports.TestExecutionService = TestExecutionService;
$injector.register('testExecutionService', TestExecutionService);
