Skip to content

Commit

Permalink
#20: finish utility unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nkrusch committed Jan 30, 2021
1 parent 17b03cb commit 34e035b
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 63 deletions.
30 changes: 21 additions & 9 deletions cli/utilities.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
const fs = require('fs');
const path = require('path');

/**
* @class
* @classdesc Utility class provides helper methods
* for performing commonly recurring operations such
* as IO; and reading, writing, merging objects.
*
* When performing these operations, it is preferable
* to use these utility methods to ensure same behavior
* for these operations everywhere, and to establish one
* place of change, should these operations change in the
* future.
*/
class Utilities {

/**
Expand Down Expand Up @@ -161,6 +173,15 @@ class Utilities {
fs.writeFileSync(filePath, content);
}

/**
* Check if file exists
* @param filePath - path to file
* @return {boolean} - true/false
*/
fileExists(filePath) {
return fs.existsSync(filePath);
}

/**
* Create empty directory.
*
Expand All @@ -187,15 +208,6 @@ class Utilities {
return JSON.parse(this.readFile(filePath));
}

/**
* Check if file exists
* @param filePath - path to file
* @return {boolean} - true/false
*/
fileExists(filePath) {
return fs.existsSync(filePath);
}

/**
* Reads text file then replaces all variable placeholders, e.g. ${var1}
* @param path - path to file
Expand Down
210 changes: 156 additions & 54 deletions test/cli-utililities.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,46 @@ const fs = require('fs');

describe('Test utility functions', () => {

/**
* Stub Node.js IO methods
*/
// eslint-disable-next-line no-undef
beforeEach(() => {
sinon.stub(fs, 'readFileSync');
sinon.stub(fs, 'existsSync');
sinon.stub(fs, 'mkdirSync');
sinon.stub(fs, 'readdirSync');
sinon.stub(fs, 'writeFileSync');
sinon.stub(fs, 'createReadStream');
sinon.stub(fs, 'createWriteStream');
sinon.stub(fs, 'lstatSync');
sinon.stub(fs, 'copyFileSync');
sinon.stub(fs, 'symlinkSync');
sinon.stub(fs, 'readlinkSync');

fs.createReadStream.returns({
pipe: () => true
});
});

/**
* Restore stubbed IO methods
*/
// eslint-disable-next-line no-undef
afterEach(() => {
fs.existsSync.restore();
fs.mkdirSync.restore();
fs.readdirSync.restore();
fs.readFileSync.restore();
fs.writeFileSync.restore();
fs.createReadStream.restore();
fs.createWriteStream.restore();
fs.lstatSync.restore();
fs.copyFileSync.restore();
fs.symlinkSync.restore();
fs.readlinkSync.restore();
});

describe('generateDirectoryName...', () => {

it('...returns lowercase name', () => {
Expand Down Expand Up @@ -141,26 +181,6 @@ describe('Test utility functions', () => {

describe('copyFolderSync', () => {

// eslint-disable-next-line no-undef
beforeEach(() => {
sinon.stub(fs, 'mkdirSync');
sinon.stub(fs, 'readdirSync');
sinon.stub(fs, 'lstatSync');
sinon.stub(fs, 'copyFileSync');
sinon.stub(fs, 'symlinkSync');
sinon.stub(fs, 'readlinkSync');
});

// eslint-disable-next-line no-undef
afterEach(() => {
fs.mkdirSync.restore();
fs.readdirSync.restore();
fs.lstatSync.restore();
fs.copyFileSync.restore();
fs.symlinkSync.restore();
fs.readlinkSync.restore();
});

it('...copies directory with files to new location', () => {
fs.readdirSync.returns(['file1.txt', 'file2.txt']);
fs.lstatSync.returns({isFile: () => true});
Expand All @@ -184,24 +204,23 @@ describe('Test utility functions', () => {
expect(fs.readdirSync.callCount).to.equal(2);
});

});

describe('copyFile', () => {

// eslint-disable-next-line no-undef
beforeEach(() => {
sinon.stub(fs, 'createReadStream');
sinon.stub(fs, 'createWriteStream');
fs.createReadStream.returns({
pipe: () => true
it('...does nothing when not file/dir/symlink', () => {
sinon.spy(Utilities, 'copyFolderSync');
fs.readdirSync.returns(['invalid']);
fs.lstatSync.onCall(0).returns({
isFile: () => false,
isSymbolicLink: () => false,
isDirectory: () => false
});
Utilities.copyFolderSync('a', 'b');
expect(fs.copyFileSync.callCount).to.equal(0);
expect(fs.symlinkSync.callCount).to.equal(0);
expect(Utilities.copyFolderSync.callCount).to.equal(1);
});

// eslint-disable-next-line no-undef
afterEach(() => {
fs.createReadStream.restore();
fs.createWriteStream.restore();
});
});

describe('copyFile', () => {

it('...copies file from old location to new location', () => {
Utilities.copyFile('./test1', './test2');
Expand All @@ -213,16 +232,6 @@ describe('Test utility functions', () => {

describe('readFile', () => {

// eslint-disable-next-line no-undef
beforeEach(() => {
sinon.stub(fs, 'readFileSync');
});

// eslint-disable-next-line no-undef
afterEach(() => {
fs.readFileSync.restore();
});

it('...calls read file', () => {
Utilities.readFile('xyz');
expect(fs.readFileSync.calledOnce).to.equal(true);
Expand All @@ -231,20 +240,113 @@ describe('Test utility functions', () => {

describe('writeFile', () => {

// eslint-disable-next-line no-undef
beforeEach(() => {
sinon.stub(fs, 'writeFileSync');
it('...calls write file', () => {
Utilities.writeFile('xyz', 'text content...');
expect(fs.writeFileSync.calledOnce).to.equal(true);
});

});

describe('fileExists', () => {

it('...returns true for existing file', () => {
fs.existsSync.returns(true);
expect(Utilities.fileExists('im_here')).to.equal(true);
});

// eslint-disable-next-line no-undef
afterEach(() => {
fs.writeFileSync.restore();
it('...returns false when file does not exist', () => {
fs.existsSync.returns(false);
expect(Utilities.fileExists('nope')).to.equal(false);
});

it('...calls write file', () => {
Utilities.writeFile('xyz', 'text content...');
expect(fs.writeFileSync.calledOnce).to.equal(true);
});

describe('createDir', () => {

it('...will create a directory when it doesn\'t exist', () => {
fs.existsSync.returns(false);
const result = Utilities.createDir('my_dir');

expect(fs.mkdirSync.calledOnce).to.equal(true);
expect(result).to.equal(true);
});

it('...returns true for empty folder', () => {
fs.existsSync.returns(true);
fs.readdirSync.returns({length: 0});
const result = Utilities.createDir('empty_dir');

expect(fs.mkdirSync.notCalled).to.equal(true);
expect(result).to.equal(true);
});

it('...returns false for non-empty folder', () => {
fs.existsSync.returns(true);
fs.readdirSync.returns({length: 1});
expect(Utilities.createDir('non_empty_dir')).to.equal(false);
});

});

describe('readJSON', () => {

it('...returns a parsed JSON object', () => {
fs.readFileSync.returns('{ "title" : "test"}');

const obj = Utilities.readJSON('xyz');

expect(obj.title).to.equal('test');
});

it('...throws error for non-JSON format file content', () => {
fs.readFileSync.returns('this is some plain text');
expect(() => Utilities.readJSON('xyz')).to
.throw('Unexpected token');
});

});

describe('readAndReplaceTextFile', () => {

it('...replaces single variable', () => {
fs.readFileSync.returns('Text with ${variable} in the middle!');
const variables = {variable: 'find me'};
const result = Utilities.readAndReplaceTextFile('some_file', variables);

expect(result).to.contain(variables.variable);
});

it('...replaces multiple variables', () => {
fs.readFileSync.returns('Some math ${a} + ${b} = ${c}');
const math = {a: 1, b: 2, c: 3};
const result = Utilities.readAndReplaceTextFile('my_file', math);

expect(result).to.contain('1 + 2 = 3');
});

});

describe('readAndReplaceJSONFile', () => {

it('...replaces variables in an object', () => {
fs.readFileSync.returns(
'{ "name":"${name}", "version" : "v-${version}" }'
);
const values = {name: 'my_app', version: '1.0.0'};
const jsonString = Utilities.readAndReplaceJSONFile('manifest', values);
const obj = JSON.parse(jsonString);

expect(obj.name).to.equal(values.name);
expect(obj.version).to.equal('v-1.0.0');
});

it('...replaces nested variables', () => {
fs.readFileSync.returns('{ "config": { "count" : "${n}" }}');
const values = {n: 10};
const result = Utilities.readAndReplaceJSONFile('manifest', values);

expect(result).to.contain('10');
});

});
});

0 comments on commit 34e035b

Please sign in to comment.