Skip to content

Commit b470c6c

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

File tree

2 files changed

+94
-20
lines changed

2 files changed

+94
-20
lines changed

test/project-commands.ts

+86-17
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
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 createProjectCalledWithForce: boolean;
18+
let validateProjectCallsCount: number;
1019
const dummyArgs = ["dummyArgsString"];
1120

1221
class ProjectServiceMock implements IProjectService {
13-
async validateProjectName(opts: { projectName: string, force: boolean, pathToProject: string }) : Promise<string> {
22+
async validateProjectName(opts: { projectName: string, force: boolean, pathToProject: string }): Promise<string> {
23+
validateProjectCallsCount++;
1424
return null;
1525
}
1626

1727
async createProject(projectOptions: IProjectSettings): Promise<ICreateProjectData> {
28+
createProjectCalledWithForce = projectOptions.force;
1829
selectedTemplateName = projectOptions.template;
1930
isProjectCreated = true;
2031
return null;
@@ -45,7 +56,8 @@ function createTestInjector() {
4556
template: undefined
4657
});
4758
testInjector.register("createCommand", CreateProjectCommand);
48-
testInjector.register("stringParameterBuilder", StringParameterBuilder);
59+
testInjector.register("stringParameter", StringCommandParameter);
60+
testInjector.register("prompter", PrompterStub);
4961

5062
return testInjector;
5163
}
@@ -55,9 +67,34 @@ describe("Project commands tests", () => {
5567
let options: IOptions;
5668
let createProjectCommand: ICommand;
5769

70+
function setupAnswers(opts: {
71+
projectNameAnswer?: string,
72+
flavorAnswer?: string,
73+
templateAnswer?: string,
74+
}) {
75+
const prompterStub = <stubs.PrompterStub>testInjector.resolve("$prompter");
76+
const choices: IDictionary<string> = {};
77+
if (opts.projectNameAnswer) {
78+
choices["First, what will be the name of your app?"] = opts.projectNameAnswer;
79+
}
80+
if (opts.flavorAnswer) {
81+
choices[opts.projectNameAnswer ? "Next" : "First" + ", which flavor would you like to use?"] = opts.flavorAnswer;
82+
}
83+
if (opts.templateAnswer) {
84+
choices[opts.projectNameAnswer ? "Finally" : "Next" + ", which template would you like to start from?"] = opts.templateAnswer;
85+
}
86+
87+
prompterStub.expect({
88+
choices
89+
});
90+
}
91+
5892
beforeEach(() => {
5993
testInjector = createTestInjector();
94+
helpers.isInteractive = () => true;
6095
isProjectCreated = false;
96+
validateProjectCallsCount = 0;
97+
createProjectCalledWithForce = false;
6198
selectedTemplateName = undefined;
6299
options = testInjector.resolve("$options");
63100
createProjectCommand = testInjector.resolve("$createCommand");
@@ -70,6 +107,8 @@ describe("Project commands tests", () => {
70107
await createProjectCommand.execute(dummyArgs);
71108

72109
assert.isTrue(isProjectCreated);
110+
assert.equal(validateProjectCallsCount, 1);
111+
assert.isTrue(createProjectCalledWithForce);
73112
});
74113

75114
it("should not fail when using only --tsc.", async () => {
@@ -78,6 +117,8 @@ describe("Project commands tests", () => {
78117
await createProjectCommand.execute(dummyArgs);
79118

80119
assert.isTrue(isProjectCreated);
120+
assert.equal(validateProjectCallsCount, 1);
121+
assert.isTrue(createProjectCalledWithForce);
81122
});
82123

83124
it("should not fail when using only --template.", async () => {
@@ -86,6 +127,8 @@ describe("Project commands tests", () => {
86127
await createProjectCommand.execute(dummyArgs);
87128

88129
assert.isTrue(isProjectCreated);
130+
assert.equal(validateProjectCallsCount, 1);
131+
assert.isTrue(createProjectCalledWithForce);
89132
});
90133

91134
it("should set the template name correctly when used --ng.", async () => {
@@ -94,6 +137,8 @@ describe("Project commands tests", () => {
94137
await createProjectCommand.execute(dummyArgs);
95138

96139
assert.deepEqual(selectedTemplateName, constants.ANGULAR_NAME);
140+
assert.equal(validateProjectCallsCount, 1);
141+
assert.isTrue(createProjectCalledWithForce);
97142
});
98143

99144
it("should set the template name correctly when used --tsc.", async () => {
@@ -102,36 +147,60 @@ describe("Project commands tests", () => {
102147
await createProjectCommand.execute(dummyArgs);
103148

104149
assert.deepEqual(selectedTemplateName, constants.TYPESCRIPT_NAME);
150+
assert.equal(validateProjectCallsCount, 1);
151+
assert.isTrue(createProjectCalledWithForce);
152+
});
153+
154+
it("should fail when --ng and --template are used simultaneously.", async () => {
155+
options.ng = true;
156+
options.template = "ng";
157+
158+
await assert.isRejected(createProjectCommand.execute(dummyArgs));
159+
});
160+
161+
it("should fail when --tsc and --template are used simultaneously.", async () => {
162+
options.tsc = true;
163+
options.template = "tsc";
164+
165+
await assert.isRejected(createProjectCommand.execute(dummyArgs));
105166
});
106167

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

110171
await createProjectCommand.execute(dummyArgs);
111172

112-
assert.isUndefined(selectedTemplateName);
173+
assert.deepEqual(selectedTemplateName, "tns-template-hello-world-ng");
174+
assert.equal(validateProjectCallsCount, 1);
175+
assert.isTrue(createProjectCalledWithForce);
113176
});
114177

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

118181
await createProjectCommand.execute(dummyArgs);
119182

120-
assert.isUndefined(selectedTemplateName);
183+
assert.deepEqual(selectedTemplateName, "tns-template-hello-world-ts");
184+
assert.equal(validateProjectCallsCount, 1);
185+
assert.isTrue(createProjectCalledWithForce);
121186
});
122187

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

127-
await assert.isRejected(createProjectCommand.execute(dummyArgs));
191+
await createProjectCommand.execute(dummyArgs);
192+
193+
assert.deepEqual(selectedTemplateName, "tns-template-hello-world");
194+
assert.equal(validateProjectCallsCount, 1);
195+
assert.isTrue(createProjectCalledWithForce);
128196
});
129197

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

134-
await assert.isRejected(createProjectCommand.execute(dummyArgs));
201+
await createProjectCommand.execute(dummyArgs);
202+
203+
assert.deepEqual(selectedTemplateName, "https://github.com/NativeScript/template-blank-vue/tarball/0.9.0");
135204
});
136205
});
137206
});

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)