"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
const _ = require("lodash");
const constants_1 = require("../constants");
const helpers_1 = require("../common/helpers");
const yok_1 = require("../common/yok");
class TestInitCommand {
    constructor($packageManager, $projectData, $errors, $options, $prompter, $fs, $resources, $pluginsService, $logger, $testInitializationService) {
        this.$packageManager = $packageManager;
        this.$projectData = $projectData;
        this.$errors = $errors;
        this.$options = $options;
        this.$prompter = $prompter;
        this.$fs = $fs;
        this.$resources = $resources;
        this.$pluginsService = $pluginsService;
        this.$logger = $logger;
        this.$testInitializationService = $testInitializationService;
        this.allowedParameters = [];
        this.karmaConfigAdditionalFrameworks = {
            mocha: ["chai"],
        };
        this.$projectData.initializeProjectData();
    }
    execute(args) {
        return __awaiter(this, void 0, void 0, function* () {
            const projectDir = this.$projectData.projectDir;
            const frameworkToInstall = this.$options.framework ||
                (yield this.$prompter.promptForChoice("Select testing framework:", constants_1.TESTING_FRAMEWORKS));
            if (constants_1.TESTING_FRAMEWORKS.indexOf(frameworkToInstall) === -1) {
                this.$errors.failWithHelp(`Unknown or unsupported unit testing framework: ${frameworkToInstall}.`);
            }
            const projectFilesExtension = this.$projectData.projectType === constants_1.ProjectTypes.TsFlavorName ||
                this.$projectData.projectType === constants_1.ProjectTypes.NgFlavorName
                ? ".ts"
                : ".js";
            let modulesToInstall = [];
            try {
                modulesToInstall = this.$testInitializationService.getDependencies(frameworkToInstall);
            }
            catch (err) {
                this.$errors.fail(`Unable to install the unit testing dependencies. Error: '${err.message}'`);
            }
            modulesToInstall = modulesToInstall.filter((moduleToInstall) => !moduleToInstall.projectType ||
                moduleToInstall.projectType === projectFilesExtension);
            for (const mod of modulesToInstall) {
                let moduleToInstall = mod.name;
                moduleToInstall += `@${mod.version}`;
                yield this.$packageManager.install(moduleToInstall, projectDir, {
                    "save-dev": true,
                    "save-exact": true,
                    optional: false,
                    disableNpmInstall: this.$options.disableNpmInstall,
                    frameworkPath: this.$options.frameworkPath,
                    ignoreScripts: this.$options.ignoreScripts,
                    path: this.$options.path,
                });
                const modulePath = path.join(projectDir, "node_modules", mod.name);
                const modulePackageJsonPath = path.join(modulePath, "package.json");
                const modulePackageJsonContent = this.$fs.readJson(modulePackageJsonPath);
                const modulePeerDependencies = modulePackageJsonContent.peerDependencies || {};
                for (const peerDependency in modulePeerDependencies) {
                    const isPeerDependencyExcluded = _.includes(mod.excludedPeerDependencies, peerDependency);
                    if (isPeerDependencyExcluded) {
                        continue;
                    }
                    const dependencyVersion = modulePeerDependencies[peerDependency] || "*";
                    try {
                        yield this.$packageManager.install(`${peerDependency}@${dependencyVersion}`, projectDir, {
                            "save-dev": true,
                            "save-exact": true,
                            disableNpmInstall: false,
                            frameworkPath: this.$options.frameworkPath,
                            ignoreScripts: this.$options.ignoreScripts,
                            path: this.$options.path,
                        });
                    }
                    catch (e) {
                        this.$logger.error(e.message);
                    }
                }
            }
            yield this.$pluginsService.add("@nativescript/unit-test-runner", this.$projectData);
            const testsDir = path.join(this.$projectData.appDirectoryPath, "tests");
            const relativeTestsDir = path.relative(this.$projectData.projectDir, testsDir);
            let shouldCreateSampleTests = true;
            if (this.$fs.exists(testsDir)) {
                this.$logger.info(`${relativeTestsDir} directory already exists, will not create an example test project.`);
                shouldCreateSampleTests = false;
            }
            this.$fs.ensureDirectoryExists(testsDir);
            const frameworks = [frameworkToInstall]
                .concat(this.karmaConfigAdditionalFrameworks[frameworkToInstall] || [])
                .map((fw) => `'${fw}'`)
                .join(", ");
            const testFiles = `'${helpers_1.fromWindowsRelativePathToUnix(relativeTestsDir)}/**/*${projectFilesExtension}'`;
            const karmaConfTemplate = this.$resources.readText("test/karma.conf.js");
            const karmaConf = _.template(karmaConfTemplate)({ frameworks, testFiles });
            this.$fs.writeFile(path.join(projectDir, "karma.conf.js"), karmaConf);
            const exampleFilePath = this.$resources.resolvePath(`test/example.${frameworkToInstall}${projectFilesExtension}`);
            if (shouldCreateSampleTests && this.$fs.exists(exampleFilePath)) {
                this.$fs.copyFile(exampleFilePath, path.join(testsDir, `example${projectFilesExtension}`));
                this.$logger.info(`\nExample test file created in ${relativeTestsDir}`.yellow);
            }
            else {
                this.$logger.info(`\nPlace your test files under ${relativeTestsDir}`.yellow);
            }
            this.$logger.info('Run your tests using the "$ ns test <platform>" command.'.yellow);
        });
    }
}
yok_1.injector.registerCommand("test|init", TestInitCommand);
