Commit e6c7469f e6c7469f7318174b1cc6a620acf6c53bbcd5cc59 by Nicolas Perriault

introduced global setUp() and tearDown() hooks

1 parent 19a08203
......@@ -66,7 +66,37 @@ casper.test.begin('Casperjs.org is navigable', 2, function suite(test) {
});
```
`Tester#begin()` has also `setUp()` and `tearDown()` capabilities if you pass it a configuration object instead of a function:
[`Tester#setUp()`](docs.casperjs.org/en/latest/modules/tester.html#setup) and [`Tester#tearDown()`](docs.casperjs.org/en/latest/modules/tester.html#teardown) methods have been also added in order to ease the definition of operations to be performed before and after each test defined using `Tester#begin()`:
```js
casper.test.setUp(function() {
console.log('executed before each test');
});
casper.test.tearDown(function() {
console.log('executed after each test');
});
```
Both can work asynchronously as well of you define the `done` argument:
```js
casper.test.setUp(function(done) {
setTimeout(function() {
console.log('asynchronously executed before each test');
done();
}, 1000);
});
casper.test.tearDown(function(done) {
setTimeout(function() {
console.log('asynchronously executed after each test');
done();
}, 1000);
});
```
`Tester#begin()` itself has also local `setUp()` and `tearDown()` capabilities if you pass it a configuration object instead of a function:
```js
casper.test.begin('range tests', 1, {
......@@ -75,9 +105,11 @@ casper.test.begin('range tests', 1, {
setUp: function(test) {
this.range.push(3);
},
tearDown: function(test) {
range = [];
},
test: function(test) {
test.assertEquals(range.length, 3);
test.done();
......@@ -85,7 +117,7 @@ casper.test.begin('range tests', 1, {
});
```
Also, scraping and testing are now betterly separated in CasperJS, and bad code is now a bit less bad. That involves breaking up BC on some points though:
Scraping and testing are now better separated in CasperJS. That involves breaking up BC on some points though:
- The Casper object won't be created with a `test` reference if not invoked using the [`casperjs test` command](http://casperjs.org/testing.html#casper-test-command), therefore the ability to run any test without calling it has been dropped. I know, get over it.
- Passing the planned number of tests to `casper.done()` has been dropped as well, because `done()` may be never called at all when big troubles happen; rather use the new `begin()` method and provide the expected number of tests using the second argument:
......
......@@ -761,6 +761,31 @@ Render test results, save results in an XUnit formatted file, and optionally exi
This method is not to be called when using the ``casperjs test`` command (see documentation for :doc:`testing <../testing>`), where it's done automatically for you.
``setUp()``
-------------------------------------------------------------------------------
**Signature:** ``setUp([Function fn])``
Defines a function which will be executed before every test defined using `begin()`_::
casper.test.setUp(function() {
casper.start().userAgent('Mosaic 0.1');
});
To perform asynchronous operations, use the ``done`` argument::
casper.test.setUp(function(done) {
casper.start('http://foo').then(function() {
// ...
}).run(done);
});
.. warning::
Don't specify the ``done`` argument if you don't intend to use the method asynchronously.
.. seealso:: `tearDown()`_
``skip()``
-------------------------------------------------------------------------------
......@@ -774,3 +799,28 @@ Skips a given number of planned tests::
test.skip(2, 'Two tests skipped');
test.done();
});
``tearDown()``
-------------------------------------------------------------------------------
**Signature:** ``tearDown([Function fn])``
Defines a function which will be executed before after every test defined using `begin()`_::
casper.test.tearDown(function() {
casper.echo('See ya');
});
To perform asynchronous operations, use the ``done`` argument::
casper.test.tearDown(function(done) {
casper.start('http://foo/goodbye').then(function() {
// ...
}).run(done);
});
.. warning::
Don't specify the ``done`` argument if you don't intend to use the method asynchronously.
.. seealso:: `setUp()`_
......
......@@ -96,6 +96,8 @@ var Tester = function Tester(casper, options) {
this.casper = casper;
// public properties
this._setUp = undefined;
this._tearDown = undefined;
this.aborted = false;
this.executed = 0;
this.currentTestFile = null;
......@@ -892,27 +894,44 @@ Tester.prototype.bar = function bar(text, style) {
};
/**
* Starts a suite.
* Defines a function which will be executed before every test.
*
* Can be invoked two different ways:
* @param Function fn
*/
Tester.prototype.setUp = function setUp(fn) {
"use strict";
this._setUp = fn;
};
/**
* Defines a function which will be executed after every test.
*
* casper.test.begin("suite description", plannedTests, function(test){})
* @param Function fn
*/
Tester.prototype.tearDown = function tearDown(fn) {
"use strict";
this._tearDown = fn;
};
/**
* Starts a suite.
*
* Or:
* Can be invoked different ways:
*
* casper.test.begin("suite description", plannedTests, function(test){})
* casper.test.begin("suite description", function(test){})
*
*/
Tester.prototype.begin = function begin() {
"use strict";
if (this.started && this.running) {
if (this.started && this.running)
return this.queue.push(arguments);
}
function getConfig(args) {
var config = {
setUp: function(){},
tearDown: function(){}
};
if (utils.isFunction(args[1])) {
config.test = args[1];
} else if (utils.isObject(args[1])) {
......@@ -926,42 +945,56 @@ Tester.prototype.begin = function begin() {
} else {
throw new CasperError('Invalid call');
}
if (!utils.isFunction(config.test)) {
if (!utils.isFunction(config.test))
throw new CasperError('begin() is missing a mandatory test function');
}
return config;
}
var description = arguments[0] || f("Untitled suite in %s", this.currentTestFile),
config = getConfig([].slice.call(arguments));
if (!this.options.concise) {
config = getConfig([].slice.call(arguments)),
next = function() {
config.test(this, this.casper);
if (this.options.concise)
this.casper.echo([
this.colorize('PASS', 'INFO'),
this.formatMessage(description),
this.colorize(f('(%d test%s)',
config.planned,
config.planned > 1 ? 's' : ''), 'INFO')
].join(' '));
}.bind(this);
if (!this.options.concise)
this.comment(description);
}
this.currentSuite = new TestCaseResult({
name: description,
file: this.currentTestFile,
config: config,
planned: config.planned || undefined
});
this.executed = 0;
this.running = this.started = true;
try {
if (config.setUp) {
if (config.setUp)
config.setUp(this, this.casper);
}
config.test(this, this.casper);
if (!this._setUp)
return next();
if (this._setUp.length > 0)
return this._setUp.call(this, next); // async
this._setUp.call(this); // sync
next();
} catch (err) {
this.processError(err);
this.done();
}
if (this.options.concise) {
this.casper.echo([
this.colorize('PASS', 'INFO'),
this.formatMessage(description),
this.colorize(f('(%d test%s)',
config.planned,
config.planned > 1 ? 's' : ''), 'INFO')
].join(' '));
}
};
/**
......@@ -995,10 +1028,12 @@ Tester.prototype.done = function done() {
"use strict";
/*jshint maxstatements:20, maxcomplexity:20*/
var planned, config = this.currentSuite && this.currentSuite.config || {};
if (utils.isNumber(arguments[0])) {
this.casper.warn('done() `planned` arg is deprecated as of 1.1');
planned = arguments[0];
}
if (config && config.tearDown && utils.isFunction(config.tearDown)) {
try {
config.tearDown(this, this.casper);
......@@ -1006,25 +1041,45 @@ Tester.prototype.done = function done() {
this.processError(error);
}
}
if (this.currentSuite && this.currentSuite.planned &&
this.currentSuite.planned !== this.executed + this.currentSuite.skipped &&
!this.currentSuite.failed) {
this.dubious(this.currentSuite.planned, this.executed, this.currentSuite.name);
} else if (planned && planned !== this.executed) {
// BC
this.dubious(planned, this.executed);
}
if (this.currentSuite) {
this.suiteResults.push(this.currentSuite);
this.currentSuite = undefined;
this.executed = 0;
var next = function() {
if (this.currentSuite && this.currentSuite.planned &&
this.currentSuite.planned !== this.executed + this.currentSuite.skipped &&
!this.currentSuite.failed) {
this.dubious(this.currentSuite.planned, this.executed, this.currentSuite.name);
} else if (planned && planned !== this.executed) {
// BC
this.dubious(planned, this.executed);
}
if (this.currentSuite) {
this.suiteResults.push(this.currentSuite);
this.currentSuite = undefined;
this.executed = 0;
}
this.emit('test.done');
this.casper.currentHTTPResponse = {};
this.running = this.started = false;
var nextTest = this.queue.shift();
if (nextTest) {
this.begin.apply(this, nextTest);
}
}.bind(this);
if (!this._tearDown) {
return next();
}
this.emit('test.done');
this.casper.currentHTTPResponse = {};
this.running = this.started = false;
var nextTest = this.queue.shift();
if (nextTest) {
this.begin.apply(this, nextTest);
try {
if (this._tearDown.length > 0) {
// async
this._tearDown.call(this, next);
} else {
// sync
this._tearDown.call(this);
next();
}
} catch (error) {
this.processError(error);
}
};
......
/*global casper*/
/*jshint strict:false*/
var setUp, tearDown;
casper.test.setUp(function(done) {
setTimeout(function() {
setUp = true;
done();
}, 50);
});
casper.test.tearDown(function(done) {
setTimeout(function() {
tearDown = true;
done();
}, 50);
});
casper.test.begin('setUp() tests', 1, function(test) {
test.assertTrue(setUp, 'Tester.setUp() executed the async setup function');
test.done();
});
casper.test.begin('tearDown() tests', 1, function(test) {
test.assertTrue(tearDown, 'Tester.tearDown() executed the async tear down function');
// reset
test.setUp();
test.tearDown();
test.done();
});
/*global casper*/
/*jshint strict:false*/
var setUp, tearDown;
casper.test.setUp(function() {
setUp = true;
});
casper.test.tearDown(function() {
tearDown = true;
});
casper.test.begin('setUp() tests', 1, function(test) {
test.assertTrue(setUp, 'Tester.setUp() executed the setup function');
test.done();
});
casper.test.begin('tearDown() tests', 1, function(test) {
test.assertTrue(tearDown, 'Tester.tearDown() executed the tear down function');
// reset
test.setUp();
test.tearDown();
test.done();
});