Skip to content

Commit b2e3a57

Browse files
author
DimitarTachev
committed
test: add a few tests for the interactive app creation
1 parent c595c0e commit b2e3a57

File tree

2 files changed

+83
-20
lines changed

2 files changed

+83
-20
lines changed

test/project-commands.ts

+75-17
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
import { Yok } from "../lib/common/yok";
22
import * as stubs from "./stubs";
33
import { CreateProjectCommand } from "../lib/commands/create-project";
4-
import { StringParameterBuilder } from "../lib/common/command-params";
4+
import { StringCommandParameter } from "../lib/common/command-params";
5+
import helpers = require("../lib/common/helpers");
56
import * as constants from "../lib/constants";
67
import { assert } from "chai";
8+
import { PrompterStub } from "./stubs";
9+
10+
const NgFlavor = "Angular";
11+
const VueFlavor = "Vue.js";
12+
const TsFlavor = "Plain TypeScript";
13+
const JsFlavor = "Plain JavaScript";
714

815
let selectedTemplateName: string;
916
let isProjectCreated: boolean;
17+
let validateProjectCallsCount: number;
1018
const dummyArgs = ["dummyArgsString"];
1119

1220
class ProjectServiceMock implements IProjectService {
13-
async validateProjectName(opts: { projectName: string, force: boolean, pathToProject: string }) : Promise<string> {
21+
async validateProjectName(opts: { projectName: string, force: boolean, pathToProject: string }): Promise<string> {
22+
validateProjectCallsCount++;
1423
return null;
1524
}
1625

@@ -45,7 +54,8 @@ function createTestInjector() {
4554
template: undefined
4655
});
4756
testInjector.register("createCommand", CreateProjectCommand);
48-
testInjector.register("stringParameterBuilder", StringParameterBuilder);
57+
testInjector.register("stringParameter", StringCommandParameter);
58+
testInjector.register("prompter", PrompterStub);
4959

5060
return testInjector;
5161
}
@@ -55,9 +65,33 @@ describe("Project commands tests", () => {
5565
let options: IOptions;
5666
let createProjectCommand: ICommand;
5767

68+
function setupAnswers(opts: {
69+
projectNameAnswer?: string,
70+
flavorAnswer?: string,
71+
templateAnswer?: string,
72+
}) {
73+
const prompterStub = <stubs.PrompterStub>testInjector.resolve("$prompter");
74+
const choices: IDictionary<string> = {};
75+
if (opts.projectNameAnswer) {
76+
choices["First, what will be the name of your app?"] = opts.projectNameAnswer;
77+
}
78+
if (opts.flavorAnswer) {
79+
choices[opts.projectNameAnswer ? "Next" : "First" + ", which flavor would you like to use?"] = opts.flavorAnswer;
80+
}
81+
if (opts.templateAnswer) {
82+
choices[opts.projectNameAnswer ? "Finally" : "Next" + ", which template would you like to start from?"] = opts.templateAnswer;
83+
}
84+
85+
prompterStub.expect({
86+
choices
87+
});
88+
}
89+
5890
beforeEach(() => {
5991
testInjector = createTestInjector();
92+
helpers.isInteractive = () => true;
6093
isProjectCreated = false;
94+
validateProjectCallsCount = 0;
6195
selectedTemplateName = undefined;
6296
options = testInjector.resolve("$options");
6397
createProjectCommand = testInjector.resolve("$createCommand");
@@ -70,6 +104,7 @@ describe("Project commands tests", () => {
70104
await createProjectCommand.execute(dummyArgs);
71105

72106
assert.isTrue(isProjectCreated);
107+
assert.equal(validateProjectCallsCount, 1);
73108
});
74109

75110
it("should not fail when using only --tsc.", async () => {
@@ -78,6 +113,7 @@ describe("Project commands tests", () => {
78113
await createProjectCommand.execute(dummyArgs);
79114

80115
assert.isTrue(isProjectCreated);
116+
assert.equal(validateProjectCallsCount, 1);
81117
});
82118

83119
it("should not fail when using only --template.", async () => {
@@ -86,6 +122,7 @@ describe("Project commands tests", () => {
86122
await createProjectCommand.execute(dummyArgs);
87123

88124
assert.isTrue(isProjectCreated);
125+
assert.equal(validateProjectCallsCount, 1);
89126
});
90127

91128
it("should set the template name correctly when used --ng.", async () => {
@@ -94,6 +131,7 @@ describe("Project commands tests", () => {
94131
await createProjectCommand.execute(dummyArgs);
95132

96133
assert.deepEqual(selectedTemplateName, constants.ANGULAR_NAME);
134+
assert.equal(validateProjectCallsCount, 1);
97135
});
98136

99137
it("should set the template name correctly when used --tsc.", async () => {
@@ -102,36 +140,56 @@ describe("Project commands tests", () => {
102140
await createProjectCommand.execute(dummyArgs);
103141

104142
assert.deepEqual(selectedTemplateName, constants.TYPESCRIPT_NAME);
143+
assert.equal(validateProjectCallsCount, 1);
144+
});
145+
146+
it("should fail when --ng and --template are used simultaneously.", async () => {
147+
options.ng = true;
148+
options.template = "ng";
149+
150+
await assert.isRejected(createProjectCommand.execute(dummyArgs));
151+
});
152+
153+
it("should fail when --tsc and --template are used simultaneously.", async () => {
154+
options.tsc = true;
155+
options.template = "tsc";
156+
157+
await assert.isRejected(createProjectCommand.execute(dummyArgs));
105158
});
106159

107-
it("should not set the template name when --ng is not used.", async () => {
108-
options.ng = false;
160+
it("should ask for a template when ng flavor is selected.", async () => {
161+
setupAnswers({ flavorAnswer: NgFlavor, templateAnswer: "Hello World" });
109162

110163
await createProjectCommand.execute(dummyArgs);
111164

112-
assert.isUndefined(selectedTemplateName);
165+
assert.deepEqual(selectedTemplateName, "tns-template-hello-world-ng");
166+
assert.equal(validateProjectCallsCount, 1);
113167
});
114168

115-
it("should not set the template name when --tsc is not used.", async () => {
116-
options.tsc = false;
169+
it("should ask for a template when ts flavor is selected.", async () => {
170+
setupAnswers({ flavorAnswer: TsFlavor, templateAnswer: "Hello World" });
117171

118172
await createProjectCommand.execute(dummyArgs);
119173

120-
assert.isUndefined(selectedTemplateName);
174+
assert.deepEqual(selectedTemplateName, "tns-template-hello-world-ts");
175+
assert.equal(validateProjectCallsCount, 1);
121176
});
122177

123-
it("should fail when --ng and --template are used simultaneously.", async () => {
124-
options.ng = true;
125-
options.template = "ng";
178+
it("should ask for a template when js flavor is selected.", async () => {
179+
setupAnswers({ flavorAnswer: JsFlavor, templateAnswer: "Hello World" });
126180

127-
await assert.isRejected(createProjectCommand.execute(dummyArgs));
181+
await createProjectCommand.execute(dummyArgs);
182+
183+
assert.deepEqual(selectedTemplateName, "tns-template-hello-world");
184+
assert.equal(validateProjectCallsCount, 1);
128185
});
129186

130-
it("should fail when --tsc and --template are used simultaneously.", async () => {
131-
options.tsc = true;
132-
options.template = "tsc";
187+
it("should select the default vue template when the vue flavor is selected.", async () => {
188+
setupAnswers({ flavorAnswer: VueFlavor });
133189

134-
await assert.isRejected(createProjectCommand.execute(dummyArgs));
190+
await createProjectCommand.execute(dummyArgs);
191+
192+
assert.deepEqual(selectedTemplateName, "https://github.com/NativeScript/template-blank-vue/tarball/0.9.0");
135193
});
136194
});
137195
});

test/stubs.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -514,11 +514,13 @@ export class LockFile {
514514
export class PrompterStub implements IPrompter {
515515
private strings: IDictionary<string> = {};
516516
private passwords: IDictionary<string> = {};
517+
private choices: IDictionary<string> = {};
517518

518-
expect(options?: { strings: IDictionary<string>, passwords: IDictionary<string> }) {
519+
expect(options?: { strings?: IDictionary<string>, passwords?: IDictionary<string>, choices?: IDictionary<string> }) {
519520
if (options) {
520521
this.strings = options.strings || this.strings;
521522
this.passwords = options.passwords || this.passwords;
523+
this.choices = options.choices || this.choices;
522524
}
523525
}
524526

@@ -540,8 +542,11 @@ export class PrompterStub implements IPrompter {
540542
async promptForChoice(promptMessage: string, choices: any[]): Promise<string> {
541543
throw unreachable();
542544
}
543-
async promptForDetailedChoice(promptMessage: string, choices: any[]): Promise<string> {
544-
throw unreachable();
545+
async promptForDetailedChoice(question: string, choices: any[]): Promise<string> {
546+
chai.assert.ok(question in this.choices, `PrompterStub didn't expect to be asked: ${question}`);
547+
const result = this.choices[question];
548+
delete this.choices[question];
549+
return result;
545550
}
546551
async confirm(prompt: string, defaultAction?: () => boolean): Promise<boolean> {
547552
throw unreachable();

0 commit comments

Comments
 (0)