- /*!
- *
- * Copyright (c) 2013 Sebastian Golasch
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- */
-
- 'use strict';
-
- // Ext. libs
- var _ = require('lodash');
- var Q = require('q');
-
- // Int. libs
- var actions = require('dalek-internal-actions');
- var assertions = require('dalek-internal-assertions');
-
- /**
- * Prepares the test instance values
- *
- * @param {object} opts Options like the tests name, etc.
- * @constructor
- */
-
- var Test = function (opts) {
- // prepare meta queue data
- this.actionPromiseQueue = [];
-
- // prepare assertion data
- this.expactation = null;
- this.runnedExpactations = 0;
- this.failedAssertions = 0;
-
- // prepare test specific data
- this.name = opts.name;
- this.lastChain = [];
- this.uuids = {};
- this.contextVars = {};
-
- if (this.name) {
- this.timeoutForDone = setTimeout(function () {
- this.done();
- this.reporter.emit('warning', 'done not called!');
- }.bind(this), 2000);
- }
- };
-
- /**
- * Generates an test instance
- *
- * @module DalekJS
- * @class Test
- * @namespace Dalek
- * @part test
- * @api
- */
-
- Test.prototype = {
-
- /**
- * Specify how many assertions are expected to run within a test.
- * Very useful for ensuring that all your callbacks and assertions are run.
- *
- * @method expect
- * @param {Integer} expecatation Number of assertions that should be run
- * @chainable
- */
-
- expect: function (expectation) {
- this.expectation = parseInt(expectation, 10);
- return this;
- },
-
- /**
- * Global data store (works between the node & browser envs)
- *
- * @method expect
- * @param {string|number} key Key to store or fetch data
- * @param {mixed} value *optional* Data that should be stored
- * @return {mixed} Data that has been stored
- * @chainable
- */
-
- data: function (key, value) {
- if (value) {
- this.contextVars[key] = value;
- return this;
- }
-
- return this.contextVars[key];
- },
-
- /**
- * Increment the number of executed assertions
- *
- * @method incrementExpectations
- * @chainable
- */
-
- incrementExpectations: function () {
- this.runnedExpactations++;
- return this;
- },
-
- /**
- * Increment the number of failed assertions
- *
- * @method incrementFailedAssertions
- * @chainable
- */
-
- incrementFailedAssertions: function () {
- this.failedAssertions++;
- return this;
- },
-
- /**
- * Checks if the runned tests fullfill the set expectations
- * or if no expectations were raised
- *
- * @method checkExpectations
- * @return {bool} checkedExpectations Expectations match
- */
-
- checkExpectations: function () {
- return (this.expectation === null || !this.expectation || (this.runnedExpactations === this.expectation));
- },
-
- /**
- * Checks if all runned assertions passed
- *
- * @method checkAssertions
- * @return {bool} assertiosnFailed Any expectation failed
- */
-
- checkAssertions: function () {
- return this.failedAssertions === 0;
- },
-
- /**
- * Sets up all the bindings needed for a test to run
- *
- * @method done
- * @return {object} result A promise
- * @private
- */
-
- done: function () {
- var result = Q.resolve();
- // clear the done error timeout
- clearTimeout(this.timeoutForDone);
- // remove all previously attached event listeners to clear the message queue
- this.driver.events.removeAllListeners('driver:message');
- // resolve the deferred when the test is finished
- Test.testStarted.fin(this._testFinished.bind(this, result));
- return result;
- },
-
- /**
- * Emits the test finished events & resolves all promises
- * when its done
- *
- * @method _testFinished
- * @param {object} result Promised result var
- * @return {object} result Promised result var
- * @private
- */
-
- _testFinished: function (result) {
- // add a last deferred function on the end of the action queue,
- // to tell that this test is finished
- this.actionPromiseQueue.push(this._testFin.bind(this));
-
- // initialize all of the event receiver functions,
- // that later take the driver result
- this.actionPromiseQueue.forEach(function (f) {
- result = result.then(f).fail(function () {
- console.error(arguments);
- process.exit(0);
- });
- }.bind(this));
-
- // run the driver when all actions are stored in the queue
- Q.allSettled(this.actionPromiseQueue)
- .then(this.driver.run.bind(this.driver));
-
- return result;
- },
-
- /**
- * Emits the test started event
- *
- * @method _reportTestStarted
- * @param {string} name Name of the test
- * @chainable
- * @private
- */
-
- _reportTestStarted: function (name) {
- this.reporter.emit('report:test:started', {name: name});
- return this;
- },
-
- /**
- * Checks if the test run is complete & emits/resolves
- * all the needed events/promises when the run is complete
- *
- * @method _onDriverMessage
- * @param {object} data Data that is returned by the driver:message event
- * @chainable
- * @private
- */
-
- _onDriverMessage: function (data) {
- // check if the test run is complete
- if (data && data.key === 'run.complete') {
- // emit the test finish events & resolve the deferred
- this._emitConcreteTestFinished();
- this._emitAssertionStatus();
- this._emitTestFinished();
- this.deferred.resolve();
- }
-
- return this;
- },
-
- /**
- * Emits an event, that the current test run has been finished
- *
- * @method _emitConcreteTestFinished
- * @chainable
- * @private
- */
-
- _emitConcreteTestFinished: function () {
- this.events.emit('test:' + this._uid + ':finished', 'test:finished', this);
- return this;
- },
-
- /**
- * Emits an event that describes the current state of all assertions
- *
- * @method _emitAssertionStatus
- * @chainable
- * @private
- */
-
- _emitAssertionStatus: function () {
- this.reporter.emit('report:assertion:status', {
- expected: (this.expectation ? this.expectation : this.runnedExpactations),
- run: this.runnedExpactations,
- status: this._testStatus()
- });
- return this;
- },
-
- /**
- * Get the overall test status (assertions & expectation)
- *
- * @method _testStatus
- * @return {bool} status The test status
- * @chainable
- * @private
- */
-
- _testStatus: function () {
- return this.checkExpectations() && this.checkAssertions();
- },
-
- /**
- * Emits an event that describes the current state of all assertions.
- * The event should be fired when a test is finished
- *
- * @method _emitTestFinished
- * @chainable
- * @private
- */
-
- _emitTestFinished: function () {
- this.reporter.emit('report:test:finished', {
- name: this.name,
- id: this._uid,
- passedAssertions: this.runnedExpactations - this.failedAssertions,
- failedAssertions: this.failedAssertions,
- runnedExpactations: this.runnedExpactations,
- status: this._testStatus(),
- nl: true
- });
-
- return this;
- },
-
- /**
- * Kicks off the test & binds all promises/events
- *
- * @method _testFin
- * @return {object} promise A promise
- * @private
- */
-
- _testFin: function () {
- this.deferred = Q.defer();
-
- if (_.isFunction(this.driver.end)) {
- this.driver.end();
- }
-
- // emit report startet event
- this._reportTestStarted(this.name);
-
- // listen to all the messages from the driver
- this.driver.events.on('driver:message', this._onDriverMessage.bind(this));
- return this.deferred.promise;
- },
-
- /**
- * Copies assertion methods
- *
- * @method _inheritAssertions
- * @param {Test} test Instacne of test
- * @chainable
- * @private
- */
-
- _inheritAssertions: function (test) {
- ['is'].forEach(function (method) {
- test[method] = test.assert[method].bind(test.assert);
- });
- return test;
- },
-
- /**
- * Copies assertion helper methods
- *
- * @method _inheritAssertions
- * @param {Test} test Instacne of test
- * @chainable
- * @private
- */
-
- _inheritAssertionHelpers: function (test) {
- ['not', 'between', 'gt', 'gte', 'lt', 'lte'].forEach(function (method) {
- test.is[method] = test.assert[method].bind(test.assert);
- test.assert.is[method] = test.assert[method].bind(test.assert);
- });
- ['contain', 'match'].forEach(function (method) {
- test.to = test.to || {};
- test.assert.to = test.assert.to || {};
-
- test.to[method] = test.assert[method].bind(test.assert);
- test.assert.to[method] = test.assert[method].bind(test.assert);
- });
- return test;
- },
-
- /**
- * Set up the instance
- *
- * @method _inheritAssertions
- * @param {Test} test Instacne of test
- * @param {object} opts Options
- * @chainable
- * @private
- */
-
- _initialize: function (test, opts) {
- test._uid = _.uniqueId('test');
- test.events = opts.events;
- test.driver = opts.driver;
- test.reporter = opts.reporter;
- return test;
- }
-
- };
-
- // export a function that generates a new test instance
- module.exports = function (opts) {
- // mixin assertions, actions & getters
- Test.prototype = _.extend(Test.prototype, actions({reporter: opts.reporter}).prototype);
- var test = new Test(opts);
- test.assert = new (assertions())({test: test});
- test.assert.done = test.done.bind(this);
- test.assert.query = test.query.bind(test.assert);
- test.assert.$ = test.query.bind(test.assert);
- test.end = test.assert.end.bind(test.assert);
-
- // copy log methods
- test.log = {};
- test.log.dom = test.logger.dom.bind(test);
- test.log.message = test.logger.message.bind(test);
-
- // copy assertions methods
- test = test._inheritAssertions(test);
-
- // copy assertion helper methods
- test = test._inheritAssertionHelpers(test);
-
- // initialize the instance
- test = test._initialize(test, opts);
-
- // TODO: Promise driver start
- // so that we can reexecute them and clean the env between tests
- Test.testStarted = test.driver.start(Q);
- return test;
- };
-
-