Skip to content

Commit 6cee3a3

Browse files
ErisDSacburdine
authored andcommitted
fix(ui): 🎨 template prompt adjustments
refs #313 - don't allow users to skip, as this would break things - only show the option to continue, view or edit if --verbose is passed and --no-prompt is not - reword the question to be would you like to view or edit - fix the duplicate word "file" for systemd - add a ui.log for creating the files - TODO: use a checkmark?!
1 parent f7da9dc commit 6cee3a3

File tree

3 files changed

+112
-82
lines changed

3 files changed

+112
-82
lines changed

‎extensions/systemd/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class SystemdExtension extends cli.Extension {
3333
user: uid,
3434
environment: this.system.environment,
3535
ghost_exec_path: process.argv.slice(0,2).join(' ')
36-
}), 'systemd service file', serviceFilename, '/lib/systemd/system').then((generated) => {
36+
}), 'systemd service', serviceFilename, '/lib/systemd/system').then((generated) => {
3737
if (!generated) {
3838
this.ui.log('Systemd unit file not generated', 'yellow');
3939
return;

‎lib/instance.js

+43-46
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const Promise = require('bluebird');
1313
*/
1414
class Instance {
1515
/**
16-
* Local insatne config (.ghost-cli)
16+
* Local instance config (.ghost-cli)
1717
* Contains some instance-specific variables
1818
*
1919
* @property
@@ -207,61 +207,57 @@ class Instance {
207207
* @public
208208
*/
209209
template(contents, descriptor, file, dir) {
210-
// If `--no-prompt` is passed to the CLI, just generate the template
211-
if (!this.ui.allowPrompt) {
212-
return this._generateTemplate(contents, file, dir);
210+
// If `--no-prompt` is passed to the CLI or the `--verbose` flag was not passed, don't show anything
211+
if (!this.ui.allowPrompt || !this.ui.verbose) {
212+
return this._generateTemplate(contents, descriptor, file, dir);
213+
} else {
214+
return this.ui.prompt({
215+
type: 'expand',
216+
name: 'choice',
217+
message: `Would you like to view or edit the ${descriptor} file?`,
218+
default: 'n',
219+
choices: [
220+
{key: 'n', name: 'No, continue', value: 'continue'},
221+
{key: 'v', name: 'View the file', value: 'view'},
222+
{key: 'e', name: 'Edit the file before generation', value: 'edit'}
223+
]
224+
}).then((answer) => {
225+
let choice = answer.choice;
226+
227+
if (choice === 'continue') {
228+
return this._generateTemplate(contents, descriptor, file, dir);
229+
}
230+
231+
if (choice === 'view') {
232+
this.ui.log(contents);
233+
return this.template(contents, descriptor, file, dir);
234+
}
235+
236+
if (choice === 'edit') {
237+
return this.ui.prompt({
238+
type: 'editor',
239+
name: 'contents',
240+
message: 'Edit the generated file',
241+
default: contents
242+
}).then((answer) => {
243+
contents = answer.contents;
244+
return this._generateTemplate(contents, descriptor, file, dir);
245+
});
246+
}
247+
});
213248
}
214-
215-
return this.ui.prompt({
216-
type: 'expand',
217-
name: 'choice',
218-
message: `Ghost-CLI would like to generate a ${descriptor} file.`,
219-
default: 'y',
220-
choices: [
221-
{ key: 'y', name: 'Yes, write config file', value: 'write' },
222-
{ key: 'n', name: 'No, don\'t create the file', value: 'skip' },
223-
{ key: 'v', name: 'View the file', value: 'view' },
224-
{ key: 'e', name: 'Edit the file before generation', value: 'edit' }
225-
]
226-
}).then((answer) => {
227-
let choice = answer.choice;
228-
229-
if (choice === 'skip') {
230-
return Promise.resolve(false);
231-
}
232-
233-
if (choice === 'write') {
234-
return this._generateTemplate(contents, file, dir);
235-
}
236-
237-
if (choice === 'view') {
238-
this.ui.log(contents);
239-
return this.template(contents, descriptor, file, dir);
240-
}
241-
242-
if (choice === 'edit') {
243-
return this.ui.prompt({
244-
type: 'editor',
245-
name: 'contents',
246-
message: 'Edit the generated file',
247-
default: contents
248-
}).then((answer) => {
249-
contents = answer.contents;
250-
return this._generateTemplate(contents, file, dir);
251-
});
252-
}
253-
});
254249
}
255250

256251
/**
257252
* Actually handles saving the file. Used by the template method
258253
*
259254
* @param {string} contents Contents of file
255+
* @param {string} descriptor description of file
260256
* @param {string} file Filename
261-
* @param {string} dir Directroy to link
257+
* @param {string} dir Directory to link
262258
* @return {bool} True if the file was successfully created & linked
263259
*/
264-
_generateTemplate(contents, file, dir) {
260+
_generateTemplate(contents, descriptor, file, dir) {
265261
let tmplDir = path.join(this.dir, 'system', 'files');
266262
let tmplFile = path.join(tmplDir, file);
267263

@@ -272,6 +268,7 @@ class Instance {
272268

273269
if (dir) {
274270
let outputLocation = path.join(dir, file);
271+
this.ui.success(`Creating ${descriptor} file at ${tmplFile}`);
275272
promises.push(() => this.ui.sudo(`ln -sf ${tmplFile} ${outputLocation}`));
276273
}
277274

‎test/unit/instance-spec.js

+68-35
Original file line numberDiff line numberDiff line change
@@ -365,49 +365,93 @@ describe('Unit: Instance', function () {
365365
});
366366

367367
describe('template', function () {
368-
it('resolves with false if the choice is to skip', function () {
369-
let promptStub = sandbox.stub().resolves({ choice: 'skip' });
370-
let testInstance = new Instance({ prompt: promptStub, allowPrompt: true }, {}, '');
368+
it('immediately calls _generateTemplate if ui.allowPrompt is false', function () {
369+
let promptStub = sandbox.stub().resolves();
370+
let testInstance = new Instance({
371+
prompt: promptStub,
372+
allowPrompt: false,
373+
verbose: true
374+
}, {}, '');
375+
let generateStub = sandbox.stub(testInstance, '_generateTemplate').resolves(true);
371376

372377
return testInstance.template('some contents', 'a file', 'file.txt', '/some/dir').then((result) => {
373-
expect(promptStub.calledOnce).to.be.true;
374-
expect(result).to.be.false;
378+
expect(result).to.be.true;
379+
expect(promptStub.called).to.be.false;
380+
expect(generateStub.calledOnce).to.be.true;
381+
expect(generateStub.args[0][0]).to.equal('some contents');
375382
});
376383
});
377384

378-
it('generates template if the choice is to proceeed', function () {
379-
let promptStub = sandbox.stub().resolves({choice: 'write'});
380-
let testInstance = new Instance({ prompt: promptStub, allowPrompt: true }, {} , '');
385+
it('immediately calls _generateTemplate if ui.verbose is false', function () {
386+
let promptStub = sandbox.stub().resolves();
387+
let testInstance = new Instance({
388+
prompt: promptStub,
389+
allowPrompt: true,
390+
verbose: false
391+
}, {}, '');
392+
let generateStub = sandbox.stub(testInstance, '_generateTemplate').resolves(true);
393+
394+
return testInstance.template('some contents', 'a file', 'file.txt', '/some/dir').then((result) => {
395+
expect(result).to.be.true;
396+
expect(promptStub.called).to.be.false;
397+
expect(generateStub.calledOnce).to.be.true;
398+
expect(generateStub.args[0][0]).to.equal('some contents');
399+
});
400+
});
401+
402+
it('immediately calls _generateTemplate if ui.allowPrompt and ui.verbose is false', function () {
403+
let promptStub = sandbox.stub().resolves();
404+
let testInstance = new Instance({
405+
prompt: promptStub,
406+
allowPrompt: true,
407+
verbose: false
408+
}, {}, '');
409+
let generateStub = sandbox.stub(testInstance, '_generateTemplate').resolves(true);
410+
411+
return testInstance.template('some contents', 'a file', 'file.txt', '/some/dir').then((result) => {
412+
expect(result).to.be.true;
413+
expect(promptStub.called).to.be.false;
414+
expect(generateStub.calledOnce).to.be.true;
415+
expect(generateStub.args[0][0]).to.equal('some contents');
416+
});
417+
});
418+
419+
it('generates template if the choice is to continue (with --verbose)', function () {
420+
let promptStub = sandbox.stub().resolves({choice: 'continue'});
421+
let testInstance = new Instance({ prompt: promptStub, allowPrompt: true, verbose: true }, {} , '');
381422
let generateStub = sandbox.stub(testInstance, '_generateTemplate').resolves(true);
382423

383424
return testInstance.template('some contents', 'a file', 'file.txt', '/some/dir').then((result) => {
384425
expect(result).to.be.true;
385426
expect(generateStub.calledOnce).to.be.true;
386427
expect(promptStub.calledOnce).to.be.true;
387-
expect(generateStub.args[0]).to.deep.equal(['some contents', 'file.txt', '/some/dir']);
428+
expect(generateStub.args[0]).to.deep.equal(['some contents', 'a file', 'file.txt', '/some/dir']);
388429
});
389430
});
390431

391-
it('logs and calls template method again if choice is view', function () {
432+
it('logs and calls template method again if choice is view (with --verbose)', function () {
392433
let promptStub = sandbox.stub();
393434
promptStub.onCall(0).resolves({choice: 'view'});
394-
promptStub.onCall(1).resolves({choice: 'skip'});
435+
promptStub.onCall(1).resolves({choice: 'continue'});
395436
let logStub = sandbox.stub();
396-
let testInstance = new Instance({ log: logStub, prompt: promptStub, allowPrompt: true }, {}, '');
437+
let testInstance = new Instance({ log: logStub, prompt: promptStub, allowPrompt: true, verbose: true }, {}, '');
438+
let generateStub = sandbox.stub(testInstance, '_generateTemplate').resolves(true);
397439

398440
return testInstance.template('some contents', 'a file', 'file.txt', '/some/dir').then((result) => {
399-
expect(result).to.be.false;
441+
expect(result).to.be.true;
400442
expect(promptStub.calledTwice).to.be.true;
401443
expect(logStub.calledOnce).to.be.true;
402444
expect(logStub.args[0][0]).to.equal('some contents');
445+
expect(generateStub.calledOnce).to.be.true;
446+
expect(generateStub.args[0]).to.deep.equal(['some contents', 'a file', 'file.txt', '/some/dir']);
403447
});
404448
});
405449

406-
it('opens editor and generates template with contents if choice is edit', function () {
450+
it('opens editor and generates template with contents if choice is edit (with --verbose)', function () {
407451
let promptStub = sandbox.stub();
408452
promptStub.onCall(0).resolves({choice: 'edit'});
409453
promptStub.onCall(1).resolves({contents: 'some edited contents'});
410-
let testInstance = new Instance({ prompt: promptStub, allowPrompt: true }, {}, '');
454+
let testInstance = new Instance({ prompt: promptStub, allowPrompt: true, verbose: true }, {}, '');
411455
let generateStub = sandbox.stub(testInstance, '_generateTemplate').resolves(true);
412456

413457
return testInstance.template('some contents', 'a file', 'file.txt', '/some/dir').then((result) => {
@@ -417,49 +461,38 @@ describe('Unit: Instance', function () {
417461
expect(generateStub.args[0][0]).to.equal('some edited contents');
418462
});
419463
});
420-
421-
it('immediately calls _generateTemplate if ui.allowPrompt is false', function () {
422-
let promptStub = sandbox.stub().resolves();
423-
let testInstance = new Instance({
424-
prompt: promptStub,
425-
allowPrompt: false
426-
}, {}, '');
427-
let generateStub = sandbox.stub(testInstance, '_generateTemplate').resolves(true);
428-
429-
return testInstance.template('some contents', 'a file', 'file.txt', '/some/dir').then((result) => {
430-
expect(result).to.be.true;
431-
expect(promptStub.called).to.be.false;
432-
expect(generateStub.calledOnce).to.be.true;
433-
expect(generateStub.args[0][0]).to.equal('some contents');
434-
});
435-
});
436464
});
437465

438466
describe('_generateTemplate', function () {
439467
it('writes out template to correct directory but doesn\'t link if no dir is passed', function () {
440468
let dir = tmp.dirSync({unsafeCleanup: true}).name;
441-
let testInstance = new Instance({}, {}, dir);
469+
let successStub = sandbox.stub();
470+
let testInstance = new Instance({success: successStub}, {}, dir);
442471

443-
return testInstance._generateTemplate('some contents', 'file.txt').then((result) => {
472+
return testInstance._generateTemplate('some contents', 'a file', 'file.txt').then((result) => {
444473
expect(result).to.be.true;
445474
let fpath = path.join(dir, 'system', 'files', 'file.txt');
446475
expect(fs.existsSync(fpath)).to.be.true;
447476
expect(fs.readFileSync(fpath, 'utf8')).to.equal('some contents');
477+
expect(successStub.called).to.be.false;
448478
});
449479
});
450480

451481
it('writes out template and links it correctly if dir is passed', function () {
452482
let dir = tmp.dirSync({unsafeCleanup: true}).name;
453483
let sudoStub = sandbox.stub().resolves();
454-
let testInstance = new Instance({ sudo: sudoStub }, {}, dir);
484+
let successStub = sandbox.stub();
485+
let testInstance = new Instance({ sudo: sudoStub, success: successStub }, {}, dir);
455486

456-
return testInstance._generateTemplate('some contents', 'file.txt', '/another/dir').then((result) => {
487+
return testInstance._generateTemplate('some contents', 'a file', 'file.txt', '/another/dir').then((result) => {
457488
expect(result).to.be.true;
458489
let fpath = path.join(dir, 'system', 'files', 'file.txt');
459490
expect(fs.existsSync(fpath)).to.be.true;
460491
expect(fs.readFileSync(fpath, 'utf8')).to.equal('some contents');
461492
expect(sudoStub.calledOnce).to.be.true;
462493
expect(sudoStub.args[0][0]).to.equal(`ln -sf ${fpath} /another/dir/file.txt`);
494+
expect(successStub.calledOnce).to.be.true;
495+
expect(successStub.firstCall.args[0]).to.match(/^Creating a file file at/);
463496
});
464497
});
465498
});

0 commit comments

Comments
 (0)