From 70db501f4bbd5b0be4b05ac7a57b4c4e711cfd17 Mon Sep 17 00:00:00 2001 From: Anton Usmansky Date: Thu, 2 Aug 2018 16:37:52 +0300 Subject: [PATCH 1/8] chore: rename StreamWriter -> DataFile --- index.js | 12 ++++++------ lib/{stream-writer.js => data-file.js} | 4 ++-- test/lib/{stream-writer.js => data-file.js} | 18 +++++++++--------- test/lib/index.js | 12 ++++++------ 4 files changed, 23 insertions(+), 23 deletions(-) rename lib/{stream-writer.js => data-file.js} (92%) rename test/lib/{stream-writer.js => data-file.js} (83%) diff --git a/index.js b/index.js index 01e5250..f94040a 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ const _ = require('lodash'); const path = require('path'); const fs = require('fs-extra'); const parseConfig = require('./lib/config'); -const StreamWriter = require('./lib/stream-writer'); +const DataFile = require('./lib/data-file'); const wrapCommands = require('./lib/commands-wrapper'); module.exports = (hermione, opts) => { @@ -14,14 +14,14 @@ module.exports = (hermione, opts) => { return; } - let writeStream; + let dataFile; const retriesMap = _(hermione.config.getBrowserIds()) .zipObject() .mapValues(() => new Map()) .value(); hermione.on(hermione.events.RUNNER_START, () => { - writeStream = StreamWriter.create(pluginConfig.path); + dataFile = DataFile.create(pluginConfig.path); }); hermione.on(hermione.events.RETRY, (test) => { @@ -49,15 +49,15 @@ module.exports = (hermione, opts) => { } test.timeEnd = Date.now(); - writeStream.write(test); + dataFile.write(test); }); - hermione.on(hermione.events.ERROR, () => writeStream.end()); + hermione.on(hermione.events.ERROR, () => dataFile.end()); hermione.on(hermione.events.NEW_BROWSER, wrapCommands); hermione.on(hermione.events.RUNNER_END, () => { - writeStream.end(); + dataFile.end(); copyToReportDir(pluginConfig.path, ['index.html', 'bundle.min.js', 'styles.css']); }); }; diff --git a/lib/stream-writer.js b/lib/data-file.js similarity index 92% rename from lib/stream-writer.js rename to lib/data-file.js index 2b2a9f2..7308e14 100644 --- a/lib/stream-writer.js +++ b/lib/data-file.js @@ -4,9 +4,9 @@ const path = require('path'); const fs = require('fs-extra'); const _ = require('lodash'); -module.exports = class StreamWriter { +module.exports = class DataFile { static create(reportPath) { - return new StreamWriter(reportPath); + return new DataFile(reportPath); } constructor(reportPath) { diff --git a/test/lib/stream-writer.js b/test/lib/data-file.js similarity index 83% rename from test/lib/stream-writer.js rename to test/lib/data-file.js index aac522e..35ac724 100644 --- a/test/lib/stream-writer.js +++ b/test/lib/data-file.js @@ -1,7 +1,7 @@ 'use strict'; const fs = require('fs-extra'); -const StreamWriter = require('../../lib/stream-writer'); +const DataFile = require('../../lib/data-file'); describe('stream writer', () => { const sandbox = sinon.sandbox.create(); @@ -28,13 +28,13 @@ describe('stream writer', () => { describe('create', () => { it('should create directory for report', () => { - StreamWriter.create('report/path'); + DataFile.create('report/path'); assert.calledOnceWith(fs.ensureDirSync, 'report/path'); }); it('should create write stream to the passed path', () => { - StreamWriter.create('report/path'); + DataFile.create('report/path'); assert.calledOnceWith(fs.createWriteStream, 'report/path/data.js'); }); @@ -42,7 +42,7 @@ describe('stream writer', () => { describe('write', () => { it('should write opening bracket at first call', () => { - const stream = StreamWriter.create('report/path'); + const stream = DataFile.create('report/path'); stream.write(dataStub()); @@ -50,7 +50,7 @@ describe('stream writer', () => { }); it('should write object with "n" property as "fullTitle"', () => { - const stream = StreamWriter.create('report/path'); + const stream = DataFile.create('report/path'); const data = dataStub({fullTitle: 'test1'}); stream.write(data); @@ -59,7 +59,7 @@ describe('stream writer', () => { }); it('should write object with command list from data', () => { - const stream = StreamWriter.create('report/path'); + const stream = DataFile.create('report/path'); const data = dataStub({ fullTitle: 'test1', hermioneCtx: { @@ -74,7 +74,7 @@ describe('stream writer', () => { }); it('should write object with empty command list if it does not exist in data', () => { - const stream = StreamWriter.create('report/path'); + const stream = DataFile.create('report/path'); const data = dataStub({fullTitle: 'test1'}); stream.write(data); @@ -84,7 +84,7 @@ describe('stream writer', () => { }); it('should divide data chains with comma delimiter', () => { - const stream = StreamWriter.create('report/path'); + const stream = DataFile.create('report/path'); stream.write(dataStub()); stream.write(dataStub()); @@ -96,7 +96,7 @@ describe('stream writer', () => { describe('end', () => { it('should end stream with the closing bracket', () => { - const stream = StreamWriter.create('report/path'); + const stream = DataFile.create('report/path'); stream.end(); assert.calledOnceWith(streamStub.end, ']'); diff --git a/test/lib/index.js b/test/lib/index.js index 693466f..13287a4 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -4,7 +4,7 @@ const _ = require('lodash'); const fs = require('fs-extra'); const proxyquire = require('proxyquire'); const EventEmitter = require('events').EventEmitter; -const StreamWriter = require('../../lib/stream-writer'); +const DataFile = require('../../lib/data-file'); const mkHermione = () => { const emitter = new EventEmitter(); @@ -54,7 +54,7 @@ describe('plugin', () => { write: sandbox.stub().named('write'), end: sandbox.stub().named('end') }; - sandbox.stub(StreamWriter, 'create').returns(stream); + sandbox.stub(DataFile, 'create').returns(stream); sandbox.stub(fs, 'copySync'); }); @@ -72,12 +72,12 @@ describe('plugin', () => { assert.equal(hermione.listeners(hermione.events.RUNNER_START).length, 0); }); - it('should create stream on RUNNER_START', () => { + it('should create data file on RUNNER_START', () => { initPlugin_(); hermione.emit(hermione.events.RUNNER_START); - assert.calledOnce(StreamWriter.create); + assert.calledOnce(DataFile.create); }); describe('on TEST_BEGIN', () => { @@ -127,7 +127,7 @@ describe('plugin', () => { assert.propertyVal(test, 'timeEnd', 100500); }); - it('should write data to stream', () => { + it('should write data to data file', () => { const test = mkTest(); hermione.emit(hermione.events.TEST_END, test); @@ -147,7 +147,7 @@ describe('plugin', () => { }); }); - describe('should close stream', () => { + describe('should finalize data file', () => { it('on error', () => { initPlugin_(); From e186e310aff7df495ef1f487588871f92a165faa Mon Sep 17 00:00:00 2001 From: Anton Usmansky Date: Thu, 2 Aug 2018 16:52:32 +0300 Subject: [PATCH 2/8] chore: tests refactoring --- test/lib/index.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/test/lib/index.js b/test/lib/index.js index 13287a4..7c13aaf 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -37,7 +37,6 @@ describe('plugin', () => { const sandbox = sinon.sandbox.create(); let hermione; let plugin; - let stream; let commandWrapper; const initPlugin_ = (opts = {}) => { @@ -50,11 +49,10 @@ describe('plugin', () => { beforeEach(() => { hermione = mkHermione(); - stream = { - write: sandbox.stub().named('write'), - end: sandbox.stub().named('end') - }; - sandbox.stub(DataFile, 'create').returns(stream); + + sandbox.stub(DataFile, 'create').returns(Object.create(DataFile.prototype)); + sandbox.stub(DataFile.prototype); + sandbox.stub(fs, 'copySync'); }); @@ -132,7 +130,7 @@ describe('plugin', () => { hermione.emit(hermione.events.TEST_END, test); - assert.calledOnceWith(stream.write, test); + assert.calledOnceWith(DataFile.prototype.write, test); }); it('should do nothing for pending tests', () => { @@ -143,7 +141,7 @@ describe('plugin', () => { hermione.emit(hermione.events.TEST_END, test); assert.notProperty(test, 'timeEnd'); - assert.notCalled(stream.write); + assert.notCalled(DataFile.prototype.write); }); }); @@ -154,7 +152,7 @@ describe('plugin', () => { hermione.emit(hermione.events.RUNNER_START); hermione.emit(hermione.events.ERROR); - assert.calledOnce(stream.end); + assert.calledOnce(DataFile.prototype.end); }); it('on runner end', () => { @@ -163,7 +161,7 @@ describe('plugin', () => { hermione.emit(hermione.events.RUNNER_START); hermione.emit(hermione.events.RUNNER_END); - assert.calledOnce(stream.end); + assert.calledOnce(DataFile.prototype.end); }); }); From a1bb041d560abed27300ee4af31f76a8a7d88305 Mon Sep 17 00:00:00 2001 From: Anton Usmansky Date: Thu, 2 Aug 2018 16:58:40 +0300 Subject: [PATCH 3/8] fix: subscribe to NEW_BROWSER event only in workers --- index.js | 7 +++++-- test/lib/index.js | 21 +++++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index f94040a..4e8b5cc 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,11 @@ module.exports = (hermione, opts) => { return; } + if (hermione.isWorker()) { + hermione.on(hermione.events.NEW_BROWSER, wrapCommands); + return; + } + let dataFile; const retriesMap = _(hermione.config.getBrowserIds()) .zipObject() @@ -54,8 +59,6 @@ module.exports = (hermione, opts) => { hermione.on(hermione.events.ERROR, () => dataFile.end()); - hermione.on(hermione.events.NEW_BROWSER, wrapCommands); - hermione.on(hermione.events.RUNNER_END, () => { dataFile.end(); copyToReportDir(pluginConfig.path, ['index.html', 'bundle.min.js', 'styles.css']); diff --git a/test/lib/index.js b/test/lib/index.js index 7c13aaf..0782750 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -23,6 +23,8 @@ const mkHermione = () => { getBrowserIds: sinon.stub().returns(['default-bro']) }; + emitter.isWorker = sinon.stub().returns(false); + return emitter; }; @@ -176,11 +178,22 @@ describe('plugin', () => { }); }); - it('should wrap browser commands on NEW_BROWSER', () => { - initPlugin_(); + describe('on NEW_BROWSER', () => { + it('should wrap browser commands in worker', () => { + hermione.isWorker.returns(true); + initPlugin_(); - hermione.emit(hermione.events.NEW_BROWSER); + hermione.emit(hermione.events.NEW_BROWSER); + + assert.calledOnce(commandWrapper); + }); - assert.calledOnce(commandWrapper); + it('should not wrap browser commands in master', () => { + initPlugin_(); + + hermione.emit(hermione.events.NEW_BROWSER); + + assert.notCalled(commandWrapper); + }); }); }); From 9ea21c91ead30d33d663694de89cf8132007b66b Mon Sep 17 00:00:00 2001 From: Anton Usmansky Date: Thu, 2 Aug 2018 17:20:50 +0300 Subject: [PATCH 4/8] feat: initialize dataFile on plugin load instead of RUNNER_START --- index.js | 7 +------ test/lib/data-file.js | 2 ++ test/lib/index.js | 21 ++++----------------- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/index.js b/index.js index 4e8b5cc..870b323 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,6 @@ const wrapCommands = require('./lib/commands-wrapper'); module.exports = (hermione, opts) => { const pluginConfig = parseConfig(opts); - if (!pluginConfig.enabled) { return; } @@ -19,16 +18,12 @@ module.exports = (hermione, opts) => { return; } - let dataFile; + let dataFile = DataFile.create(pluginConfig.path); const retriesMap = _(hermione.config.getBrowserIds()) .zipObject() .mapValues(() => new Map()) .value(); - hermione.on(hermione.events.RUNNER_START, () => { - dataFile = DataFile.create(pluginConfig.path); - }); - hermione.on(hermione.events.RETRY, (test) => { const fullTitle = test.fullTitle(); diff --git a/test/lib/data-file.js b/test/lib/data-file.js index 35ac724..5159d1e 100644 --- a/test/lib/data-file.js +++ b/test/lib/data-file.js @@ -26,6 +26,8 @@ describe('stream writer', () => { afterEach(() => sandbox.restore()); + it('should not do any fs stuff on create'); + describe('create', () => { it('should create directory for report', () => { DataFile.create('report/path'); diff --git a/test/lib/index.js b/test/lib/index.js index 0782750..b705b96 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -10,7 +10,6 @@ const mkHermione = () => { const emitter = new EventEmitter(); emitter.events = { - RUNNER_START: 'runner-start', TEST_BEGIN: 'test-begin', TEST_END: 'test-end', RETRY: 'retry', @@ -60,24 +59,16 @@ describe('plugin', () => { afterEach(() => sandbox.restore()); - it('should be enabled by default', () => { - initPlugin_(); + it('should create data file on plugin load', () => { + initPlugin_({path: 'report/dir'}); - assert.equal(hermione.listeners(hermione.events.RUNNER_START).length, 1); + assert.calledOnceWith(DataFile.create, 'report/dir'); }); it('should do nothing if plugin is disabled', () => { initPlugin_({enabled: false}); - assert.equal(hermione.listeners(hermione.events.RUNNER_START).length, 0); - }); - - it('should create data file on RUNNER_START', () => { - initPlugin_(); - - hermione.emit(hermione.events.RUNNER_START); - - assert.calledOnce(DataFile.create); + assert.notCalled(DataFile.create); }); describe('on TEST_BEGIN', () => { @@ -115,7 +106,6 @@ describe('plugin', () => { describe('on TEST_END', () => { beforeEach(() => { initPlugin_(); - hermione.emit(hermione.events.RUNNER_START); }); it('should set timeEnd for test', () => { @@ -151,7 +141,6 @@ describe('plugin', () => { it('on error', () => { initPlugin_(); - hermione.emit(hermione.events.RUNNER_START); hermione.emit(hermione.events.ERROR); assert.calledOnce(DataFile.prototype.end); @@ -160,7 +149,6 @@ describe('plugin', () => { it('on runner end', () => { initPlugin_(); - hermione.emit(hermione.events.RUNNER_START); hermione.emit(hermione.events.RUNNER_END); assert.calledOnce(DataFile.prototype.end); @@ -171,7 +159,6 @@ describe('plugin', () => { it(`should copy "${fileName}" service file to the report dir on runner end`, () => { initPlugin_({path: 'reportDir'}); - hermione.emit(hermione.events.RUNNER_START); hermione.emit(hermione.events.RUNNER_END); assert.equal(fs.copySync.args[i][1], `reportDir/${fileName}`); From 4faf7c73813578da6c7525bf1c2da89ca8d4defb Mon Sep 17 00:00:00 2001 From: Anton Usmansky Date: Thu, 2 Aug 2018 17:24:13 +0300 Subject: [PATCH 5/8] fix: remove redundant ERROR handler --- index.js | 2 -- test/lib/index.js | 25 ++++++++----------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index 870b323..a0e8314 100644 --- a/index.js +++ b/index.js @@ -52,8 +52,6 @@ module.exports = (hermione, opts) => { dataFile.write(test); }); - hermione.on(hermione.events.ERROR, () => dataFile.end()); - hermione.on(hermione.events.RUNNER_END, () => { dataFile.end(); copyToReportDir(pluginConfig.path, ['index.html', 'bundle.min.js', 'styles.css']); diff --git a/test/lib/index.js b/test/lib/index.js index b705b96..dd8f818 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -13,7 +13,6 @@ const mkHermione = () => { TEST_BEGIN: 'test-begin', TEST_END: 'test-end', RETRY: 'retry', - ERROR: 'critical-error', RUNNER_END: 'runner-end', NEW_BROWSER: 'new-browser' }; @@ -137,31 +136,23 @@ describe('plugin', () => { }); }); - describe('should finalize data file', () => { - it('on error', () => { - initPlugin_(); - - hermione.emit(hermione.events.ERROR); - - assert.calledOnce(DataFile.prototype.end); - }); - - it('on runner end', () => { + describe('on RUNNER_END', () => { + it('should finalize data file', () => { initPlugin_(); hermione.emit(hermione.events.RUNNER_END); assert.calledOnce(DataFile.prototype.end); }); - }); - ['index.html', 'bundle.min.js', 'styles.css'].forEach((fileName, i) => { - it(`should copy "${fileName}" service file to the report dir on runner end`, () => { - initPlugin_({path: 'reportDir'}); + ['index.html', 'bundle.min.js', 'styles.css'].forEach((fileName, i) => { + it(`should copy "${fileName}" service file to the report dir on runner end`, () => { + initPlugin_({path: 'reportDir'}); - hermione.emit(hermione.events.RUNNER_END); + hermione.emit(hermione.events.RUNNER_END); - assert.equal(fs.copySync.args[i][1], `reportDir/${fileName}`); + assert.equal(fs.copySync.args[i][1], `reportDir/${fileName}`); + }); }); }); From 56e54cc46b8d0862cfe2848993dfb8537dd1defe Mon Sep 17 00:00:00 2001 From: Anton Usmansky Date: Thu, 2 Aug 2018 17:29:02 +0300 Subject: [PATCH 6/8] fix: wait for dataFile finish writing on RUNNER_END --- index.js | 4 ++-- package-lock.json | 6 +++--- package.json | 2 +- test/lib/data-file.js | 1 + test/lib/index.js | 8 ++++---- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index a0e8314..28f61d4 100644 --- a/index.js +++ b/index.js @@ -52,8 +52,8 @@ module.exports = (hermione, opts) => { dataFile.write(test); }); - hermione.on(hermione.events.RUNNER_END, () => { - dataFile.end(); + hermione.on(hermione.events.RUNNER_END, async () => { + await dataFile.end(); copyToReportDir(pluginConfig.path, ['index.html', 'bundle.min.js', 'styles.css']); }); }; diff --git a/package-lock.json b/package-lock.json index 2aae8d6..488f8e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3175,9 +3175,9 @@ } }, "eslint-config-gemini-testing": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/eslint-config-gemini-testing/-/eslint-config-gemini-testing-2.4.1.tgz", - "integrity": "sha1-X1QDAYxOtrw2q+Czrbyy/V0r3OM=", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-gemini-testing/-/eslint-config-gemini-testing-2.8.0.tgz", + "integrity": "sha512-zp9yStBk+Yplm0QpeyvgLPZA12wzDsWpvzi/YiN+GLSrt9ilq0NbNvFOp5KEFvnJNh57s8SioeIbv+jJX2BBQA==", "dev": true }, "espree": { diff --git a/package.json b/package.json index 063ca59..3748032 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "chai": "^4.0.2", "css-loader": "^0.28.7", "eslint": "^3.19.0", - "eslint-config-gemini-testing": "^2.4.0", + "eslint-config-gemini-testing": "^2.8.0", "material-ui": "^0.19.3", "mocha": "^3.4.2", "proxyquire": "^1.8.0", diff --git a/test/lib/data-file.js b/test/lib/data-file.js index 5159d1e..5481dc2 100644 --- a/test/lib/data-file.js +++ b/test/lib/data-file.js @@ -27,6 +27,7 @@ describe('stream writer', () => { afterEach(() => sandbox.restore()); it('should not do any fs stuff on create'); + it('should return promise on end call'); describe('create', () => { it('should create directory for report', () => { diff --git a/test/lib/index.js b/test/lib/index.js index dd8f818..b034232 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -137,19 +137,19 @@ describe('plugin', () => { }); describe('on RUNNER_END', () => { - it('should finalize data file', () => { + it('should finalize data file', async () => { initPlugin_(); - hermione.emit(hermione.events.RUNNER_END); + await hermione.emit(hermione.events.RUNNER_END); assert.calledOnce(DataFile.prototype.end); }); ['index.html', 'bundle.min.js', 'styles.css'].forEach((fileName, i) => { - it(`should copy "${fileName}" service file to the report dir on runner end`, () => { + it(`should copy "${fileName}" service file to the report dir on runner end`, async () => { initPlugin_({path: 'reportDir'}); - hermione.emit(hermione.events.RUNNER_END); + await hermione.emit(hermione.events.RUNNER_END); assert.equal(fs.copySync.args[i][1], `reportDir/${fileName}`); }); From 76f031feca4d5371b74d13633e6b9f0460b10c6a Mon Sep 17 00:00:00 2001 From: Anton Usmansky Date: Thu, 2 Aug 2018 17:31:07 +0300 Subject: [PATCH 7/8] chore: add TODO to copy files on RUNNER_END asynchronously --- test/lib/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/lib/index.js b/test/lib/index.js index b034232..5f7416e 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -154,6 +154,8 @@ describe('plugin', () => { assert.equal(fs.copySync.args[i][1], `reportDir/${fileName}`); }); }); + + it('should copy files asynchronously'); }); describe('on NEW_BROWSER', () => { From f49be405300c44e02d6475f3ff36270953e35d01 Mon Sep 17 00:00:00 2001 From: Anton Usmansky Date: Wed, 8 Aug 2018 13:09:24 +0300 Subject: [PATCH 8/8] WIP --- lib/data-file.js | 22 ++--- package-lock.json | 9 ++ package.json | 3 +- test/lib/data-file.js | 212 ++++++++++++++++++++++++++++++++---------- test/setup.js | 2 + 5 files changed, 188 insertions(+), 60 deletions(-) diff --git a/lib/data-file.js b/lib/data-file.js index 7308e14..86e4fa1 100644 --- a/lib/data-file.js +++ b/lib/data-file.js @@ -6,12 +6,14 @@ const _ = require('lodash'); module.exports = class DataFile { static create(reportPath) { - return new DataFile(reportPath); + return new this(reportPath); } constructor(reportPath) { - fs.ensureDirSync(reportPath); - this._stream = fs.createWriteStream(path.join(reportPath, 'data.js')); + this._promise = fs.ensureDir(reportPath) + .then(() => fs.open(path.join(reportPath, 'data.js'))) + .then((fd) => this._fd = fd) + .then(() => fs.appendFile(this._fd, 'const data = [')); } write(data) { @@ -31,16 +33,14 @@ module.exports = class DataFile { testInfo.r = retry; } - this._writeDelim(); - this._stream.write(JSON.stringify(testInfo)); + const chunk = `${JSON.stringify(testInfo)},`; + this._promise = this._promise + .then(() => fs.appendFile(this._fd, chunk)); } end() { - this._stream.end(']'); - } - - _writeDelim() { - this._stream.write('const data = ['); - this._writeDelim = () => this._stream.write(','); + return this._promise + .then(() => fs.appendFile(this._fd, ']')) + .then(() => fs.close(this._fd)); } }; diff --git a/package-lock.json b/package-lock.json index 488f8e0..4f31bae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2384,6 +2384,15 @@ "type-detect": "^4.0.0" } }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "^1.0.2" + } + }, "chain-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.0.tgz", diff --git a/package.json b/package.json index 3748032..ba8ec0e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Plugin for hermione for commands execution profiling", "scripts": { "lint": "eslint .", - "test": "npm run lint && npm run test-unit", + "test": "npm run test-unit && npm run lint", "test-unit": "mocha test", "build": "webpack --config webpack.prod.js", "prepublish": "npm run build", @@ -41,6 +41,7 @@ "babel-preset-react": "^6.24.1", "babel-preset-stage-2": "^6.24.1", "chai": "^4.0.2", + "chai-as-promised": "^7.1.1", "css-loader": "^0.28.7", "eslint": "^3.19.0", "eslint-config-gemini-testing": "^2.8.0", diff --git a/test/lib/data-file.js b/test/lib/data-file.js index 5481dc2..2f87b49 100644 --- a/test/lib/data-file.js +++ b/test/lib/data-file.js @@ -5,104 +5,220 @@ const DataFile = require('../../lib/data-file'); describe('stream writer', () => { const sandbox = sinon.sandbox.create(); - let streamStub; + // let streamStub; const dataStub = (opts = {}) => { return Object.assign( {browserId: 'default-browser'}, opts, - {fullTitle: () => opts.fullTitle || 'fullTitle'} + {fullTitle: () => opts.fullTitle || 'defaultFullTitle'} ); }; + const mkDataFile = (reportPath = 'default/path') => { + return DataFile.create(reportPath); + }; + beforeEach(() => { - streamStub = { - write: sinon.stub().named('write'), - end: sinon.stub().named('end') - }; - sandbox.stub(fs, 'ensureDirSync'); - sandbox.stub(fs, 'createWriteStream').returns(streamStub); + // streamStub = { + // write: sinon.stub().named('write'), + // end: sinon.stub().named('end') + // }; + // sandbox.stub(fs, 'ensureDirSync'); + // sandbox.stub(fs, 'createWriteStream').returns(streamStub); + + sandbox.stub(fs, 'appendFile'); + sandbox.stub(fs, 'ensureDir').resolves(); + sandbox.stub(fs, 'open').resolves(12345); + sandbox.stub(fs, 'close').resolves(); }); afterEach(() => sandbox.restore()); - it('should not do any fs stuff on create'); it('should return promise on end call'); + it('should not do any fs stuff on create'); describe('create', () => { - it('should create directory for report', () => { - DataFile.create('report/path'); + it('should create ensure target dir is exists', async () => { + const dataFile = mkDataFile('report/path'); + + await dataFile.end(); - assert.calledOnceWith(fs.ensureDirSync, 'report/path'); + assert.calledOnceWith(fs.ensureDir, 'report/path'); }); - it('should create write stream to the passed path', () => { - DataFile.create('report/path'); + it('should open data file in target dir', async () => { + const dataFile = mkDataFile('report/path'); + + await dataFile.end(); + + assert.calledOnceWith(fs.open, 'report/path/data.js'); + }); + + it('should write opening bracket into data file', async () => { + fs.open.resolves(100500); + const dataFile = mkDataFile(); + + await dataFile.end(); - assert.calledOnceWith(fs.createWriteStream, 'report/path/data.js'); + assert.calledWith(fs.appendFile, 100500, 'const data = ['); }); }); describe('write', () => { - it('should write opening bracket at first call', () => { - const stream = DataFile.create('report/path'); + const getWrittenData = () => { + return JSON.parse(fs.appendFile.secondCall.args[1].replace(/,$/, '')); + }; + + const write = async (data) => { + const dataFile = mkDataFile(); + dataFile.write(data); + await dataFile.end(); + }; - stream.write(dataStub()); + it('should append data to file', async () => { + fs.open.resolves(100500); + const dataFile = mkDataFile(); - assert.calledWith(streamStub.write.firstCall, 'const data = ['); + dataFile.write(dataStub()); + await dataFile.end(); + + assert.calledThrice(fs.appendFile); + assert.alwaysCalledWith(fs.appendFile, 100500, sinon.match.string); }); - it('should write object with "n" property as "fullTitle"', () => { - const stream = DataFile.create('report/path'); - const data = dataStub({fullTitle: 'test1'}); + it('should write data with coma at the end', async () => { + await write(dataStub()); + + const writtenData = fs.appendFile.secondCall.args[1]; + assert.match(writtenData, /,$/); + }); - stream.write(data); + it('should write test full title', async () => { + await write(dataStub({ + fullTitle: 'test1' + })); - assert.propertyVal(JSON.parse(streamStub.write.secondCall.args[0]), 'n', 'test1'); + assert.match(getWrittenData(), { + n: 'test1' + }); }); - it('should write object with command list from data', () => { - const stream = DataFile.create('report/path'); - const data = dataStub({ - fullTitle: 'test1', + it('should write command list from hermioneCtx', async () => { + await write(dataStub({ hermioneCtx: { commandList: [{cl: [1]}] } + })); + + assert.match(getWrittenData(), { + cl: [1] }); + }); - stream.write(data); - const passedData = JSON.parse(streamStub.write.secondCall.args[0]); + it('should write empty command list if there is no command list in data') - assert.deepEqual(passedData.cl, [1]); - }); + it('should write timings', async () => { + await write(dataStub({ + timeStart: 100400, + timeEnd: 100500 + })); - it('should write object with empty command list if it does not exist in data', () => { - const stream = DataFile.create('report/path'); - const data = dataStub({fullTitle: 'test1'}); + assert.match(getWrittenData(), { + ts: 100400, + te: 100500, + d: 100 // duration + }); + }); - stream.write(data); - const passedData = JSON.parse(streamStub.write.secondCall.args[0]); + it('should write browser data', async () => { + await write(dataStub({ + browserId: 'bro', + sessionId: '100500' + })); - assert.deepEqual(passedData.cl, []); + assert.match(getWrittenData(), { + bid: 'bro', + sid: '100500' + }); }); - it('should divide data chains with comma delimiter', () => { - const stream = DataFile.create('report/path'); + it('should write retry', async () => { + await write(dataStub({ + retry: 100500 + })); + + assert.match(getWrittenData(), { + r: 100500 + }); + }); - stream.write(dataStub()); - stream.write(dataStub()); + it('should not write retry there is no retry in data', async () => { + await write(dataStub()); - // calls: 1 - open bracket, 2 - first data, 3 - delim, 4 - second data - assert.calledWithExactly(streamStub.write.thirdCall, ','); + assert.notProperty(getWrittenData(), 'r'); }); + + // it('should write object with command list from data', () => { + // const stream = DataFile.create('report/path'); + // const data = dataStub({ + // fullTitle: 'test1', + // hermioneCtx: { + // commandList: [{cl: [1]}] + // } + // }); + + // stream.write(data); + // const passedData = JSON.parse(streamStub.write.secondCall.args[0]); + + // assert.deepEqual(passedData.cl, [1]); + // }); + + // it('should write object with empty command list if it does not exist in data', () => { + // const stream = DataFile.create('report/path'); + // const data = dataStub({fullTitle: 'test1'}); + + // stream.write(data); + // const passedData = JSON.parse(streamStub.write.secondCall.args[0]); + + // assert.deepEqual(passedData.cl, []); + // }); + + // it('should divide data chains with comma delimiter', () => { + // const stream = DataFile.create('report/path'); + + // stream.write(dataStub()); + // stream.write(dataStub()); + + // // calls: 1 - open bracket, 2 - first data, 3 - delim, 4 - second data + // assert.calledWithExactly(streamStub.write.thirdCall, ','); + // }); }); describe('end', () => { - it('should end stream with the closing bracket', () => { - const stream = DataFile.create('report/path'); + it('should reject if failed to open file', async () => { + fs.open.rejects(new Error('foo')); + const dataFile = mkDataFile(); + + await assert.isRejected(dataFile.end(), /foo/); + }); + + it('should add closing bracket to data file', async () => { + fs.open.resolves(100500); + const dataFile = mkDataFile(); + + await dataFile.end(); + + assert.calledWith(fs.appendFile, 100500, ']'); + }); + + it('should close file', async () => { + fs.open.resolves(100500); + const dataFile = mkDataFile(); + + await dataFile.end(); - stream.end(); - assert.calledOnceWith(streamStub.end, ']'); + assert.calledOnceWith(fs.close, 100500); }); }); }); diff --git a/test/setup.js b/test/setup.js index 3931e95..bdcfadf 100644 --- a/test/setup.js +++ b/test/setup.js @@ -5,4 +5,6 @@ const chai = require('chai'); global.sinon = require('sinon'); global.assert = chai.assert; +chai.use(require('chai-as-promised')); + sinon.assert.expose(chai.assert, {prefix: ''});