Commit 484639be 484639be1e1e8f45c136d82a53b80e9adf2b97b9 by Nicolas Perriault

Initial attempt at bringing support for test cases - refs #56

1 parent a2ebe03a
......@@ -4,6 +4,7 @@ CasperJS Changelog
XXXX-XX-XX, v1.0.0
------------------
- merged PR [#322](https://github.com/n1k0/casperjs/pull/322) - Support number in withFrame()
- fixed [#323](https://github.com/n1k0/casperjs/issues/323) - `thenEvaluate()` should be updated to take the same parameters as `evaluate()`, while maintaining backwards compatibility.
- merged PR [#319](https://github.com/n1k0/casperjs/pull/319), fixed [#209](https://github.com/n1k0/casperjs/issues/209) - test duration has been added to XUnit XML result file.
- `Casper.userAgent()` does not require the instance to be started anymore
......
......@@ -5,17 +5,18 @@ You can check out the [contribution graphs on github](https://github.com/n1k0/ca
```
$ git shortlog -s -n | cut -c8-
Nicolas Perriault
oncletom
Brikou CARRE
oncletom
hannyu
Chris Lorenzo
Victor Yap
nrabinowitz
pborreli
Dave Lee
Rob Barreca
Andrew Childs
Solomon White
reina.sweet
Andrew Childs
Dave Lee
Reina Sweet
Elmar Langholz
Jason Funk
......@@ -24,7 +25,6 @@ Julien Moulin
Michael Geers
Jan Schaumann
Clochix
Rafael Garcia
Raphaël Benitte
Tim Bunce
alfetopito
......@@ -49,4 +49,5 @@ Mehdi Kabab
Mikko Peltonen
Pascal Borreli
Rafael
Rafael Garcia
```
......
Subproject commit b0f51d643a839fb66c174086c426b9c7b49e2a80
Subproject commit 0f6c923dbfe9bb605adfa5c9d6c8cd3ac1d56586
......
......@@ -1372,6 +1372,19 @@ Casper.prototype.sendKeys = function(selector, keys, options) {
};
/**
* Sets current page content.
*
* @param String content Desired page content
* @return Casper
*/
Casper.prototype.setContent = function setContent(content) {
"use strict";
this.checkStarted();
this.page.content = content;
return this;
};
/**
* Sets current WebPage instance the credentials for HTTP authentication.
*
* @param String username
......@@ -1873,18 +1886,22 @@ Casper.prototype.waitWhileVisible = function waitWhileVisible(selector, then, on
* Makes the provided frame page as the currently active one. Note that the
* active page will be reverted when finished.
*
* @param String frameName Target frame name
* @param String|Number frameInfo Target frame name or number
* @param Function then Next step function
* @return Casper
*/
Casper.prototype.withFrame = function withFrame(frameName, then) {
Casper.prototype.withFrame = function withFrame(frameInfo, then) {
"use strict";
this.then(function _step() {
if (this.page.childFramesName().indexOf(frameName) === -1) {
throw new CasperError(f('No frame named "%s" was found.', frameName));
if (utils.isNumber(frameInfo)) {
if (frameInfo > this.page.childFramesCount() - 1) {
throw new CasperError(f('Frame number "%d" is out of bounds.', frameInfo));
}
} else if (this.page.childFramesName().indexOf(frameInfo) === -1) {
throw new CasperError(f('No frame named "%s" was found.', frameInfo));
}
// make the frame page the currently active one
this.page.switchToChildFrame(frameName);
this.page.switchToChildFrame(frameInfo);
});
try {
this.then(then);
......
......@@ -200,6 +200,43 @@ function format(f) {
exports.format = format;
/**
* Formats a test value.
*
* @param Mixed value
* @return String
*/
function formatTestValue(value, name) {
"use strict";
var formatted = '';
if (value instanceof Error) {
formatted += value.message + '\n';
if (value.stack) {
formatted += indent(value.stack, 12, '#');
}
} else if (name === 'stack') {
if (isArray(value)) {
formatted += value.map(function(entry) {
return format('in %s() in %s:%d', (entry.function || "anonymous"), entry.file, entry.line);
}).join('\n');
} else {
formatted += 'not provided';
}
} else {
try {
formatted += serialize(value);
} catch (e) {
try {
formatted += serialize(value.toString());
} catch (e2) {
formatted += '(unserializable value)';
}
}
}
return formatted;
}
exports.formatTestValue = formatTestValue;
/**
* Retrieves the value of an Object foreign property using a dot-separated
* path string.
*
......@@ -226,6 +263,22 @@ function getPropertyPath(obj, path) {
exports.getPropertyPath = getPropertyPath;
/**
* Indents a string.
*
* @param String string
* @param Number nchars
* @param String prefix
* @return String
*/
function indent(string, nchars, prefix) {
"use strict";
return string.split('\n').map(function(line) {
return (prefix || '') + new Array(nchars).join(' ') + line;
}).join('\n');
}
exports.indent = indent;
/**
* Inherit the prototype methods from one constructor into another.
*
* @param {function} ctor Constructor function which needs to inherit the
......@@ -295,7 +348,7 @@ exports.isClipRect = isClipRect;
function isFalsy(subject) {
"use strict";
/*jshint eqeqeq:false*/
return subject == new Function('return false;')();
return !subject;
}
exports.isFalsy = isFalsy;
/**
......@@ -392,7 +445,7 @@ exports.isString = isString;
function isTruthy(subject) {
"use strict";
/*jshint eqeqeq:false*/
return subject == new Function('return true;')();
return !!subject;
}
exports.isTruthy = isTruthy;
......
......@@ -80,76 +80,75 @@ exports.create = function create() {
*/
function XUnitExporter() {
"use strict";
this._xml = utils.node('testsuite');
this.results = undefined;
this._xml = utils.node('testsuites');
this._xml.toString = function toString() {
return this.outerHTML; // ouch
return '<?xml version="1.0" encoding="UTF-8"?>' + this.outerHTML; // ouch
};
}
exports.XUnitExporter = XUnitExporter;
/**
* Adds a successful test result.
*
* @param String classname
* @param String name
* @param Number duration Test duration in milliseconds
*/
XUnitExporter.prototype.addSuccess = function addSuccess(classname, name, duration) {
"use strict";
var snode = utils.node('testcase', {
classname: generateClassName(classname),
name: name
});
if (duration !== undefined) {
snode.setAttribute('time', utils.ms2seconds(duration));
}
this._xml.appendChild(snode);
};
/**
* Adds a failed test result.
* Retrieves generated XML object - actually an HTMLElement.
*
* @param String classname
* @param String name
* @param String message
* @param String type
* @param Number duration Test duration in milliseconds
* @return HTMLElement
*/
XUnitExporter.prototype.addFailure = function addFailure(classname, name, message, type, duration) {
XUnitExporter.prototype.getXML = function getXML() {
"use strict";
var fnode = utils.node('testcase', {
classname: generateClassName(classname),
name: name
});
if (duration !== undefined) {
fnode.setAttribute('time', utils.ms2seconds(duration));
if (!(this.results instanceof require('tester').TestSuite)) {
throw new CasperError('Results not set, cannot get XML.');
}
var failure = utils.node('failure', {
type: type || "unknown"
});
failure.appendChild(document.createTextNode(message || "no message left"));
fnode.appendChild(failure);
this._xml.appendChild(fnode);
this.results.forEach(function(result) {
var suiteNode = utils.node('testsuite', {
name: result.name,
tests: result.assertions,
failures: result.failed,
time: utils.ms2seconds(result.calculateDuration()),
'package': generateClassName(result.file),
});
result.passes.forEach(function(success) {
var testCase = utils.node('testcase', {
name: success.message || success.standard,
classname: generateClassName(success.file),
time: utils.ms2seconds(~~success.time)
});
suiteNode.appendChild(testCase);
});
result.failures.forEach(function(failure) {
var testCase = utils.node('testcase', {
name: failure.message || failure.standard,
classname: generateClassName(failure.file),
time: utils.ms2seconds(~~failure.time)
});
var failureNode = utils.node('failure', {
type: failure.type || "failure"
});
failureNode.appendChild(document.createTextNode(failure.message || "no message left"));
if (failure.values && failure.values.error instanceof Error) {
var errorNode = utils.node('error', {
type: utils.betterTypeOf(failure.values.error)
});
errorNode.appendChild(document.createTextNode(failure.values.error.stack));
testCase.appendChild(errorNode);
}
testCase.appendChild(failureNode);
suiteNode.appendChild(testCase);
});
this._xml.appendChild(suiteNode);
}.bind(this));
this._xml.setAttribute('duration', utils.ms2seconds(this.results.calculateDuration()));
return this._xml;
};
/**
* Adds test suite duration
* Sets test results.
*
* @param Number duration Test duration in milliseconds
* @param TestSuite results
*/
XUnitExporter.prototype.setSuiteDuration = function setSuiteDuration(duration) {
XUnitExporter.prototype.setResults = function setResults(results) {
"use strict";
if (!isNaN(duration)) {
this._xml.setAttribute("time", utils.ms2seconds(duration));
if (!(results instanceof require('tester').TestSuite)) {
throw new CasperError('Invalid results type.');
}
};
/**
* Retrieves generated XML object - actually an HTMLElement.
*
* @return HTMLElement
*/
XUnitExporter.prototype.getXML = function getXML() {
"use strict";
return this._xml;
return this.results = results;
};
......
......@@ -25,7 +25,17 @@ casper.withFrame('frame2', function() {
this.test.assertTitle('CasperJS frame 3');
});
casper.withFrame(0, function() {
this.test.assertTitle('CasperJS frame 1');
this.test.assertExists("#f1");
this.test.assertDoesntExist("#f2");
});
casper.withFrame(1, function() {
this.test.assertTitle('CasperJS frame 3');
});
casper.run(function() {
this.test.assertTitle('CasperJS test frames');
this.test.done(10);
this.test.done(14);
});
......
......@@ -47,7 +47,7 @@ casper.thenOpen('tests/site/index.html', function() {
t.assertTruthy('1', 'Tester.assertTruthy() works as expected');
t.comment('Tester.assertFalsy()');
t.assertFalsy('0', 'Tester.assertFalsy() works as expected');
t.assertFalsy('', 'Tester.assertFalsy() works as expected');
t.comment('Tester.assertNot()');
t.assertNot(false, 'Tester.assertNot() works as expected');
......@@ -200,27 +200,6 @@ casper.reload(function() {
t.assertField('checklist[]', [], 'Tester.assertField() works as expected with check lists');
});
casper.then(function() {
t.comment('Tester.getFailures()');
t.assertEquals(typeof t.getFailures().length, "number", "Tester.getFailures() works as expected");
var passCount = t.getPasses().length;
t.comment('Tester.getPasses()');
t.assertEquals(1, 1, "Rogue assertEquals pass case");
t.assertEquals(t.getPasses().length, passCount + 1, "Tester.getPasses() works as expected");
});
casper.then(function() {
t.comment('Tester.calculateSuiteDuration()');
function add(a, b) {
return a + b;
}
var passedTime = t.getPassesTime().reduce(add, 0),
failedTime = t.getFailuresTime().reduce(add, 0),
calculatedSum = t.calculateSuiteDuration();
t.assertEquals(calculatedSum, passedTime + failedTime, "Tester.calculateSuiteDuration() works as expected")
});
casper.run(function() {
t.done(59);
t.done(55);
});
......
/*global casper*/
/*jshint strict:false*/
casper.test.comment('phantom.Casper.XUnitExporter');
var tester = require('tester');
var testpage = require('webpage').create();
var xunit = require('xunit').create();
xunit.addSuccess('foo', 'bar');
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="foo" name="bar"/,
'XUnitExporter.addSuccess() adds a successful testcase');
xunit.addFailure('bar', 'baz', 'wrong', 'chucknorriz');
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="bar" name="baz"><failure type="chucknorriz">wrong/,
'XUnitExporter.addFailure() adds a failed testcase');
casper.test.begin('XUnitReporter() initialization', function suite() {
var xunit = require('xunit').create();
var results = new tester.TestSuite();
xunit.setResults(results);
this.assertTruthy(xunit.getXML());
this.done(1);
});
// named classname
xunit = require('xunit').create();
xunit.addSuccess(require('fs').workingDirectory + '/plop.js', 'It worked');
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="(.*)plop" name="It worked"/,
'XUnitExporter.addSuccess() handles class name');
xunit.addSuccess(require('fs').workingDirectory + '/plip.js', 'Failure');
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="(.*)plip" name="Failure"/,
'XUnitExporter.addFailure() handles class name');
casper.test.begin('XUnitReporter() can hold test suites', function suite() {
var xunit = require('xunit').create();
var results = new tester.TestSuite();
var suite1 = new tester.TestSuiteResult({
name: 'foo',
file: '/foo'
});
results.push(suite1);
var suite2 = new tester.TestSuiteResult({
name: 'bar',
file: '/bar'
});
results.push(suite2);
xunit.setResults(results);
casper.start().setContent(xunit.getXML());
this.assertEvalEquals(function() {
return __utils__.findAll('testsuite').length;
}, 2);
this.assertExists('testsuites[duration]');
this.assertExists('testsuite[name="foo"][package="foo"]');
this.assertExists('testsuite[name="bar"][package="bar"]');
this.done(4);
});
// named with time
xunit = require('xunit').create();
xunit.addSuccess('foo', 'It worked', 1024);
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="foo" name="It worked" time="(.*)"/,
'XUnitExporter.addSuccess() writes duration of test');
xunit.addFailure('bar', 'baz', 'wrong', 'chucknorriz', 1024);
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="bar" name="baz" time="(.*)"><failure type="chucknorriz">wrong/,
'XUnitExporter.addFailure() adds a failed testcase with duration');
casper.test.begin('XUnitReporter() can hold a suite with a succesful test', function suite() {
var xunit = require('xunit').create();
var results = new tester.TestSuite();
var suite1 = new tester.TestSuiteResult({
name: 'foo',
file: '/foo'
});
suite1.addSuccess({
success: true,
type: "footype",
message: "footext",
file: "/foo"
});
results.push(suite1);
xunit.setResults(results);
casper.start().setContent(xunit.getXML());
this.assertExists('testsuite[name="foo"][package="foo"][tests="1"][failures="0"] testcase[name="footext"]');
casper.test.done(1);
});
// named with time
xunit = require('xunit').create();
casper.test.assertMatch(xunit.getXML(),
/<testsuite>/,
'XUnitExporter.create() created <testsuite> without time');
xunit.setSuiteDuration(1024);
casper.test.assertMatch(xunit.getXML(),
/<testsuite time="1.024">/,
'XUnitExporter.setSuiteDuration() sets time in seconds');
casper.test.begin('XUnitReporter() can handle a failed test', function suite() {
var xunit = require('xunit').create();
var results = new tester.TestSuite();
var suite1 = new tester.TestSuiteResult({
name: 'foo',
file: '/foo'
});
suite1.addFailure({
success: false,
type: "footype",
message: "footext",
file: "/foo"
});
results.push(suite1);
xunit.setResults(results);
casper.start().setContent(xunit.getXML());
this.assertExists('testsuite[name="foo"][package="foo"][tests="1"][failures="1"] testcase[name="footext"] failure[type="footype"]');
casper.test.done(1);
});
casper.test.done(8);
......