diff --git a/README.md b/README.md index 7fe4aded3..ff62a93de 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,15 @@ Hermione is the utility for integration testing of web pages using [WebdriverIO] - [system](#system) - [debug](#debug) - [mochaOpts](#mochaopts) + - [ctx](#ctx) - [plugins](#plugins) - [prepareBrowser](#preparebrowser) - [prepareEnvironment](#prepareenvironment) - [CLI](#cli) - [Overriding settings](#overriding-settings) - [Programmatic API](#programmatic-api) + - [run](#run) + - [readTests](#readtests) - [Environment variables](#environment-variables) - [HERMIONE_SKIP_BROWSERS](#hermione_skip_browsers) @@ -468,6 +471,22 @@ mochaOpts: { } ``` +#### ctx +A context which will be available in tests via method `hermione.ctx`: +```javascript +ctx: { + foo: 'bar' +} +``` + +```javascript +it('awesome test', function() { + console.log(hermione.ctx); // {foo: 'bar'} +}); +``` + +**Recommendation**: use `ctx` in your tests in favor of global variables. + ### plugins `Hermione` plugins are commonly used to extend built-in possibilities. For example, [hermione-allure-reporter](https://github.com/gemini-testing/hermione-allure-reporter) and [hermione-tunnel](https://github.com/gemini-testing/hermione-tunnel). diff --git a/lib/config/options.js b/lib/config/options.js index cb4d7e328..bbe20f40a 100644 --- a/lib/config/options.js +++ b/lib/config/options.js @@ -24,7 +24,9 @@ const rootSection = section(_.extend(browserOptions.getTopLevel(), { system: section({ debug: options.boolean('debug'), - mochaOpts: options.optionalObject('mochaOpts') + mochaOpts: options.optionalObject('mochaOpts'), + + ctx: options.anyObject() }), plugins: options.anyObject(), diff --git a/lib/runner/mocha-runner/index.js b/lib/runner/mocha-runner/index.js index 8d408ffa3..dac40cf9e 100644 --- a/lib/runner/mocha-runner/index.js +++ b/lib/runner/mocha-runner/index.js @@ -14,6 +14,7 @@ module.exports = class MochaRunner extends QEmitter { super(); this._sharedMochaOpts = config.system.mochaOpts; + this._ctx = config.system.ctx; this._browserAgent = browserAgent; this._testSkipper = testSkipper; } @@ -33,7 +34,7 @@ module.exports = class MochaRunner extends QEmitter { } _createMocha(suiteFiles, titles, filterFn) { - const mochaAdapter = MochaAdapter.create(this._sharedMochaOpts, this._browserAgent); + const mochaAdapter = MochaAdapter.create(this._sharedMochaOpts, this._browserAgent, this._ctx); suiteFiles = [].concat(suiteFiles); return mochaAdapter diff --git a/lib/runner/mocha-runner/mocha-adapter.js b/lib/runner/mocha-runner/mocha-adapter.js index a382171f2..aa315d1c6 100644 --- a/lib/runner/mocha-runner/mocha-adapter.js +++ b/lib/runner/mocha-runner/mocha-adapter.js @@ -22,11 +22,11 @@ const isSkipped = (suite) => { }; module.exports = class MochaAdapter { - static create(opts, browserAgent) { - return new MochaAdapter(opts, browserAgent); + static create(opts, browserAgent, ctx) { + return new MochaAdapter(opts, browserAgent, ctx); } - constructor(opts, browserAgent) { + constructor(opts, browserAgent, ctx) { this._mocha = new Mocha(opts); this._mocha.fullTrace(); this.suite = this._mocha.suite; @@ -35,10 +35,14 @@ module.exports = class MochaAdapter { this._browser = null; this._currentRunnable = null; + this._hermione = {ctx}; this._injectBrowser(); - this._injectSkip(); this._injectRunnableSpy(); + this._injectSkip(); + + this.suite.on('pre-require', () => global.hermione = this._hermione); + this.suite.on('post-require', () => delete global.hermione); } applySkip(testSkipper) { @@ -97,18 +101,10 @@ module.exports = class MochaAdapter { _injectSkip() { const skip = new Skip(); + const skipBuilder = new SkipBuilder(skip, this._browserAgent.browserId); + const onlyBuilder = new OnlyBuilder(skipBuilder); - this.suite.on('pre-require', () => { - const skipBuilder = new SkipBuilder(skip, this._browserAgent.browserId); - const onlyBuilder = new OnlyBuilder(skipBuilder); - - global.hermione = { - skip: skipBuilder, - only: onlyBuilder - }; - }); - - this.suite.on('post-require', () => delete global.hermione); + _.extend(this._hermione, {skip: skipBuilder, only: onlyBuilder}); this._addEventHandler(['suite', 'test'], (runnable) => skip.handleEntity(runnable)); } diff --git a/test/lib/config/options.js b/test/lib/config/options.js index 426504541..ab6aa013c 100644 --- a/test/lib/config/options.js +++ b/test/lib/config/options.js @@ -63,6 +63,23 @@ describe('config options', () => { assert.deepEqual(config.system.mochaOpts.grep, /test/); }); }); + + describe('ctx', () => { + it('should be empty by default', () => { + const config = createConfig(); + + assert.deepEqual(config.system.ctx, {}); + }); + + it('should override ctx option', () => { + const readConfig = _.set({}, 'system.ctx', {some: 'ctx'}); + Config.read.returns(readConfig); + + const config = createConfig(); + + assert.deepEqual(config.system.ctx, {some: 'ctx'}); + }); + }); }); describe('prepareEnvironment', () => { diff --git a/test/lib/runner/mocha-runner/index.js b/test/lib/runner/mocha-runner/index.js index 2a5f235a1..45803ed73 100644 --- a/test/lib/runner/mocha-runner/index.js +++ b/test/lib/runner/mocha-runner/index.js @@ -12,7 +12,7 @@ describe('mocha-runner', () => { const mochaRunnerInit = () => { return new MochaRunner( - {system: {mochaOpts: {}}}, + {system: {mochaOpts: {}, ctx: {}}}, sinon.createStubInstance(BrowserAgent), sinon.createStubInstance(TestSkipper) ); @@ -60,6 +60,14 @@ describe('mocha-runner', () => { )); }); + it('should share a ctx from config between all mocha instances', () => { + return run_(['path/to/file', 'path/to/other/file']) + .then(() => assert.equal( + MochaAdapter.create.firstCall.args[2], + MochaAdapter.create.secondCall.args[2] + )); + }); + it('should run all mocha instances', () => { return run_(['some/file', 'other/file']) .then(() => assert.calledTwice(MochaAdapter.prototype.run)); diff --git a/test/lib/runner/mocha-runner/mocha-adapter.js b/test/lib/runner/mocha-runner/mocha-adapter.js index aa2025057..afdf3a686 100644 --- a/test/lib/runner/mocha-runner/mocha-adapter.js +++ b/test/lib/runner/mocha-runner/mocha-adapter.js @@ -56,10 +56,8 @@ describe('mocha-runner/mocha-adapter', () => { }); } - const mkMochaAdapter_ = (opts) => { - testSkipper = sinon.createStubInstance(TestSkipper); - - return MochaAdapter.create(opts || {}, browserAgent, testSkipper); + const mkMochaAdapter_ = (opts, ctx) => { + return MochaAdapter.create(opts || {}, browserAgent, ctx); }; const mkBrowserStub_ = () => { @@ -67,6 +65,8 @@ describe('mocha-runner/mocha-adapter', () => { }; beforeEach(() => { + testSkipper = sinon.createStubInstance(TestSkipper); + clearRequire = sandbox.stub().named('clear-require'); browserAgent = sinon.createStubInstance(BrowserAgent); @@ -147,31 +147,45 @@ describe('mocha-runner/mocha-adapter', () => { assert.isDefined(global.hermione); }); - it('hermione.skip should return SkipBuilder instance', () => { - const mochaAdapter = mkMochaAdapter_(); + describe('hermione global', () => { + afterEach(() => delete global.hermione); - mochaAdapter.addFiles(['path/to/file']); - MochaStub.prototype.suite.emit('pre-require'); + it('hermione.skip should return SkipBuilder instance', () => { + const mochaAdapter = mkMochaAdapter_(); - assert.instanceOf(global.hermione.skip, SkipBuilder); - }); + mochaAdapter.addFiles(['path/to/file']); + MochaStub.prototype.suite.emit('pre-require'); - it('hermione.only should return OnlyBuilder instance', () => { - const mochaAdapter = mkMochaAdapter_(); + assert.instanceOf(global.hermione.skip, SkipBuilder); + }); - mochaAdapter.addFiles(['path/to/file']); - MochaStub.prototype.suite.emit('pre-require'); + it('hermione.only should return OnlyBuilder instance', () => { + const mochaAdapter = mkMochaAdapter_(); - assert.instanceOf(global.hermione.only, OnlyBuilder); - }); + mochaAdapter.addFiles(['path/to/file']); + MochaStub.prototype.suite.emit('pre-require'); - it('should remove global "hermione" object on "post-require" event', () => { - const mochaAdapter = mkMochaAdapter_(); + assert.instanceOf(global.hermione.only, OnlyBuilder); + }); - mochaAdapter.addFiles(['path/to/file']); - MochaStub.prototype.suite.emit('post-require'); + it('hermione.ctx should return passed ctx', () => { + const mochaAdapter = mkMochaAdapter_({}, {some: 'ctx'}); + + mochaAdapter.addFiles(['path/to/file.js']); + MochaStub.prototype.suite.emit('pre-require'); - assert.isUndefined(global.hermione); + assert.deepEqual(global.hermione.ctx, {some: 'ctx'}); + }); + + it('should remove global "hermione" object on "post-require" event', () => { + const mochaAdapter = mkMochaAdapter_(); + + mochaAdapter.addFiles(['path/to/file']); + MochaStub.prototype.suite.emit('pre-require'); + MochaStub.prototype.suite.emit('post-require'); + + assert.isUndefined(global.hermione); + }); }); });