UNPKG

8.64 kBJavaScriptView Raw
1var assert = require('assert');
2var async = require('async');
3var sinon = require('sinon');
4var shortid = require('shortid');
5var _ = require('lodash');
6var path = require('path');
7var parseUrl = require('url').parse;
8var rimraf = require('rimraf');
9var log = require('../lib/log');
10var fs = require('fs');
11var childProcess = require('child_process');
12var request = require('request');
13var inquirer = require('inquirer');
14var createApp = require('../commands/create-app');
15var os = require('os');
16var debug = require('debug')('4front:cli:create-app-test');
17var mockSpawn = require('./mock-spawn');
18var mockInquirer = require('./mock-inquirer');
19
20require('dash-assert');
21
22var sampleTemplate = path.resolve(__dirname, "./fixtures/sample-app-template.zip");
23var sampleTemplateWithRoot = path.resolve(__dirname, "./fixtures/sample-app-with-root-dir.zip");
24
25describe('create-app', function() {
26 var self;
27
28 beforeEach(function(done) {
29 self = this;
30
31 this.program = {
32 profile: {
33 name: 'default',
34 endpoint: 'https://apphost.com/',
35 jwt: {
36 token: '23523454'
37 }
38 },
39 baseDir: os.tmpdir()
40 };
41
42 this.mockAnswers = {
43 appName: 'test-app',
44 endpoint: 'https://apphost.com',
45 username: 'username',
46 password: 'password'
47 };
48
49 this.mockInquirer = require('./mock-inquirer')(this.mockAnswers);
50 this.mockOrgs = [{orgId: '1', name: 'org 1'}, {orgId: '2', name: 'org 2'}];
51 this.mockTemplates = [{name: 'template1', url:'http://github.com/repo.tar.gz'}];
52
53 sinon.stub(log, 'write', _.noop());
54
55 sinon.stub(request, 'get', function(options, callback) {
56 var urlPath = parseUrl(options.url).pathname;
57 switch (urlPath) {
58 case '/api/platform/starter-templates':
59 return callback(null, {statusCode: 200}, self.mockTemplates);
60 break;
61 case '/api/profile/orgs':
62 return callback(null, {statusCode: 200}, self.mockOrgs);
63 break;
64 case '/sample-app-template.zip':
65 // Return the readStream directly. The way this call is made
66 // request is not passing in a callback.
67 return fs.createReadStream(sampleTemplate);
68 break;
69 case '/sample-app-with-root-dir.zip':
70 // Return the readStream directly. The way this call is made
71 // request is not passing in a callback.
72 return fs.createReadStream(sampleTemplateWithRoot);
73
74 default:
75 throw new Error("Unexpected request " + urlPath);
76 }
77 });
78
79 sinon.stub(request, 'head').yields(null, {statusCode:404});
80
81 sinon.stub(request, 'post', function(options, callback) {
82 // Mock the create app post request
83 if (options.url.indexOf('/apps')) {
84 callback(null, {statusCode: 201}, {
85 appId: options.json.appId,
86 name: options.json.name,
87 url: 'https://' + options.json.name + '.apphost.com'
88 });
89 }
90 });
91
92 sinon.stub(inquirer, 'prompt', this.mockInquirer.prompt);
93 sinon.stub(childProcess, 'spawn', mockSpawn);
94
95 rimraf(path.join(os.tmpdir(), 'test-app'), done);
96 });
97
98 afterEach(function() {
99 request.post.restore();
100 request.head.restore();
101 request.get.restore();
102 inquirer.prompt.restore();
103 log.write.restore();
104 childProcess.spawn.restore();
105 });
106
107 it('collect input', function(done) {
108 _.extend(this.mockAnswers, {
109 templateUrl: 'blank',
110 startingMode: 'scratch'
111 });
112
113 createApp(this.program, function(err) {
114 if (err) return done(err);
115
116 assert.isTrue(request.get.calledWith(
117 sinon.match({url: 'https://apphost.com/api/profile/orgs'})))
118
119 assert.isTrue(request.get.calledWith(
120 sinon.match({url: 'https://apphost.com/api/platform/starter-templates'})))
121
122 assert.isTrue(request.head.calledWith(
123 sinon.match({url: 'https://apphost.com/api/apps/test-app'})))
124
125 assert.isTrue(self.mockInquirer.wasAsked('orgId'))
126 assert.isTrue(self.mockInquirer.wasAsked('appName'));
127 assert.isTrue(self.mockInquirer.wasAsked('startingMode'));
128 assert.isTrue(self.mockInquirer.wasAsked('templateUrl'));
129 assert.isFalse(self.mockInquirer.wasAsked('confirmExistingDir'));
130 done();
131 });
132 });
133
134 describe('downloads starter template', function() {
135 it('uses command line template arg', function(done) {
136 _.extend(this.program, {
137 templateUrl: "http://github.com/sample-app-template.zip"
138 });
139
140 createApp(this.program, function(err, createdApp) {
141 if (err) return done(err);
142
143 var appDir = path.join(self.program.baseDir, self.mockAnswers.appName);
144
145 assert.isFalse(self.mockInquirer.wasAsked('startingMode'));
146
147 assert.isTrue(request.get.calledWith(
148 sinon.match({url: self.program.templateUrl})));
149
150 // Assert that both npm and bower install were run
151 assert.isTrue(childProcess.spawn.calledWith('npm', ['install'], sinon.match({cwd: appDir})));
152 assert.isTrue(childProcess.spawn.calledWith('bower', ['install'], sinon.match({cwd: appDir})));
153
154 // verify that the extracted contents from the template zip are present
155 ['index.html', 'js/app.js', 'css/styles.css', 'package.json'].forEach(function(file) {
156 var filePath = path.join(appDir, file);
157 assert.isTrue(fs.existsSync(filePath));
158 });
159
160 var packageJson = JSON.parse(fs.readFileSync(path.join(appDir, 'package.json')));
161 assert.equal(packageJson['_virtualApp'].appId, createdApp.appId);
162
163 done();
164 });
165 });
166
167 it('uses template specified at prompt', function(done) {
168 _.extend(this.mockAnswers, {
169 startingMode: 'scratch',
170 templateUrl: 'http://github.com/sample-app-template.zip'
171 });
172
173 createApp(this.program, function(err) {
174 if (err) return done(err);
175
176 assert.isTrue(self.mockInquirer.wasAsked('templateUrl'));
177 assert.isTrue(request.get.calledWith(
178 sinon.match({url: self.mockAnswers.templateUrl})));
179
180 done();
181 });
182 });
183
184 it('skips root directory in zip archive', function(done) {
185 _.extend(this.mockAnswers, {
186 startingMode: 'scratch',
187 templateUrl: 'http://github.com/sample-app-with-root-dir.zip'
188 });
189
190 createApp(this.program, function(err) {
191 if (err) return done(err);
192
193 var appDir = path.join(self.program.baseDir, self.mockAnswers.appName);
194
195 // verify that the extracted contents from the template zip are present
196 ['index.html', 'js/app.js'].forEach(function(file) {
197 var filePath = path.join(appDir, 'app', file);
198
199 debug('check if %s exists', filePath);
200 assert.isTrue(fs.existsSync(filePath));
201 });
202
203 done();
204 });
205 });
206 });
207
208 it('returns error when app directory already exists', function(done) {
209 _.extend(this.mockAnswers, {
210 appName: shortid.generate(),
211 startingMode: 'scratch',
212 templateUrl: 'blank'
213 });
214
215 fs.mkdirSync(path.join(self.program.baseDir, this.mockAnswers.appName));
216
217 createApp(this.program, function(err) {
218 assert.isNotNull(err);
219 assert.ok(/already exists/.test(err));
220 done();
221 });
222 });
223
224 it('creates index.html when no template chosen', function(done) {
225 _.extend(this.mockAnswers, {
226 startingMode: 'scratch',
227 templateUrl: 'blank'
228 });
229
230 createApp(this.program, function(err, createdApp) {
231 if (err) return done(err);
232
233 var appDir = path.join(self.program.baseDir, self.mockAnswers.appName);
234
235 ['index.html', 'package.json'].forEach(function(file) {
236 var filePath = path.join(appDir, file);
237 assert.isTrue(fs.existsSync(filePath));
238 });
239
240 // Look at the package.json
241 var packageJson = JSON.parse(fs.readFileSync(path.join(appDir, 'package.json')));
242 assert.isObject(packageJson['_virtualApp']);
243 assert.equal(packageJson['_virtualApp'].appId, createdApp.appId);
244
245 done();
246 });
247 });
248
249 it('continues to ask for appName until one available', function(done) {
250 request.head.restore();
251
252 var headStub = sinon.stub(request, 'head');
253 headStub.onCall(0).yields(null, {statusCode: 200});
254 headStub.onCall(1).yields(null, {statusCode: 200});
255 // On the 3rd call yield a 404 indicating the app name is available
256 headStub.onCall(2).yields(null, {statusCode: 404});
257
258 createApp(this.program, function(err) {
259 if (err) return done(err);
260
261 // The appName question should have been asked 3 times
262 assert.equal(self.mockInquirer.askedCount('appName'), 3);
263
264 done();
265 });
266 });
267});