From 82641ad3850ffd21f57dae75162f55a753272e97 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Mon, 20 Nov 2017 17:09:47 +0200 Subject: [PATCH 1/4] Fix update command and add tests. --- lib/commands/update.ts | 106 ++++++++------ package.json | 2 + test/update.ts | 318 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 384 insertions(+), 42 deletions(-) create mode 100644 test/update.ts diff --git a/lib/commands/update.ts b/lib/commands/update.ts index 2669740cca..229afea273 100644 --- a/lib/commands/update.ts +++ b/lib/commands/update.ts @@ -15,39 +15,24 @@ export class UpdateCommand implements ICommand { this.$projectData.initializeProjectData(); } + private folders: string[] = ["lib", "hooks", "platforms", "node_modules"]; + private tempFolder: string = ".tmp_backup"; + public async execute(args: string[]): Promise { - const folders = ["lib", "hooks", "platforms", "node_modules"]; - const tmpDir = path.join(this.$projectData.projectDir, ".tmp_backup"); + const tmpDir = path.join(this.$projectData.projectDir, this.tempFolder); try { - shelljs.rm("-fr", tmpDir); - shelljs.mkdir(tmpDir); - shelljs.cp(path.join(this.$projectData.projectDir, "package.json"), tmpDir); - for (const folder of folders) { - const folderToCopy = path.join(this.$projectData.projectDir, folder); - if (this.$fs.exists(folderToCopy)) { - shelljs.cp("-rf", folderToCopy, tmpDir); - } - } + this.backup(tmpDir); } catch (error) { this.$logger.error("Could not backup project folders!"); + shelljs.rm("-fr", tmpDir); return; } try { - await this.executeCore(args, folders); + await this.executeCore(args); } catch (error) { - shelljs.cp("-f", path.join(tmpDir, "package.json"), this.$projectData.projectDir); - for (const folder of folders) { - shelljs.rm("-rf", path.join(this.$projectData.projectDir, folder)); - - const folderToCopy = path.join(tmpDir, folder); - - if (this.$fs.exists(folderToCopy)) { - shelljs.cp("-fr", folderToCopy, this.$projectData.projectDir); - } - } - + this.restoreBackup(tmpDir); this.$logger.error("Could not update the project!"); } finally { shelljs.rm("-fr", tmpDir); @@ -55,9 +40,9 @@ export class UpdateCommand implements ICommand { } public async canExecute(args: string[]): Promise { - for (const arg of args) { - const platform = arg.split("@")[0]; - this.$platformService.validatePlatformInstalled(platform, this.$projectData); + const platforms = this.getPlatforms(); + + for (const platform of platforms.packagePlatforms) { const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); const platformProjectService = platformData.platformProjectService; await platformProjectService.validate(this.$projectData); @@ -66,42 +51,79 @@ export class UpdateCommand implements ICommand { return args.length < 2 && this.$projectData.projectDir !== ""; } - private async executeCore(args: string[], folders: string[]): Promise { - let platforms = this.$platformService.getInstalledPlatforms(this.$projectData); - const availablePlatforms = this.$platformService.getAvailablePlatforms(this.$projectData); - const packagePlatforms: string[] = []; + private async executeCore(args: string[]): Promise { + const platforms = this.getPlatforms(); - for (const platform of availablePlatforms) { + for (const platform of _.xor(platforms.installed, platforms.packagePlatforms)) { const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); - const platformVersion = this.$projectDataService.getNSValue(this.$projectData.projectDir, platformData.frameworkPackageName); - if (platformVersion) { - packagePlatforms.push(platform); - this.$projectDataService.removeNSProperty(this.$projectData.projectDir, platformData.frameworkPackageName); - } + this.$projectDataService.removeNSProperty(this.$projectData.projectDir, platformData.frameworkPackageName); } - await this.$platformService.removePlatforms(platforms, this.$projectData); + await this.$platformService.removePlatforms(platforms.installed, this.$projectData); await this.$pluginsService.remove("tns-core-modules", this.$projectData); await this.$pluginsService.remove("tns-core-modules-widgets", this.$projectData); - for (const folder of folders) { - shelljs.rm("-fr", folder); + for (const folder of this.folders) { + shelljs.rm("-rf", path.join(this.$projectData.projectDir, folder)); } - platforms = platforms.concat(packagePlatforms); if (args.length === 1) { - for (const platform of platforms) { + for (const platform of platforms.packagePlatforms) { await this.$platformService.addPlatforms([platform + "@" + args[0]], this.$options.platformTemplate, this.$projectData, this.$options, this.$options.frameworkPath); } await this.$pluginsService.add("tns-core-modules@" + args[0], this.$projectData); } else { - await this.$platformService.addPlatforms(platforms, this.$options.platformTemplate, this.$projectData, this.$options, this.$options.frameworkPath); + await this.$platformService.addPlatforms(platforms.packagePlatforms, this.$options.platformTemplate, this.$projectData, this.$options, this.$options.frameworkPath); await this.$pluginsService.add("tns-core-modules", this.$projectData); } await this.$pluginsService.ensureAllDependenciesAreInstalled(this.$projectData); } + + private getPlatforms(): {installed: string[], packagePlatforms: string[]} { + const installedPlatforms = this.$platformService.getInstalledPlatforms(this.$projectData); + const availablePlatforms = this.$platformService.getAvailablePlatforms(this.$projectData); + const packagePlatforms: string[] = []; + + for (const platform of availablePlatforms) { + const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); + const platformVersion = this.$projectDataService.getNSValue(this.$projectData.projectDir, platformData.frameworkPackageName); + if (platformVersion) { + packagePlatforms.push(platform); + } + } + + return { + installed: installedPlatforms, + packagePlatforms: installedPlatforms.concat(packagePlatforms) + }; + } + + private restoreBackup(tmpDir: string): void { + shelljs.cp("-f", path.join(tmpDir, "package.json"), this.$projectData.projectDir); + for (const folder of this.folders) { + shelljs.rm("-rf", path.join(this.$projectData.projectDir, folder)); + + const folderToCopy = path.join(tmpDir, folder); + + if (this.$fs.exists(folderToCopy)) { + shelljs.cp("-fr", folderToCopy, this.$projectData.projectDir); + } + } + } + + private backup(tmpDir: string): void { + shelljs.rm("-fr", tmpDir); + shelljs.mkdir(tmpDir); + shelljs.cp(path.join(this.$projectData.projectDir, "package.json"), tmpDir); + for (const folder of this.folders) { + const folderToCopy = path.join(this.$projectData.projectDir, folder); + if (this.$fs.exists(folderToCopy)) { + shelljs.cp("-rf", folderToCopy, tmpDir); + } + } + } } $injector.registerCommand("update", UpdateCommand); diff --git a/package.json b/package.json index 963004dc46..912d7dfa70 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "analyze": true, "devDependencies": { "@types/chai": "4.0.1", + "@types/sinon": "4.0.0", "@types/chai-as-promised": "0.0.31", "@types/chokidar": "1.6.0", "@types/lockfile": "1.0.0", @@ -104,6 +105,7 @@ "grunt-ts": "6.0.0-beta.16", "istanbul": "0.4.5", "mocha": "3.1.2", + "sinon": "4.1.2", "should": "7.0.2", "source-map-support": "^0.4.14", "tslint": "5.4.3", diff --git a/test/update.ts b/test/update.ts new file mode 100644 index 0000000000..6d683d40f7 --- /dev/null +++ b/test/update.ts @@ -0,0 +1,318 @@ +import * as path from "path"; +import * as stubs from "./stubs"; +import * as yok from "../lib/common/yok"; +import { UpdateCommand } from "../lib/commands/update"; +import { assert } from "chai"; +import * as sinon from 'sinon'; +import { Options } from "../lib/options"; +import { AndroidProjectService } from "../lib/services/android-project-service"; +import {StaticConfig } from "../lib/config"; +import { SettingsService } from "../lib/common/test/unit-tests/stubs"; +import * as shelljs from "shelljs"; +const projectFolder = "test"; + +function createTestInjector( + installedPlatforms: string[] = [], + availablePlatforms: string[] = [], + projectDir: string = projectFolder, + validate: Function = (): Promise => Promise.resolve() +): IInjector { + const testInjector: IInjector = new yok.Yok(); + testInjector.register("logger", stubs.LoggerStub); + testInjector.register("options", Options); + testInjector.register('fs', stubs.FileSystemStub); + testInjector.register("analyticsService", { + trackException: async (): Promise => undefined, + checkConsent: async (): Promise => undefined, + trackFeature: async (): Promise => undefined + }); + testInjector.register('hostInfo', {}); + testInjector.register('errors', stubs.ErrorsStub); + testInjector.register("staticConfig", StaticConfig); + testInjector.register("androidProjectService", AndroidProjectService); + testInjector.register("androidToolsInfo", stubs.AndroidToolsInfoStub); + testInjector.register("projectData", { projectDir, initializeProjectData: () => { /* empty */ } }); + testInjector.register("projectDataService", { + getNSValue: () => { + return "1.0.0"; + } + }); + testInjector.register("pluginVariablesService", {}); + testInjector.register("platformService", { + getInstalledPlatforms: function(): string[]{ + return installedPlatforms; + }, + getAvailablePlatforms: function(): string[]{ + return availablePlatforms; + }, + removePlatforms: async (): Promise => undefined, + addPlatforms: async (): Promise => undefined, + }); + testInjector.register("platformsData", { + availablePlatforms: { + Android: "Android", + iOS: "iOS" + }, + getPlatformData: () => { + return { + platformProjectService: { + validate + } + }; + } + }); + testInjector.register("settingsService", SettingsService); + testInjector.register("pluginsService", { + add: async (): Promise => undefined, + remove: async (): Promise => undefined, + ensureAllDependenciesAreInstalled: () => { return Promise.resolve(); }, + }); + testInjector.register("update", UpdateCommand); + + return testInjector; +} + +describe("update command method tests", () => { + describe("canExecute", () => { + it("calls platform service validate", async () => { + let validated = false; + const testInjector = createTestInjector( + [], + ["android"], + projectFolder, + () => { + validated = true; + return Promise.resolve(); + }); + const updateCommand = testInjector.resolve(UpdateCommand); + const canExecute = updateCommand.canExecute(["3.3.0"]); + + return canExecute.then(() => { + assert.equal(validated, true); + }); + }); + + it("returns false if too many artuments", async () => { + const testInjector = createTestInjector([], ["android"]); + const updateCommand = testInjector.resolve(UpdateCommand); + const canExecute = updateCommand.canExecute(["333", "111", "444"]); + + return assert.eventually.equal(canExecute, false); + }); + + it("returns false if projectDir empty string", async () => { + const testInjector = createTestInjector([], ["android"], ""); + const updateCommand = testInjector.resolve(UpdateCommand); + const canExecute = updateCommand.canExecute([]); + + return assert.eventually.equal(canExecute, false); + }); + + it("returns true all ok", async () => { + const testInjector = createTestInjector([], ["android"]); + const updateCommand = testInjector.resolve(UpdateCommand); + const canExecute = updateCommand.canExecute(["3.3.0"]); + + return assert.eventually.equal(canExecute, true); + }); + }); + + describe("execute", () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("calls backup and executeCore", async () => { + sandbox.stub(shelljs); + const testInjector = createTestInjector(); + const updateCommand = testInjector.resolve(UpdateCommand); + const executeCoreStub: sinon.SinonStub = sinon.stub(updateCommand as any, "executeCore"); + const backupStub: sinon.SinonStub = sinon.stub(updateCommand as any, "backup"); + updateCommand.execute(["3.3.0"]); + + assert.isTrue(backupStub.called); + assert.isTrue(executeCoreStub.called); + }); + + it("if backup fails, execute core not called and temp removed", async () => { + sandbox.stub(shelljs); + const rmStub: sinon.SinonStub = shelljs.rm as sinon.SinonStub; + const testInjector = createTestInjector(); + const updateCommand = testInjector.resolve(UpdateCommand); + sandbox.stub(updateCommand as any, "backup").throws(); + const executeCoreStub: sinon.SinonStub = sinon.stub(updateCommand as any, "executeCore"); + updateCommand.execute(["3.3.0"]); + + assert.isFalse(executeCoreStub.called); + assert.isTrue(rmStub.calledWith("-fr", path.join(projectFolder, (updateCommand as any).tempFolder))); + }); + }); + + describe("backup", () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("calls copy to temp for package.json and folders", async () => { + sandbox.stub(shelljs); + const cpStub: sinon.SinonStub = shelljs.cp as sinon.SinonStub; + const testInjector = createTestInjector(); + const updateCommand = testInjector.resolve(UpdateCommand); + sinon.stub(updateCommand as any, "executeCore"); + updateCommand.execute(["3.3.0"]); + + assert.isTrue(cpStub.calledWith(path.join(projectFolder, "package.json"))); + for (const folder of (updateCommand as any).folders) { + assert.isTrue(cpStub.calledWith("-rf", path.join(projectFolder, folder))); + } + }); + }); + + describe("backup", () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("calls copy to temp for package.json and folders", async () => { + sandbox.stub(shelljs); + const cpStub: sinon.SinonStub = shelljs.cp as sinon.SinonStub; + const testInjector = createTestInjector(); + const updateCommand = testInjector.resolve(UpdateCommand); + const tempDir = path.join(projectFolder, (updateCommand as any).tempFolder); + sinon.stub(updateCommand as any, "executeCore"); + (updateCommand as any).backup(tempDir); + + assert.isTrue(cpStub.calledWith(path.join(projectFolder, "package.json"))); + for (const folder of (updateCommand as any).folders) { + assert.isTrue(cpStub.calledWith("-rf", path.join(projectFolder, folder))); + } + }); + }); + + describe("restoreBackup", () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("calls copy to temp for package.json and folders", async () => { + sandbox.stub(shelljs); + const cpStub: sinon.SinonStub = shelljs.cp as sinon.SinonStub; + const testInjector = createTestInjector(); + const updateCommand = testInjector.resolve(UpdateCommand); + const tempDir = path.join(projectFolder, (updateCommand as any).tempFolder, projectFolder); + sinon.stub(updateCommand as any, "executeCore"); + (updateCommand as any).restoreBackup(tempDir); + + assert.isTrue(cpStub.calledWith("-f", path.join(tempDir, "package.json"), projectFolder)); + for (const folder of (updateCommand as any).folders) { + assert.isTrue(cpStub.calledWith("-fr", path.join(tempDir, folder), projectFolder)); + } + }); + }); + + describe("executeCore", () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("calls remove for all falders", async () => { + sandbox.stub(shelljs); + const rmStub: sinon.SinonStub = shelljs.rm as sinon.SinonStub; + const testInjector = createTestInjector(); + const updateCommand = testInjector.resolve(UpdateCommand); + return (updateCommand as any).executeCore([]).then(() => { + for (const folder of (updateCommand as any).folders) { + assert.isTrue(rmStub.calledWith("-rf", path.join(projectFolder, folder))); + } + }); + }); + + it("calls remove platforms and add platforms", async () => { + sandbox.stub(shelljs); + const installedPlatforms: string[] = ["android"]; + const testInjector = createTestInjector(installedPlatforms); + const platformService = testInjector.resolve("platformService"); + sandbox.spy(platformService, "addPlatforms"); + sandbox.spy(platformService, "removePlatforms"); + const updateCommand = testInjector.resolve(UpdateCommand); + return (updateCommand as any).executeCore([]).then(() => { + assert(platformService.removePlatforms.calledWith(installedPlatforms)); + assert(platformService.addPlatforms.calledWith(installedPlatforms)); + }); + }); + + it("call add platforms with specific verison", async () => { + sandbox.stub(shelljs); + const version = "3.3.0"; + const installedPlatforms: string[] = ["android"]; + const testInjector = createTestInjector(installedPlatforms); + const platformService = testInjector.resolve("platformService"); + sandbox.spy(platformService, "addPlatforms"); + sandbox.spy(platformService, "removePlatforms"); + const updateCommand = testInjector.resolve(UpdateCommand); + return (updateCommand as any).executeCore([version]).then(() => { + assert(platformService.addPlatforms.calledWith([`${installedPlatforms}@${version}`])); + }); + }); + + it("calls remove and add of core modules and widgets", async () => { + sandbox.stub(shelljs); + const testInjector = createTestInjector(); + const pluginsService = testInjector.resolve("pluginsService"); + sandbox.spy(pluginsService, "remove"); + sandbox.spy(pluginsService, "add"); + sandbox.spy(pluginsService, "ensureAllDependenciesAreInstalled"); + const updateCommand = testInjector.resolve(UpdateCommand); + return (updateCommand as any).executeCore([]).then(() => { + assert(pluginsService.add.calledWith("tns-core-modules")); + assert(pluginsService.remove.calledWith("tns-core-modules")); + assert(pluginsService.remove.calledWith("tns-core-modules-widgets")); + assert(pluginsService.ensureAllDependenciesAreInstalled.called); + }); + }); + + it("calls add of core modules with specific version", async () => { + sandbox.stub(shelljs); + const version = "3.3.0"; + const testInjector = createTestInjector(); + const pluginsService = testInjector.resolve("pluginsService"); + sandbox.spy(pluginsService, "remove"); + sandbox.spy(pluginsService, "add"); + sandbox.spy(pluginsService, "ensureAllDependenciesAreInstalled"); + const updateCommand = testInjector.resolve(UpdateCommand); + return (updateCommand as any).executeCore([version]).then(() => { + assert(pluginsService.add.calledWith(`tns-core-modules@${version}`)); + }); + }); + }); +}); From ce93e02e206341abc9b7d41a53c611e42ab06bce Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Mon, 27 Nov 2017 18:35:53 +0200 Subject: [PATCH 2/4] Fix comments. --- lib/commands/update.ts | 21 ++++++++++---------- test/update.ts | 44 ++++++++++++++++++------------------------ 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/lib/commands/update.ts b/lib/commands/update.ts index 229afea273..8e7ee94bbd 100644 --- a/lib/commands/update.ts +++ b/lib/commands/update.ts @@ -1,5 +1,4 @@ import * as path from "path"; -import * as shelljs from "shelljs"; export class UpdateCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; @@ -25,7 +24,7 @@ export class UpdateCommand implements ICommand { this.backup(tmpDir); } catch (error) { this.$logger.error("Could not backup project folders!"); - shelljs.rm("-fr", tmpDir); + this.$fs.deleteDirectory(tmpDir); return; } @@ -35,7 +34,7 @@ export class UpdateCommand implements ICommand { this.restoreBackup(tmpDir); this.$logger.error("Could not update the project!"); } finally { - shelljs.rm("-fr", tmpDir); + this.$fs.deleteDirectory(tmpDir); } } @@ -64,7 +63,7 @@ export class UpdateCommand implements ICommand { await this.$pluginsService.remove("tns-core-modules-widgets", this.$projectData); for (const folder of this.folders) { - shelljs.rm("-rf", path.join(this.$projectData.projectDir, folder)); + this.$fs.deleteDirectory(path.join(this.$projectData.projectDir, folder)); } if (args.length === 1) { @@ -101,26 +100,26 @@ export class UpdateCommand implements ICommand { } private restoreBackup(tmpDir: string): void { - shelljs.cp("-f", path.join(tmpDir, "package.json"), this.$projectData.projectDir); + this.$fs.copyFile(path.join(tmpDir, "package.json"), this.$projectData.projectDir); for (const folder of this.folders) { - shelljs.rm("-rf", path.join(this.$projectData.projectDir, folder)); + this.$fs.deleteDirectory(path.join(this.$projectData.projectDir, folder)); const folderToCopy = path.join(tmpDir, folder); if (this.$fs.exists(folderToCopy)) { - shelljs.cp("-fr", folderToCopy, this.$projectData.projectDir); + this.$fs.copyFile(folderToCopy, this.$projectData.projectDir); } } } private backup(tmpDir: string): void { - shelljs.rm("-fr", tmpDir); - shelljs.mkdir(tmpDir); - shelljs.cp(path.join(this.$projectData.projectDir, "package.json"), tmpDir); + this.$fs.deleteDirectory(tmpDir); + this.$fs.createDirectory(tmpDir); + this.$fs.copyFile(path.join(this.$projectData.projectDir, "package.json"), tmpDir); for (const folder of this.folders) { const folderToCopy = path.join(this.$projectData.projectDir, folder); if (this.$fs.exists(folderToCopy)) { - shelljs.cp("-rf", folderToCopy, tmpDir); + this.$fs.copyFile(folderToCopy, tmpDir); } } } diff --git a/test/update.ts b/test/update.ts index 6d683d40f7..1d100ce30e 100644 --- a/test/update.ts +++ b/test/update.ts @@ -8,7 +8,6 @@ import { Options } from "../lib/options"; import { AndroidProjectService } from "../lib/services/android-project-service"; import {StaticConfig } from "../lib/config"; import { SettingsService } from "../lib/common/test/unit-tests/stubs"; -import * as shelljs from "shelljs"; const projectFolder = "test"; function createTestInjector( @@ -72,7 +71,7 @@ function createTestInjector( return testInjector; } -describe("update command method tests", () => { +describe.only("update command method tests", () => { describe("canExecute", () => { it("calls platform service validate", async () => { let validated = false; @@ -129,7 +128,6 @@ describe("update command method tests", () => { }); it("calls backup and executeCore", async () => { - sandbox.stub(shelljs); const testInjector = createTestInjector(); const updateCommand = testInjector.resolve(UpdateCommand); const executeCoreStub: sinon.SinonStub = sinon.stub(updateCommand as any, "executeCore"); @@ -141,16 +139,16 @@ describe("update command method tests", () => { }); it("if backup fails, execute core not called and temp removed", async () => { - sandbox.stub(shelljs); - const rmStub: sinon.SinonStub = shelljs.rm as sinon.SinonStub; const testInjector = createTestInjector(); + const fs = testInjector.resolve("fs"); + const deleteDirectory: sinon.SinonStub = sandbox.stub(fs, "deleteDirectory"); const updateCommand = testInjector.resolve(UpdateCommand); sandbox.stub(updateCommand as any, "backup").throws(); const executeCoreStub: sinon.SinonStub = sinon.stub(updateCommand as any, "executeCore"); updateCommand.execute(["3.3.0"]); assert.isFalse(executeCoreStub.called); - assert.isTrue(rmStub.calledWith("-fr", path.join(projectFolder, (updateCommand as any).tempFolder))); + assert.isTrue(deleteDirectory.calledWith(path.join(projectFolder, (updateCommand as any).tempFolder))); }); }); @@ -166,16 +164,16 @@ describe("update command method tests", () => { }); it("calls copy to temp for package.json and folders", async () => { - sandbox.stub(shelljs); - const cpStub: sinon.SinonStub = shelljs.cp as sinon.SinonStub; const testInjector = createTestInjector(); + const fs = testInjector.resolve("fs"); + const copyFileStub = sandbox.stub(fs, "copyFile"); const updateCommand = testInjector.resolve(UpdateCommand); sinon.stub(updateCommand as any, "executeCore"); updateCommand.execute(["3.3.0"]); - assert.isTrue(cpStub.calledWith(path.join(projectFolder, "package.json"))); + assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, "package.json"))); for (const folder of (updateCommand as any).folders) { - assert.isTrue(cpStub.calledWith("-rf", path.join(projectFolder, folder))); + assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, folder))); } }); }); @@ -192,17 +190,17 @@ describe("update command method tests", () => { }); it("calls copy to temp for package.json and folders", async () => { - sandbox.stub(shelljs); - const cpStub: sinon.SinonStub = shelljs.cp as sinon.SinonStub; const testInjector = createTestInjector(); + const fs = testInjector.resolve("fs"); + const copyFileStub = sandbox.stub(fs, "copyFile"); const updateCommand = testInjector.resolve(UpdateCommand); const tempDir = path.join(projectFolder, (updateCommand as any).tempFolder); sinon.stub(updateCommand as any, "executeCore"); (updateCommand as any).backup(tempDir); - assert.isTrue(cpStub.calledWith(path.join(projectFolder, "package.json"))); + assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, "package.json"))); for (const folder of (updateCommand as any).folders) { - assert.isTrue(cpStub.calledWith("-rf", path.join(projectFolder, folder))); + assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, folder))); } }); }); @@ -219,17 +217,17 @@ describe("update command method tests", () => { }); it("calls copy to temp for package.json and folders", async () => { - sandbox.stub(shelljs); - const cpStub: sinon.SinonStub = shelljs.cp as sinon.SinonStub; const testInjector = createTestInjector(); + const fs = testInjector.resolve("fs"); + const copyFileStub = sandbox.stub(fs, "copyFile"); const updateCommand = testInjector.resolve(UpdateCommand); const tempDir = path.join(projectFolder, (updateCommand as any).tempFolder, projectFolder); sinon.stub(updateCommand as any, "executeCore"); (updateCommand as any).restoreBackup(tempDir); - assert.isTrue(cpStub.calledWith("-f", path.join(tempDir, "package.json"), projectFolder)); + assert.isTrue(copyFileStub.calledWith(path.join(tempDir, "package.json"), projectFolder)); for (const folder of (updateCommand as any).folders) { - assert.isTrue(cpStub.calledWith("-fr", path.join(tempDir, folder), projectFolder)); + assert.isTrue(copyFileStub.calledWith(path.join(tempDir, folder), projectFolder)); } }); }); @@ -246,19 +244,18 @@ describe("update command method tests", () => { }); it("calls remove for all falders", async () => { - sandbox.stub(shelljs); - const rmStub: sinon.SinonStub = shelljs.rm as sinon.SinonStub; const testInjector = createTestInjector(); + const fs = testInjector.resolve("fs"); + const deleteDirectory: sinon.SinonStub = sandbox.stub(fs, "deleteDirectory"); const updateCommand = testInjector.resolve(UpdateCommand); return (updateCommand as any).executeCore([]).then(() => { for (const folder of (updateCommand as any).folders) { - assert.isTrue(rmStub.calledWith("-rf", path.join(projectFolder, folder))); + assert.isTrue(deleteDirectory.calledWith(path.join(projectFolder, folder))); } }); }); it("calls remove platforms and add platforms", async () => { - sandbox.stub(shelljs); const installedPlatforms: string[] = ["android"]; const testInjector = createTestInjector(installedPlatforms); const platformService = testInjector.resolve("platformService"); @@ -272,7 +269,6 @@ describe("update command method tests", () => { }); it("call add platforms with specific verison", async () => { - sandbox.stub(shelljs); const version = "3.3.0"; const installedPlatforms: string[] = ["android"]; const testInjector = createTestInjector(installedPlatforms); @@ -286,7 +282,6 @@ describe("update command method tests", () => { }); it("calls remove and add of core modules and widgets", async () => { - sandbox.stub(shelljs); const testInjector = createTestInjector(); const pluginsService = testInjector.resolve("pluginsService"); sandbox.spy(pluginsService, "remove"); @@ -302,7 +297,6 @@ describe("update command method tests", () => { }); it("calls add of core modules with specific version", async () => { - sandbox.stub(shelljs); const version = "3.3.0"; const testInjector = createTestInjector(); const pluginsService = testInjector.resolve("pluginsService"); From adca13be832866dd074c086fa2e98dad1526a8f5 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Tue, 28 Nov 2017 16:30:46 +0200 Subject: [PATCH 3/4] Rewrite tests. --- lib/commands/update.ts | 17 ++--- test/update.ts | 143 +++++++++++------------------------------ 2 files changed, 48 insertions(+), 112 deletions(-) diff --git a/lib/commands/update.ts b/lib/commands/update.ts index 8e7ee94bbd..b94e17dfc7 100644 --- a/lib/commands/update.ts +++ b/lib/commands/update.ts @@ -1,4 +1,5 @@ import * as path from "path"; +import * as constants from "../constants"; export class UpdateCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; @@ -14,11 +15,11 @@ export class UpdateCommand implements ICommand { this.$projectData.initializeProjectData(); } - private folders: string[] = ["lib", "hooks", "platforms", "node_modules"]; - private tempFolder: string = ".tmp_backup"; + static readonly folders: string[] = ["lib", "hooks", "platforms", "node_modules"]; + static readonly tempFolder: string = ".tmp_backup"; public async execute(args: string[]): Promise { - const tmpDir = path.join(this.$projectData.projectDir, this.tempFolder); + const tmpDir = path.join(this.$projectData.projectDir, UpdateCommand.tempFolder); try { this.backup(tmpDir); @@ -62,7 +63,7 @@ export class UpdateCommand implements ICommand { await this.$pluginsService.remove("tns-core-modules", this.$projectData); await this.$pluginsService.remove("tns-core-modules-widgets", this.$projectData); - for (const folder of this.folders) { + for (const folder of UpdateCommand.folders) { this.$fs.deleteDirectory(path.join(this.$projectData.projectDir, folder)); } @@ -100,8 +101,8 @@ export class UpdateCommand implements ICommand { } private restoreBackup(tmpDir: string): void { - this.$fs.copyFile(path.join(tmpDir, "package.json"), this.$projectData.projectDir); - for (const folder of this.folders) { + this.$fs.copyFile(path.join(tmpDir, constants.PACKAGE_JSON_FILE_NAME), this.$projectData.projectDir); + for (const folder of UpdateCommand.folders) { this.$fs.deleteDirectory(path.join(this.$projectData.projectDir, folder)); const folderToCopy = path.join(tmpDir, folder); @@ -115,8 +116,8 @@ export class UpdateCommand implements ICommand { private backup(tmpDir: string): void { this.$fs.deleteDirectory(tmpDir); this.$fs.createDirectory(tmpDir); - this.$fs.copyFile(path.join(this.$projectData.projectDir, "package.json"), tmpDir); - for (const folder of this.folders) { + this.$fs.copyFile(path.join(this.$projectData.projectDir, constants.PACKAGE_JSON_FILE_NAME), tmpDir); + for (const folder of UpdateCommand.folders) { const folderToCopy = path.join(this.$projectData.projectDir, folder); if (this.$fs.exists(folderToCopy)) { this.$fs.copyFile(folderToCopy, tmpDir); diff --git a/test/update.ts b/test/update.ts index 1d100ce30e..dc40e1372a 100644 --- a/test/update.ts +++ b/test/update.ts @@ -71,7 +71,7 @@ function createTestInjector( return testInjector; } -describe.only("update command method tests", () => { +describe("update command method tests", () => { describe("canExecute", () => { it("calls platform service validate", async () => { let validated = false; @@ -127,120 +127,55 @@ describe.only("update command method tests", () => { sandbox.restore(); }); - it("calls backup and executeCore", async () => { - const testInjector = createTestInjector(); - const updateCommand = testInjector.resolve(UpdateCommand); - const executeCoreStub: sinon.SinonStub = sinon.stub(updateCommand as any, "executeCore"); - const backupStub: sinon.SinonStub = sinon.stub(updateCommand as any, "backup"); - updateCommand.execute(["3.3.0"]); - - assert.isTrue(backupStub.called); - assert.isTrue(executeCoreStub.called); - }); - - it("if backup fails, execute core not called and temp removed", async () => { - const testInjector = createTestInjector(); + it("if backup fails, pltforms not deleted and added, temp removed", async () => { + const installedPlatforms: string[] = ["android"]; + const testInjector = createTestInjector(installedPlatforms); const fs = testInjector.resolve("fs"); const deleteDirectory: sinon.SinonStub = sandbox.stub(fs, "deleteDirectory"); + const platformService = testInjector.resolve("platformService"); + sandbox.stub(fs, "copyFile").throws(); + sandbox.spy(platformService, "addPlatforms"); + sandbox.spy(platformService, "removePlatforms"); const updateCommand = testInjector.resolve(UpdateCommand); - sandbox.stub(updateCommand as any, "backup").throws(); - const executeCoreStub: sinon.SinonStub = sinon.stub(updateCommand as any, "executeCore"); - updateCommand.execute(["3.3.0"]); - - assert.isFalse(executeCoreStub.called); - assert.isTrue(deleteDirectory.calledWith(path.join(projectFolder, (updateCommand as any).tempFolder))); - }); - }); - - describe("backup", () => { - let sandbox: sinon.SinonSandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it("calls copy to temp for package.json and folders", async () => { - const testInjector = createTestInjector(); - const fs = testInjector.resolve("fs"); - const copyFileStub = sandbox.stub(fs, "copyFile"); - const updateCommand = testInjector.resolve(UpdateCommand); - sinon.stub(updateCommand as any, "executeCore"); - updateCommand.execute(["3.3.0"]); - - assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, "package.json"))); - for (const folder of (updateCommand as any).folders) { - assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, folder))); - } - }); - }); - - describe("backup", () => { - let sandbox: sinon.SinonSandbox; - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); + return updateCommand.execute(["3.3.0"]).then(() => { + assert.isTrue(deleteDirectory.calledWith(path.join(projectFolder, UpdateCommand.tempFolder))); + assert.isFalse(platformService.removePlatforms.calledWith(installedPlatforms)); + assert.isFalse(platformService.addPlatforms.calledWith(installedPlatforms)); + }); }); - it("calls copy to temp for package.json and folders", async () => { + it("calls copy to temp for package.json and folders(backup)", async () => { const testInjector = createTestInjector(); const fs = testInjector.resolve("fs"); const copyFileStub = sandbox.stub(fs, "copyFile"); const updateCommand = testInjector.resolve(UpdateCommand); - const tempDir = path.join(projectFolder, (updateCommand as any).tempFolder); - sinon.stub(updateCommand as any, "executeCore"); - (updateCommand as any).backup(tempDir); - - assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, "package.json"))); - for (const folder of (updateCommand as any).folders) { - assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, folder))); - } - }); - }); - - describe("restoreBackup", () => { - let sandbox: sinon.SinonSandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); + return updateCommand.execute(["3.3.0"]).then( () => { + assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, "package.json"))); + for (const folder of UpdateCommand.folders) { + assert.isTrue(copyFileStub.calledWith(path.join(projectFolder, folder))); + } + }); }); - it("calls copy to temp for package.json and folders", async () => { + it("calls copy from temp for package.json and folders to project folder(restore)", async () => { const testInjector = createTestInjector(); + testInjector.resolve("platformService").removePlatforms = () => { + throw new Error(); + }; const fs = testInjector.resolve("fs"); + const deleteDirectoryStub: sinon.SinonStub = sandbox.stub(fs, "deleteDirectory"); const copyFileStub = sandbox.stub(fs, "copyFile"); const updateCommand = testInjector.resolve(UpdateCommand); - const tempDir = path.join(projectFolder, (updateCommand as any).tempFolder, projectFolder); - sinon.stub(updateCommand as any, "executeCore"); - (updateCommand as any).restoreBackup(tempDir); - - assert.isTrue(copyFileStub.calledWith(path.join(tempDir, "package.json"), projectFolder)); - for (const folder of (updateCommand as any).folders) { - assert.isTrue(copyFileStub.calledWith(path.join(tempDir, folder), projectFolder)); - } - }); - }); - - describe("executeCore", () => { - let sandbox: sinon.SinonSandbox; + const tempDir = path.join(projectFolder, UpdateCommand.tempFolder); - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); + return updateCommand.execute(["3.3.0"]).then(() => { + assert.isTrue(copyFileStub.calledWith(path.join(tempDir, "package.json"), projectFolder)); + for (const folder of UpdateCommand.folders) { + assert.isTrue(deleteDirectoryStub.calledWith(path.join(projectFolder, folder))); + assert.isTrue(copyFileStub.calledWith(path.join(tempDir, folder), projectFolder)); + } + }); }); it("calls remove for all falders", async () => { @@ -248,8 +183,8 @@ describe.only("update command method tests", () => { const fs = testInjector.resolve("fs"); const deleteDirectory: sinon.SinonStub = sandbox.stub(fs, "deleteDirectory"); const updateCommand = testInjector.resolve(UpdateCommand); - return (updateCommand as any).executeCore([]).then(() => { - for (const folder of (updateCommand as any).folders) { + return updateCommand.execute([]).then(() => { + for (const folder of UpdateCommand.folders) { assert.isTrue(deleteDirectory.calledWith(path.join(projectFolder, folder))); } }); @@ -262,7 +197,7 @@ describe.only("update command method tests", () => { sandbox.spy(platformService, "addPlatforms"); sandbox.spy(platformService, "removePlatforms"); const updateCommand = testInjector.resolve(UpdateCommand); - return (updateCommand as any).executeCore([]).then(() => { + return updateCommand.execute([]).then(() => { assert(platformService.removePlatforms.calledWith(installedPlatforms)); assert(platformService.addPlatforms.calledWith(installedPlatforms)); }); @@ -276,7 +211,7 @@ describe.only("update command method tests", () => { sandbox.spy(platformService, "addPlatforms"); sandbox.spy(platformService, "removePlatforms"); const updateCommand = testInjector.resolve(UpdateCommand); - return (updateCommand as any).executeCore([version]).then(() => { + return updateCommand.execute([version]).then(() => { assert(platformService.addPlatforms.calledWith([`${installedPlatforms}@${version}`])); }); }); @@ -288,7 +223,7 @@ describe.only("update command method tests", () => { sandbox.spy(pluginsService, "add"); sandbox.spy(pluginsService, "ensureAllDependenciesAreInstalled"); const updateCommand = testInjector.resolve(UpdateCommand); - return (updateCommand as any).executeCore([]).then(() => { + return updateCommand.execute([]).then(() => { assert(pluginsService.add.calledWith("tns-core-modules")); assert(pluginsService.remove.calledWith("tns-core-modules")); assert(pluginsService.remove.calledWith("tns-core-modules-widgets")); @@ -304,7 +239,7 @@ describe.only("update command method tests", () => { sandbox.spy(pluginsService, "add"); sandbox.spy(pluginsService, "ensureAllDependenciesAreInstalled"); const updateCommand = testInjector.resolve(UpdateCommand); - return (updateCommand as any).executeCore([version]).then(() => { + return updateCommand.execute([version]).then(() => { assert(pluginsService.add.calledWith(`tns-core-modules@${version}`)); }); }); From 6bf0f6d2d248c4b089989f5aee4feb3e2f197b61 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Thu, 30 Nov 2017 09:58:23 +0200 Subject: [PATCH 4/4] Fix comments. --- lib/commands/update.ts | 13 ++++++++++--- lib/constants.ts | 2 ++ test/update.ts | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/commands/update.ts b/lib/commands/update.ts index b94e17dfc7..d097f37125 100644 --- a/lib/commands/update.ts +++ b/lib/commands/update.ts @@ -15,8 +15,15 @@ export class UpdateCommand implements ICommand { this.$projectData.initializeProjectData(); } - static readonly folders: string[] = ["lib", "hooks", "platforms", "node_modules"]; + static readonly folders: string[] = [ + constants.LIB_DIR_NAME, + constants.HOOKS_DIR_NAME, + constants.PLATFORMS_DIR_NAME, + constants.NODE_MODULES_FOLDER_NAME + ]; static readonly tempFolder: string = ".tmp_backup"; + static readonly updateFailMessage: string = "Could not update the project!"; + static readonly backupFailMessage: string = "Could not backup project folders!"; public async execute(args: string[]): Promise { const tmpDir = path.join(this.$projectData.projectDir, UpdateCommand.tempFolder); @@ -24,7 +31,7 @@ export class UpdateCommand implements ICommand { try { this.backup(tmpDir); } catch (error) { - this.$logger.error("Could not backup project folders!"); + this.$logger.error(UpdateCommand.backupFailMessage); this.$fs.deleteDirectory(tmpDir); return; } @@ -33,7 +40,7 @@ export class UpdateCommand implements ICommand { await this.executeCore(args); } catch (error) { this.restoreBackup(tmpDir); - this.$logger.error("Could not update the project!"); + this.$logger.error(UpdateCommand.updateFailMessage); } finally { this.$fs.deleteDirectory(tmpDir); } diff --git a/lib/constants.ts b/lib/constants.ts index 66d6b3b65c..0525e4f425 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -16,6 +16,8 @@ export const TEST_RUNNER_NAME = "nativescript-unit-test-runner"; export const LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"]; export const XML_FILE_EXTENSION = ".xml"; export const PLATFORMS_DIR_NAME = "platforms"; +export const HOOKS_DIR_NAME = "hooks"; +export const LIB_DIR_NAME = "lib"; export const CODE_SIGN_ENTITLEMENTS = "CODE_SIGN_ENTITLEMENTS"; export const AWAIT_NOTIFICATION_TIMEOUT_SECONDS = 9; export const SRC_DIR = "src"; diff --git a/test/update.ts b/test/update.ts index dc40e1372a..3f9343d41c 100644 --- a/test/update.ts +++ b/test/update.ts @@ -178,7 +178,7 @@ describe("update command method tests", () => { }); }); - it("calls remove for all falders", async () => { + it("calls remove for all folders", async () => { const testInjector = createTestInjector(); const fs = testInjector.resolve("fs"); const deleteDirectory: sinon.SinonStub = sandbox.stub(fs, "deleteDirectory");