Compare commits

...

40 Commits

Author SHA1 Message Date
Gregg Van Hove
8fca3b4c11 Fix links in 3.4 release notes 2019-04-03 17:13:18 -07:00
Gregg Van Hove
e636f5f822 Bump version to 3.4 2019-04-03 17:03:07 -07:00
Gregg Van Hove
74fd0e08e7 No fit in the suite 2019-04-01 21:01:22 -07:00
Gregg Van Hove
618e24b2f8 Handle WebSocket events in IE when detecting Errors
- Fixes #1623
2019-04-01 18:52:27 -07:00
Gregg Van Hove
5c7e25e228 Allow excluded specs in CI without breaking the output 2019-04-01 18:52:01 -07:00
Gregg Van Hove
54af109d40 Merge branch 'wood1986-fix/npm-audit-dependencies-and-fast-glob-only-failed-tests'
- Merges #1672 from @wood1986
2019-03-26 17:12:53 -07:00
wood
873a237e3d Consolidate some dev dependencies and use more maintained versions 2019-03-26 17:12:21 -07:00
Gregg Van Hove
8ca4463e01 Make node execution default and override for browsers 2019-03-25 18:37:50 -07:00
Gregg Van Hove
449eb516cc Fix sauce status codes and try travis built-in node support 2019-03-25 18:33:39 -07:00
Gregg Van Hove
a5df5a6ee9 Use the correct env var from travis for tunnels 2019-03-25 18:12:15 -07:00
Gregg Van Hove
be583232b4 bump dependencies for security fixes 2019-03-25 18:01:13 -07:00
Gregg Van Hove
d389d3c002 Merge branch 'wood1986-features/no-ruby'
- Merges #1658 from @wood1986
- Fixes #883
2019-03-25 17:56:19 -07:00
Gregg Van Hove
994d11d4f3 update node CI script to work with SauceLabs 2019-03-25 17:56:06 -07:00
wood
828d14f48e Use node.js for browser-based CI 2019-03-25 17:55:53 -07:00
Gregg Van Hove
f5663a9076 Merge branch 'johnjbarton-duration'
- Merges #1660 from @johnjbarton
- Fixes #1646
2019-03-14 19:12:39 -07:00
johnjbarton
a8c2399dd8 feat(result.duration): report test duration in ms
Wrap spec start/complete in Timer start/elapsed.
configuration.timeSpecDuration = false will disable feature.

 * Add Suite result.duration, elapsed time in ms

 * Remove timeSpecDuration option.

 * Respond to review, use noopTimer
2019-03-14 09:13:57 -07:00
Gregg Van Hove
7c0f013003 Merge branch 'johnjbarton-noop-timer'
- Merges #1669 from @johnjbarton
2019-03-13 17:34:12 -07:00
johnjbarton
ca2b62b00e refactor(Timer): share htmlReporter noopTimer via Timer.js 2019-03-12 16:26:04 -07:00
Gregg Van Hove
4108deca02 Build distribution for various typo PRs 2019-03-11 17:39:03 -07:00
Gregg Van Hove
def278f90f Merge branch 'typo-suites' of https://github.com/FelixRilling/jasmine into FelixRilling-typo-suites
- Merges #1666 from @FelixRilling
2019-03-11 17:38:29 -07:00
Gregg Van Hove
6bd4a29360 Merge branch 'jsdoc-validation' of https://github.com/FelixRilling/jasmine into FelixRilling-jsdoc-validation
- Merges #1667 from @FelixRilling
2019-03-11 17:37:43 -07:00
Gregg Van Hove
fd037f53a3 Merge branch 'typo-comments' of https://github.com/FelixRilling/jasmine into FelixRilling-typo-comments
- Merges #1665 from @FelixRilling
2019-03-11 17:34:44 -07:00
Gregg Van Hove
348242b712 Merge branch 'FelixRilling-typo-received'
- Merges #1664 from @FelixRilling
- Fixes #1663
2019-03-11 17:32:32 -07:00
Felix Rilling
b74e0abee1 Fixed flipped JSDoc. 2019-03-10 11:17:50 +01:00
Felix Rilling
e33b12b17c Fixed typos in test suite descriptions. 2019-03-10 11:07:40 +01:00
Felix Rilling
dde93ade18 Fixed typos in comments. 2019-03-10 11:04:33 +01:00
Felix Rilling
63f900287c Fixed typo "receieved" to "received", Adapted test. 2019-03-10 10:59:56 +01:00
Gregg Van Hove
239a615770 No longer run Node.js v4 on Travis
- Jasmine should still work in Node.js v4, but the jshint task isn't
  working correctly
2019-02-11 13:38:00 -08:00
Gregg Van Hove
92d5957a59 Attempt to skip jshint for node.js v4 2019-02-11 09:46:36 -08:00
Gregg Van Hove
4991f2a713 Merge branch 'print_global_error_type' of https://github.com/jbunton-atlassian/jasmine into jbunton-atlassian-print_global_error_type
- Merges #1632 from @jbunton-atlassian
2019-02-11 09:02:16 -08:00
Gregg Van Hove
0d6db64eb1 Merge branch 'onerror' of https://github.com/johnjbarton/jasmine into johnjbarton-onerror
- Merges #1644 from @jognjbarton
2019-01-30 17:38:27 -08:00
Gregg Van Hove
489fb79d6e fall back to older jshint for older node 2019-01-09 11:08:32 -08:00
Gregg Van Hove
eba8c775f3 update npm dependencies 2019-01-08 17:47:53 -08:00
johnjbarton
c36a005893 Support Error.stack in globalErrors.
See https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror#window.onerror
2019-01-03 16:14:23 -08:00
Gregg Van Hove
37dfe50d99 Stop treating objects with a nodeType as if they are DOM Nodes
- Fixes #1638
2018-12-17 17:10:53 -08:00
Gregg Van Hove
c67a5b830c Fix 3.0 breaking changes list to include note about Spy identity 2018-12-11 17:56:17 -08:00
James Bunton
d803bd12a6 When catching a global error in Node.js, print the type of error 2018-12-11 08:32:45 +11:00
Gregg Van Hove
a621d05fa7 Merge branch 'Havunen-fix-to-equal-ie'
- Merges #1621 from @Havunen
- Fixes #1618
2018-12-03 17:45:06 -08:00
Sampo Kivistö
473f5cd359 Re-use attr variable 2018-11-04 10:44:54 +02:00
Sampo Kivistö
a81e9626df Fixes issue where PhantomJS 2 and IE 10 - 11 crashed when reporting SVG element equality 2018-11-04 10:31:33 +02:00
47 changed files with 822 additions and 219 deletions

View File

@@ -1,19 +1,16 @@
language: ruby
cache: bundler
sudo: false
language: node_js
node_js:
- "11"
- "10"
- "8"
rvm: 2.5
before_install:
- gem update --system
- gem install bundler
script: $TEST_COMMAND
env:
global:
- USE_SAUCE=true
- USE_SAUCE=false
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true
- TEST_COMMAND="bash travis-core-script.sh"
- TEST_COMMAND="npm test"
- secure: WSPWhlnC4mWSnSPquX+m1/BCu5ch5NygkaHuM2Nea7lD8oS3XLX8QncZZAsQ4lnNfqoDDuBOizG0AESiqNvE4y6x5qvLLTS6q+ce255ZEMZ71TBdZgDEEvGMEjOPPsVXiXyTQOP1lwOPlrbZvaPgWV7e11KIBab6DfFcQpnvDgo=
- secure: SW7CJhZnwaNT749Gdnhvqb5rbXlAOsygUAzh9qhtyvbqXKkmJdBIEsO01YF6pbju1X2twE9JvWCOxeZju43NgQChJlPsGbjY2j3k/TdQeTAJesQe2K7ytwghunI30gjEovtRH0T3w1EmcKPH8yj5eBIcB2OYoJHx8KEC7e68q1g=
@@ -23,46 +20,55 @@ addons:
matrix:
include:
- env:
- USE_SAUCE=false
- TEST_COMMAND="bash travis-node-script.sh v4"
- env:
- USE_SAUCE=false
- TEST_COMMAND="bash travis-node-script.sh v8"
- env:
- USE_SAUCE=false
- TEST_COMMAND="bash travis-node-script.sh v9"
- env:
- USE_SAUCE=true
- TEST_COMMAND="bash travis-core-script.sh"
- JASMINE_BROWSER="firefox"
- SAUCE_OS="Linux"
- SAUCE_BROWSER_VERSION=''
- env:
- USE_SAUCE=true
- TEST_COMMAND="bash travis-core-script.sh"
- JASMINE_BROWSER="safari"
- SAUCE_OS="OS X 10.12"
- SAUCE_BROWSER_VERSION=10
- env:
- USE_SAUCE=true
- TEST_COMMAND="bash travis-core-script.sh"
- JASMINE_BROWSER="safari"
- SAUCE_OS="OS X 10.11"
- SAUCE_BROWSER_VERSION=9
- env:
- USE_SAUCE=true
- TEST_COMMAND="bash travis-core-script.sh"
- JASMINE_BROWSER="safari"
- SAUCE_OS="OS X 10.10"
- SAUCE_BROWSER_VERSION=8
- env:
- USE_SAUCE=true
- TEST_COMMAND="bash travis-core-script.sh"
- JASMINE_BROWSER="MicrosoftEdge"
- SAUCE_OS="Windows 10"
- SAUCE_BROWSER_VERSION="15"
- env:
- USE_SAUCE=true
- TEST_COMMAND="bash travis-core-script.sh"
- JASMINE_BROWSER="internet explorer"
- SAUCE_OS="Windows 8.1"
- SAUCE_BROWSER_VERSION=11
- env:
- USE_SAUCE=true
- TEST_COMMAND="bash travis-core-script.sh"
- JASMINE_BROWSER="internet explorer"
- SAUCE_OS="Windows 8"
- SAUCE_BROWSER_VERSION=10
- env:
- USE_SAUCE=true
- TEST_COMMAND="bash travis-core-script.sh"
- JASMINE_BROWSER="chrome"
- SAUCE_OS="Linux"
- SAUCE_BROWSER_VERSION=''
- env:
- USE_SAUCE=false
- TEST_COMMAND="bash travis-core-script.sh"
- JASMINE_BROWSER="phantomjs"
- USE_SAUCE=false

View File

@@ -6,15 +6,16 @@ module.exports = function(grunt) {
pkg: pkg,
jshint: require('./grunt/config/jshint.js'),
concat: require('./grunt/config/concat.js'),
compass: require('./grunt/config/compass.js'),
compress: require('./grunt/config/compress.js')
sass: require('./grunt/config/sass.js'),
compress: require('./grunt/config/compress.js'),
cssUrlEmbed: require('./grunt/config/cssUrlEmbed.js')
});
require('load-grunt-tasks')(grunt);
grunt.loadTasks('grunt/tasks');
grunt.registerTask('default', ['jshint:all']);
grunt.registerTask('default', ['jshint:all', 'sass:dist', "cssUrlEmbed"]);
var version = require('./grunt/tasks/version.js');
@@ -25,11 +26,11 @@ module.exports = function(grunt) {
grunt.registerTask('buildDistribution',
'Builds and lints jasmine.js, jasmine-html.js, jasmine.css',
[
'compass',
'sass:dist',
"cssUrlEmbed",
'jshint:beforeConcat',
'concat',
'jshint:afterConcat',
'build:copyVersionToGem'
'jshint:afterConcat'
]
);

208
ci.js Normal file
View File

@@ -0,0 +1,208 @@
const path = require("path"),
fs = require('fs'),
port = 5555,
colors = {
"passed" : "\x1B[32m",
"failed": "\x1B[31m",
"pending": "\x1B[33m",
"excluded": "\x1B[0m",
"none": "\x1B[0m"
},
symbols = {
"passed" : ".",
"failed": "F",
"pending": "*",
"excluded": "",
"none": ""
},
host = `http://localhost:${port}`,
useSauce = process.env.USE_SAUCE === 'true';
let driver, server;
function pageGenerator() {
const ejs = require("ejs"),
fg = require("fast-glob"),
templatePath = path.resolve(__dirname, 'spec/support/index.html.ejs'),
template = ejs.compile(fs.readFileSync(templatePath).toString()),
patterns = [
"lib/jasmine-core/jasmine.js",
"lib/jasmine-core/json2.js",
"lib/jasmine-core/jasmine-html.js",
"lib/jasmine-core/boot.js",
"src/core/requireCore.js",
"src/core/base.js",
"src/core/util.js",
"src/core/Spec.js",
"src/core/Env.js",
"src/**/*.js",
"spec/helpers/*.js",
"spec/**/*[Ss]pec.js"
],
ignore = [
"spec/helpers/nodeDefineJasmineUnderTest.js",
"spec/npmPackage/**/*",
"lib/jasmine-core/node_boot.js"
];
return function toHtml() {
const files = fg.sync(patterns, {ignore});
return template({files});
}
}
function buildWebdriver() {
const webdriver = require("selenium-webdriver"),
Capability = webdriver.Capability;
if (useSauce) {
const username = process.env['SAUCE_USERNAME'],
accessKey = process.env['SAUCE_ACCESS_KEY'];
return new webdriver.Builder()
.withCapabilities({
name: `jasmine-core ${new Date().toISOString()}`,
[Capability.PLATFORM]: process.env['SAUCE_OS'],
[Capability.BROWSER_NAME]: process.env['JASMINE_BROWSER'],
[Capability.VERSION]: process.env['SAUCE_BROWSER_VERSION'],
build: `Core ${process.env['TRAVIS_BUILD_NUMBER'] || 'Ran locally'}`,
tags: ['Jasmine-Core'],
"tunnel-identifier": process.env['TRAVIS_JOB_NUMBER'] ? process.env['TRAVIS_JOB_NUMBER'].toString() : null
})
.usingServer(`http://${username}:${accessKey}@localhost:4445/wd/hub`)
.build();
} else {
return new webdriver.Builder()
.forBrowser(process.env["JASMINE_BROWSER"] || "firefox")
.build();
}
}
async function resultsWithoutCircularReferences(driver, resultType, index, batchSize) {
return await driver.executeScript(
`var results = jsApiReporter.${resultType}Results(${index}, ${batchSize});\n` +
'for (var i = 0; i < results.length; i++) {\n' +
'var expectations = results[i].failedExpectations;\n' +
'if (results[i].passedExpectations) {\n' +
'expectations = expectations.concat(results[i].passedExpectations);\n' +
'}\n' +
'for (var j = 0; j < expectations.length; j++) {\n' +
'var expectation = expectations[j];\n' +
"try { JSON.stringify(expectation.expected); } catch (e) { expectation.expected = '<circular expected>'; }\n" +
"try { JSON.stringify(expectation.actual); } catch (e) { expectation.actual = '<circular actual>'; }\n" +
'}\n' +
'}\n' +
'return results;'
);
}
function flatten(arr) {
return Array.prototype.concat.apply([], arr);
}
async function getResults(driver) {
const batchSize = 50,
specResults = [],
failedSuiteResults = [];
let index = 0,
slice = [];
do {
slice = await resultsWithoutCircularReferences(driver, 'spec', index, batchSize);
specResults.push(slice);
index += batchSize;
} while (slice.length === batchSize);
index = 0;
do {
slice = await resultsWithoutCircularReferences(driver, 'suite', index, batchSize);
failedSuiteResults.push(slice.filter(function(suite) { return suite.status === 'failed' }));
index += batchSize;
} while (slice.length === batchSize);
return {specResults: flatten(specResults), failedSuiteResults: flatten(failedSuiteResults)};
}
(async function () {
await new Promise(resolve => {
console.log("Creating an express app for browers to run the tests...")
const express = require("express"),
app = express(),
html = pageGenerator();
app.use(express.static(__dirname));
app.get("/", (req, res) => res.send(html()));
server = app.listen(port, resolve);
});
console.log("Running the tests in browser...")
driver = buildWebdriver();
await driver.get(`${host}/?throwFailures=false&failFast=false&random=true`)
await new Promise(resolve => {
const intervalId = setInterval(async () => {
const isFinished = await driver.executeScript("return jsApiReporter && jsApiReporter.finished")
if (isFinished) {
clearInterval(intervalId)
resolve();
}
}, 500)
});
const {specResults, failedSuiteResults} = await getResults(driver);
console.log(specResults.map(spec => `${colors[spec.status]}${symbols[spec.status]}`).join("") + colors["none"]);
const result = specResults.reduce((result, spec) => {
result[spec.status] = [...result[spec.status], spec];
return result;
}, {pending: [], failed: [], passed: [], excluded: []});
if (result.pending.length) {
console.log(`${colors["pending"]}Pending:`);
result.pending.forEach((spec, index) => {
console.log(`${colors["none"]}${index}) ${spec.fullName}`)
console.group();
console.log(`${colors["pending"]}${spec.pendingReason || "no reason given"}`);
console.groupEnd();
console.log();
});
}
if (result.failed.length) {
console.log(`${colors["failed"]}Failed:`);
result["failed"].forEach((spec, index) => {
console.log(`${colors["none"]}${index}) ${spec.fullName}`)
console.group();
spec.failedExpectations.forEach((expect) => {
console.log(`${colors["none"]}Message:`);
console.group();
console.log(`${colors["failed"]}${expect.message}`);
console.groupEnd();
console.log(`${colors["none"]}Stack:`);
console.group();
console.log(`${colors["failed"]}${expect.stack}`);
console.groupEnd();
console.groupEnd();
})
console.groupEnd();
console.log();
});
}
const details = await driver.executeScript(`
return {
overallStatus: jsApiReporter.runDetails.overallStatus,
executionTime: jsApiReporter.executionTime(),
random: jsApiReporter.runDetails.order.random,
seed: jsApiReporter.runDetails.order.seed
}`);
console.log(`${colors["none"]}${specResults.length} spec(s), ${result.failed.length} failure(s), ${result.pending.length} pending spec(s)`);
console.log(`Finished in ${details.executionTime / 1000} second(s)`);
console.log(`Randomized with seed ${details.seed} ( ${host}/?random=${details.random}&seed=${details.seed} )`);
process.exitCode = details.overallStatus === 'passed' ? 0 : 1;
if (useSauce) {
driver.executeScript(`sauce:job-result=${process.exitCode === 0}`);
}
})().finally(() => {
return Promise.all([driver.close(), new Promise(resolve => server.close(resolve))]);
});

View File

@@ -1,11 +0,0 @@
module.exports = {
jasmine: {
options: {
cssDir: 'lib/jasmine-core/',
sassDir: 'src/html',
outputStyle: 'compact',
noLineComments: true,
bundleExec: true
}
}
};

View File

@@ -0,0 +1,7 @@
module.exports = {
encodeWithBaseDir: {
files: {
"lib/jasmine-core/jasmine.css": ["lib/jasmine-core/jasmine.css"]
}
}
};

14
grunt/config/sass.js Normal file
View File

@@ -0,0 +1,14 @@
const sass = require('node-sass');
module.exports = {
options: {
implementation: sass,
outputStyle: 'compact',
sourceComments: false
},
dist: {
files: {
"lib/jasmine-core/jasmine.css": "src/html/jasmine.scss"
}
}
};

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2018 Pivotal Labs
Copyright (c) 2008-2019 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2018 Pivotal Labs
Copyright (c) 2008-2019 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -28,12 +28,6 @@ jasmineRequire.html = function(j$) {
};
jasmineRequire.HtmlReporter = function(j$) {
var noopTimer = {
start: function() {},
elapsed: function() { return 0; }
};
function ResultsStateBuilder() {
this.topResults = new j$.ResultsNode({}, '', null);
this.currentParent = this.topResults;
@@ -87,7 +81,7 @@ jasmineRequire.HtmlReporter = function(j$) {
navigateWithNewParam = options.navigateWithNewParam || function() {},
addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
filterSpecs = options.filterSpecs,
timer = options.timer || noopTimer,
timer = options.timer || j$.noopTimer,
htmlReporterMain,
symbols,
deprecationWarnings = [];

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2018 Pivotal Labs
Copyright (c) 2008-2019 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -63,7 +63,8 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
j$.Expector = jRequire.Expector(j$);
j$.Expectation = jRequire.Expectation(j$);
j$.buildExpectationResult = jRequire.buildExpectationResult();
j$.JsApiReporter = jRequire.JsApiReporter();
j$.noopTimer = jRequire.noopTimer();
j$.JsApiReporter = jRequire.JsApiReporter(j$);
j$.matchersUtil = jRequire.matchersUtil(j$);
j$.ObjectContaining = jRequire.ObjectContaining(j$);
j$.ArrayContaining = jRequire.ArrayContaining(j$);
@@ -160,7 +161,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
*/
j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50;
/**
* Maximum number of charasters to display when pretty printing objects.
* Maximum number of characters to display when pretty printing objects.
* Characters past this number will be ellipised.
* @name jasmine.MAX_PRETTY_PRINT_CHARS
*/
@@ -232,9 +233,15 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
if (value instanceof Error) {
return true;
}
if (value && value.constructor && value.constructor.constructor &&
(value instanceof (value.constructor.constructor('return this')()).Error)) {
return true;
if (value && value.constructor && value.constructor.constructor) {
var valueGlobal = value.constructor.constructor('return this');
if (j$.isFunction_(valueGlobal)) {
valueGlobal = valueGlobal();
}
if (valueGlobal.Error && value instanceof valueGlobal.Error) {
return true;
}
}
return false;
};
@@ -556,6 +563,7 @@ getJasmineRequireObj().Spec = function(j$) {
this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
this.timer = attrs.timer || j$.noopTimer;
if (!this.queueableFn.fn) {
this.pend();
@@ -571,6 +579,7 @@ getJasmineRequireObj().Spec = function(j$) {
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
*/
this.result = {
id: this.id,
@@ -579,7 +588,8 @@ getJasmineRequireObj().Spec = function(j$) {
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
pendingReason: ''
pendingReason: '',
duration: null,
};
}
@@ -609,6 +619,7 @@ getJasmineRequireObj().Spec = function(j$) {
var onStart = {
fn: function(done) {
self.timer.start();
self.onStart(self, done);
}
};
@@ -632,6 +643,7 @@ getJasmineRequireObj().Spec = function(j$) {
self.onException.apply(self, arguments);
},
onComplete: function() {
self.result.duration = self.timer.elapsed();
onComplete(self.result.status === 'failed' && new j$.StopExecutionError('spec failed'));
},
userContext: this.userContext()
@@ -753,7 +765,7 @@ getJasmineRequireObj().Order = function() {
}
// Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function
// used to get a different output when the key changes slighly.
// used to get a different output when the key changes slightly.
// We use your return to sort the children randomly in a consistent way when
// used in conjunction with a seed
@@ -877,11 +889,12 @@ getJasmineRequireObj().Env = function(j$) {
if (!options.suppressLoadErrors) {
installGlobalErrors();
globalErrors.pushListener(function(message, filename, lineno) {
globalErrors.pushListener(function(message, filename, lineno, colNo, err) {
topSuite.result.failedExpectations.push({
passed: false,
globalErrorType: 'load',
message: message,
stack: err && err.stack,
filename: filename,
lineno: lineno
});
@@ -1288,6 +1301,7 @@ getJasmineRequireObj().Env = function(j$) {
currentlyExecutingSuites.push(suite);
defaultResourcesForRunnable(suite.id, suite.parentSuite.id);
reporter.suiteStarted(suite.result, next);
suite.startTimer();
},
nodeComplete: function(suite, result, next) {
if (suite !== currentSuite()) {
@@ -1300,7 +1314,7 @@ getJasmineRequireObj().Env = function(j$) {
if (result.status === 'failed') {
hasFailures = true;
}
suite.endTimer();
reporter.suiteDone(result, next);
},
orderChildren: function(node) {
@@ -1347,7 +1361,7 @@ getJasmineRequireObj().Env = function(j$) {
/**
* Information passed to the {@link Reporter#jasmineDone} event.
* @typedef JasmineDoneInfo
* @property {OverallStatus} overallStatus - The overall result of the sute: 'passed', 'failed', or 'incomplete'.
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
@@ -1580,9 +1594,9 @@ getJasmineRequireObj().Env = function(j$) {
fn: fn,
timeout: timeout || 0
},
throwOnExpectationFailure: config.oneFailurePerSpec
throwOnExpectationFailure: config.oneFailurePerSpec,
timer: new j$.Timer(),
});
return spec;
function specResultCallback(result, next) {
@@ -1737,13 +1751,7 @@ getJasmineRequireObj().Env = function(j$) {
return Env;
};
getJasmineRequireObj().JsApiReporter = function() {
var noopTimer = {
start: function(){},
elapsed: function(){ return 0; }
};
getJasmineRequireObj().JsApiReporter = function(j$) {
/**
* @name jsApiReporter
* @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object.
@@ -1751,7 +1759,7 @@ getJasmineRequireObj().JsApiReporter = function() {
* @hideconstructor
*/
function JsApiReporter(options) {
var timer = options.timer || noopTimer,
var timer = options.timer || j$.noopTimer,
status = 'loaded';
this.started = false;
@@ -2400,7 +2408,7 @@ getJasmineRequireObj().Clock = function() {
* The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes.
* @name Clock#withMock
* @function
* @param {closure} Function The function to be called.
* @param {Function} closure The function to be called.
*/
self.withMock = function(closure) {
this.install();
@@ -2697,12 +2705,16 @@ getJasmineRequireObj().errors = function() {
};
getJasmineRequireObj().ExceptionFormatter = function(j$) {
var ignoredProperties = ['name', 'message', 'stack', 'fileName', 'sourceURL', 'line', 'lineNumber', 'column', 'description', 'jasmineMessage'];
function ExceptionFormatter(options) {
var jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile();
this.message = function(error) {
var message = '';
if (error.name && error.message) {
if (error.jasmineMessage) {
message += error.jasmineMessage;
} else if (error.name && error.message) {
message += error.name + ': ' + error.message;
} else if (error.message) {
message += error.message;
@@ -2760,12 +2772,11 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
return;
}
var ignored = ['name', 'message', 'stack', 'fileName', 'sourceURL', 'line', 'lineNumber', 'column', 'description'];
var result = {};
var empty = true;
for (var prop in error) {
if (j$.util.arrayContains(ignored, prop)) {
if (j$.util.arrayContains(ignoredProperties, prop)) {
continue;
}
result[prop] = error[prop];
@@ -3192,28 +3203,44 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
};
this.originalHandlers = {};
this.installOne_ = function installOne_(errorType) {
this.jasmineHandlers = {};
this.installOne_ = function installOne_(errorType, jasmineMessage) {
function taggedOnError(error) {
error.jasmineMessage = jasmineMessage + ': ' + error;
var handler = handlers[handlers.length - 1];
if (handler) {
handler(error);
} else {
throw error;
}
}
this.originalHandlers[errorType] = global.process.listeners(errorType);
this.jasmineHandlers[errorType] = taggedOnError;
global.process.removeAllListeners(errorType);
global.process.on(errorType, onerror);
global.process.on(errorType, taggedOnError);
this.uninstall = function uninstall() {
var errorTypes = Object.keys(this.originalHandlers);
for (var iType = 0; iType < errorTypes.length; iType++) {
var errorType = errorTypes[iType];
global.process.removeListener(errorType, onerror);
global.process.removeListener(errorType, this.jasmineHandlers[errorType]);
for (var i = 0; i < this.originalHandlers[errorType].length; i++) {
global.process.on(errorType, this.originalHandlers[errorType][i]);
}
delete this.originalHandlers[errorType];
delete this.jasmineHandlers[errorType];
}
};
};
this.install = function install() {
if (global.process && global.process.listeners && j$.isFunction_(global.process.on)) {
this.installOne_('uncaughtException');
this.installOne_('unhandledRejection');
this.installOne_('uncaughtException', 'Uncaught exception');
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
} else {
var originalHandler = global.onerror;
global.onerror = onerror;
@@ -5055,10 +5082,12 @@ getJasmineRequireObj().pp = function(j$) {
this.emitScalar(value.toString());
} else if (typeof value === 'function') {
this.emitScalar('Function');
} else if (value.nodeType === 1) {
this.emitDomElement(value);
} else if (typeof value.nodeType === 'number') {
this.emitScalar('HTMLNode');
} else if (j$.isDomNode(value)) {
if (value.tagName) {
this.emitDomElement(value);
} else {
this.emitScalar('HTMLNode');
}
} else if (value instanceof Date) {
this.emitScalar('Date(' + value + ')');
} else if (j$.isSet(value)) {
@@ -5249,15 +5278,29 @@ getJasmineRequireObj().pp = function(j$) {
};
PrettyPrinter.prototype.emitDomElement = function(el) {
var closingTag = '</' + el.tagName.toLowerCase() + '>';
var tagName = el.tagName.toLowerCase(),
attrs = el.attributes,
i,
len = attrs.length,
out = '<' + tagName,
attr;
if (el.innerHTML === '') {
this.append(el.outerHTML.replace(closingTag, ''));
} else {
var tagEnd = el.outerHTML.indexOf('>');
this.append(el.outerHTML.substring(0, tagEnd + 1));
this.append('...' + closingTag);
for (i = 0; i < len; i++) {
attr = attrs[i];
out += ' ' + attr.name;
if (attr.value !== '') {
out += '="' + attr.value + '"';
}
}
out += '>';
if (el.childElementCount !== 0 || el.textContent !== '') {
out += '...</' + tagName + '>';
}
this.append(out);
};
PrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) {
@@ -5413,7 +5456,7 @@ getJasmineRequireObj().QueueRunner = function(j$) {
cleanup();
if (j$.isError_(err)) {
if (!(err instanceof StopExecutionError)) {
if (!(err instanceof StopExecutionError) && !err.jasmineMessage) {
self.fail(err);
}
self.errored = errored = true;
@@ -6047,7 +6090,7 @@ getJasmineRequireObj().Spy = function (j$) {
if (!strategy) {
if (argsStrategies.any() && !baseStrategy.isConfigured()) {
throw new Error('Spy \'' + strategyArgs.name + '\' receieved a call with arguments ' + j$.pp(Array.prototype.slice.call(args)) + ' but all configured strategies specify other arguments.');
throw new Error('Spy \'' + strategyArgs.name + '\' received a call with arguments ' + j$.pp(Array.prototype.slice.call(args)) + ' but all configured strategies specify other arguments.');
} else {
strategy = baseStrategy;
}
@@ -6551,6 +6594,8 @@ getJasmineRequireObj().Suite = function(j$) {
this.beforeAllFns = [];
this.afterAllFns = [];
this.timer = attrs.timer || j$.noopTimer;
this.children = [];
/**
@@ -6561,13 +6606,15 @@ getJasmineRequireObj().Suite = function(j$) {
* @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
* @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach.
*/
this.result = {
id: this.id,
description: this.description,
fullName: this.getFullName(),
failedExpectations: [],
deprecationWarnings: []
deprecationWarnings: [],
duration: null,
};
}
@@ -6609,6 +6656,14 @@ getJasmineRequireObj().Suite = function(j$) {
this.afterAllFns.unshift(fn);
};
Suite.prototype.startTimer = function() {
this.timer.start();
};
Suite.prototype.endTimer = function() {
this.result.duration = this.timer.elapsed();
};
function removeFns(queueableFns) {
for(var i = 0; i < queueableFns.length; i++) {
queueableFns[i].fn = null;
@@ -6732,6 +6787,12 @@ getJasmineRequireObj().Timer = function() {
return Timer;
};
getJasmineRequireObj().noopTimer = function() {
return {
start: function() {},
elapsed: function() { return 0; }
};
};
getJasmineRequireObj().TreeProcessor = function() {
function TreeProcessor(attrs) {
var tree = attrs.tree,
@@ -6968,5 +7029,5 @@ getJasmineRequireObj().UserContext = function(j$) {
};
getJasmineRequireObj().version = function() {
return '3.3.0';
return '3.4.0';
};

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2018 Pivotal Labs
Copyright (c) 2008-2019 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -4,6 +4,6 @@
#
module Jasmine
module Core
VERSION = "3.3.0"
VERSION = "3.4.0"
end
end

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "3.3.0",
"version": "3.4.0",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"
@@ -19,17 +19,22 @@
"homepage": "https://jasmine.github.io",
"main": "./lib/jasmine-core.js",
"devDependencies": {
"glob": "~7.1.2",
"grunt": "^1.0.1",
"grunt-cli": "^1.2.0",
"grunt-contrib-compass": "^1.1.1",
"ejs": "^2.5.5",
"express": "^4.16.4",
"fast-glob": "^2.2.6",
"grunt": "^1.0.4",
"grunt-cli": "^1.3.2",
"grunt-contrib-compress": "^1.3.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-jshint": "^1.0.0",
"jasmine": "^3.0.0",
"jsdom": "^9.12.0",
"load-grunt-tasks": "^0.4.0",
"shelljs": "^0.7.0",
"temp": "~0.8.1"
"grunt-contrib-jshint": "^2.0.0",
"grunt-css-url-embed": "^1.11.1",
"grunt-sass": "^3.0.2",
"jasmine": "^3.3.1",
"jsdom": "^13.1.0",
"load-grunt-tasks": "^4.0.0",
"node-sass": "^4.11.0",
"selenium-webdriver": "^3.6.0",
"shelljs": "^0.8.3",
"temp": "^0.9.0"
}
}

View File

@@ -29,6 +29,8 @@ There is also a 2.99 release of Jasmine that will present deprecation warnings f
* Default to running tests in random order
* The `identity` of a Jasmnine Spy is now a property and no longer a method
* Additionally, Jasmine 3.0 drops support for older browsers and environments. Notably:
- Internet Explorer 8 and 9
- Ruby 1.x (for the Ruby gem)

49
release_notes/3.4.0.md Normal file
View File

@@ -0,0 +1,49 @@
# Jasmine Core 3.4 Release Notes
## Summary
This is a maintenance release of Jasmine with a number of new features and fixes
## Changes
* Handle WebSocket events in IE when detecting Errors
- Fixes [#1623](https://github.com/jasmine/jasmine/issues/1623)
* bump dependencies for security fixes
- Merges [#1672](https://github.com/jasmine/jasmine/issues/1672) from @wood1986
* Make node execution default and override for browsers
- Merges [#1658](https://github.com/jasmine/jasmine/issues/1658) from @wood1986
- Fixes [#883](https://github.com/jasmine/jasmine/issues/883)
* feat(result.duration): report test duration in ms
- Merges [#1660](https://github.com/jasmine/jasmine/issues/1660) from @johnjbarton
- Fixes [#1646](https://github.com/jasmine/jasmine/issues/1646)
* refactor(Timer): share htmlReporter noopTimer via Timer.js
- Merges [#1669](https://github.com/jasmine/jasmine/issues/1669) from @johnjbarton
* Fix various typos
- Merges [#1666](https://github.com/jasmine/jasmine/issues/1666) from @FelixRilling
- Merges [#1667](https://github.com/jasmine/jasmine/issues/1667) from @FelixRilling
- Merges [#1665](https://github.com/jasmine/jasmine/issues/1665) from @FelixRilling
- Merges [#1664](https://github.com/jasmine/jasmine/issues/1664) from @FelixRilling
- Fixes [#1663](https://github.com/jasmine/jasmine/issues/1663)
* When catching a global error in Node.js, print the type of error
- Merges [#1632](https://github.com/jasmine/jasmine/issues/1632) from @jbunton-atlassian
* Support Error.stack in globalErrors.
- Merges [#1644](https://github.com/jasmine/jasmine/issues/1644) from @johnjbarton
* Stop treating objects with a `nodeType` as if they are DOM Nodes
- Fixes [#1638](https://github.com/jasmine/jasmine/issues/1638)
* Fixes issue where PhantomJS 2 and IE 10 - 11 crashed when reporting SVG element equality
- Merges [#1621](https://github.com/jasmine/jasmine/issues/1621) from @Havunen
- Fixes [#1618](https://github.com/jasmine/jasmine/issues/1618)
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -96,7 +96,7 @@ describe("ExceptionFormatter", function() {
);
});
it("filters Jamine stack frames from Webkit style traces", function() {
it("filters Jasmine stack frames from Webkit style traces", function() {
var error = {
stack: 'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
'fn1@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +

View File

@@ -101,6 +101,7 @@ describe("GlobalErrors", function() {
addedListener(new Error('bar'));
expect(handler).toHaveBeenCalledWith(new Error('bar'));
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe('Uncaught exception: Error: bar');
errors.uninstall();
@@ -127,10 +128,11 @@ describe("GlobalErrors", function() {
errors.pushListener(handler);
var addedListener = fakeGlobal.process.on.calls.argsFor(0)[1];
var addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
addedListener(new Error('bar'));
expect(handler).toHaveBeenCalledWith(new Error('bar'));
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe('Unhandled promise rejection: Error: bar');
errors.uninstall();

View File

@@ -209,7 +209,7 @@ describe("JsApiReporter", function() {
expect(reporter.suiteResults(1, 1)).toEqual([suiteResult2]);
});
it("returns nothing for out of bounds indicies", function() {
it("returns nothing for out of bounds indices", function() {
expect(reporter.suiteResults(0, 3)).toEqual([suiteResult1, suiteResult2]);
expect(reporter.suiteResults(2, 3)).toEqual([]);
});

View File

@@ -23,7 +23,7 @@ describe("jasmineUnderTest.pp", function () {
expect(jasmineUnderTest.pp(set)).toEqual("Set( 1, 2 )");
});
it("should truncate sets with more elments than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH", function() {
it("should truncate sets with more elements than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH", function() {
jasmine.getEnv().requireFunctioningSets();
var originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
@@ -48,7 +48,7 @@ describe("jasmineUnderTest.pp", function () {
expect(jasmineUnderTest.pp(map)).toEqual("Map( [ 1, 2 ] )");
});
it("should truncate maps with more elments than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH", function() {
it("should truncate maps with more elements than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH", function() {
jasmine.getEnv().requireFunctioningMaps();
var originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
@@ -132,6 +132,10 @@ describe("jasmineUnderTest.pp", function () {
}, bar: [1, 2, 3]})).toEqual("Object({ foo: Function, bar: [ 1, 2, 3 ] })");
});
it("should stringify objects that almost look like DOM nodes", function() {
expect(jasmineUnderTest.pp({nodeType: 1})).toEqual("Object({ nodeType: 1 })");
});
it("should truncate objects with too many keys", function () {
var originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
var long = {a: 1, b: 2, c: 3};

View File

@@ -207,7 +207,8 @@ describe("Spec", function() {
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
pendingReason: ''
pendingReason: '',
duration: null,
}, 'things');
});
@@ -242,6 +243,22 @@ describe("Spec", function() {
expect(done).toHaveBeenCalledWith(jasmine.any(jasmineUnderTest.StopExecutionError));
});
it("should report the duration of the test", function() {
var done = jasmine.createSpy('done callback'),
timer = jasmine.createSpyObj('timer', {'start': null, elapsed: 77000}),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy("spec body")},
catchExceptions: function() { return false; },
resultCallback: function() {},
queueRunnerFactory: function(attrs) {
attrs.onComplete();
},
timer: timer,
});
spec.execute(done);
expect(spec.result.duration).toBe(77000);
});
it("#status returns passing by default", function() {
var spec = new jasmineUnderTest.Spec({queueableFn: { fn: jasmine.createSpy("spec body")} });
expect(spec.status()).toBe('passed');

View File

@@ -183,7 +183,7 @@ describe('Spies', function () {
var spy = env.createSpy('foo');
spy.withArgs('bar').and.returnValue(-1);
expect(function() { spy('baz', {qux: 42}); }).toThrowError('Spy \'foo\' receieved a call with arguments [ \'baz\', Object({ qux: 42 }) ] but all configured strategies specify other arguments.');
expect(function() { spy('baz', {qux: 42}); }).toThrowError('Spy \'foo\' received a call with arguments [ \'baz\', Object({ qux: 42 }) ] but all configured strategies specify other arguments.');
});
});
});

View File

@@ -111,6 +111,19 @@ describe("Suite", function() {
expect(suite.getResult().failedExpectations).toEqual([]);
});
it("calls timer to compute duration", function(){
var env = new jasmineUnderTest.Env(),
suite = new jasmineUnderTest.Suite({
env: env,
id: 456,
description: "I am a suite",
timer: jasmine.createSpyObj('timer', {'start': null, elapsed: 77000}),
});
suite.startTimer();
suite.endTimer();
expect(suite.getResult().duration).toEqual(77000);
});
describe('#sharedUserContext', function() {
beforeEach(function() {
this.suite = new jasmineUnderTest.Suite({});

30
spec/core/baseSpec.js Normal file
View File

@@ -0,0 +1,30 @@
describe('base helpers', function() {
describe('isError_', function() {
it("correctly handles WebSocket events", function(done) {
if (typeof jasmine.getGlobal().WebSocket === 'undefined') {
done();
return;
}
var obj = (function() {
var sock = new WebSocket('ws://localhost');
var event;
sock.onerror = function(e) {
event = e
};
return function() { return event };
})();
var left = 20;
var int = setInterval(function() {
if (obj() || left === 0) {
var result = jasmineUnderTest.isError_(obj());
expect(result).toBe(false);
done();
} else {
left--;
}
}, 100);
});
});
});

View File

@@ -1436,6 +1436,9 @@ describe("Env integration", function() {
status: 'pending'
}));
var suiteDone = reporter.suiteDone.calls.argsFor(0)[0];
expect(typeof suiteDone.duration).toBe('number');
var suiteResult = reporter.suiteStarted.calls.argsFor(0)[0];
expect(suiteResult.description).toEqual("A Suite");
@@ -1942,10 +1945,10 @@ describe("Env integration", function() {
reporter.jasmineDone.and.callFake(function() {
expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('async suite', [
/^(((Uncaught )?Error: suite( thrown)?)|(suite thrown))$/
/^(((Uncaught )?(exception: )?Error: suite( thrown)?)|(suite thrown))$/
]);
expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite async spec', [
/^(((Uncaught )?Error: spec( thrown)?)|(spec thrown))$/
/^(((Uncaught )?(exception: )?Error: spec( thrown)?)|(spec thrown))$/
]);
done();
});
@@ -2046,6 +2049,7 @@ describe("Env integration", function() {
passed: false,
globalErrorType: 'load',
message: 'Uncaught SyntaxError: Unexpected end of input',
stack: 'a stack',
filename: 'borkenSpec.js',
lineno: 42
},
@@ -2053,6 +2057,7 @@ describe("Env integration", function() {
passed: false,
globalErrorType: 'load',
message: 'Uncaught Error: ENOCHEESE',
stack: undefined,
filename: undefined,
lineno: undefined
}
@@ -2062,7 +2067,7 @@ describe("Env integration", function() {
});
env.addReporter(reporter);
global.onerror('Uncaught SyntaxError: Unexpected end of input', 'borkenSpec.js', 42);
global.onerror('Uncaught SyntaxError: Unexpected end of input', 'borkenSpec.js', 42, undefined, {stack: 'a stack'});
global.onerror('Uncaught Error: ENOCHEESE');
env.execute();
@@ -2312,7 +2317,7 @@ describe("Env integration", function() {
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']);
// prevent deprecation from being desplayed
// prevent deprecation from being displayed
spyOn(console, "error");
reporter.jasmineDone.and.callFake(function(result) {
@@ -2364,7 +2369,7 @@ describe("Env integration", function() {
try { throw new Error('suite level deprecation') } catch (err) { suiteLevelError = err; }
try { throw new Error('spec level deprecation') } catch (err) { specLevelError = err; }
// prevent deprecation from being desplayed
// prevent deprecation from being displayed
spyOn(console, "error");
reporter.jasmineDone.and.callFake(function(result) {

View File

@@ -575,23 +575,15 @@ describe("toEqual", function() {
return typeof document === 'undefined'
}
beforeEach(function(done) {
beforeEach(function() {
this.nonBrowser = isNotRunningInBrowser();
if (this.nonBrowser) {
var jsdom = require('jsdom');
var self = this;
jsdom.env('', function(err, win) {
if (err) {
done.fail(err);
} else {
jasmineUnderTest.getGlobal().Node = win.Node;
self.doc = win.document;
done();
}
});
var JSDOM = require('jsdom').JSDOM;
var dom = new JSDOM();
jasmineUnderTest.getGlobal().Node = dom.window.Node;
this.doc = dom.window.document;
} else {
this.doc = document;
done();
}
});
@@ -624,6 +616,99 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between SVG nodes", function () {
var nodeA = this.doc.createElementNS('http://www.w3.org/2000/svg', 'svg'),
nodeB = this.doc.createElementNS('http://www.w3.org/2000/svg', 'svg');
nodeA.setAttribute('height', '50');
nodeB.setAttribute('height', '30');
var rect = this.doc.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('width', '50');
nodeA.appendChild(rect);
expect(nodeA.isEqualNode(nodeB)).toBe(false);
var actual = {a: nodeA},
expected = {a: nodeB},
message = 'Expected $.a = <svg height="50">...</svg> to equal <svg height="30">.';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports whole DOM node when attribute contains > character", function () {
var nodeA = this.doc.createElement('div'),
nodeB = this.doc.createElement('div');
nodeA.setAttribute('thing', '>>>');
nodeB.setAttribute('thing', 'bar');
expect(nodeA.isEqualNode(nodeB)).toBe(false);
var actual = {a: nodeA},
expected = {a: nodeB},
message = 'Expected $.a = <div thing=">>>"> to equal <div thing="bar">.';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it('reports no content when DOM node has multiple empty text nodes', function () {
var nodeA = this.doc.createElement('div'),
nodeB = this.doc.createElement('div');
nodeA.appendChild(this.doc.createTextNode(''));
nodeA.appendChild(this.doc.createTextNode(''));
nodeA.appendChild(this.doc.createTextNode(''));
nodeA.appendChild(this.doc.createTextNode(''));
expect(nodeA.isEqualNode(nodeB)).toBe(false);
var actual = {a: nodeA},
expected = {a: nodeB},
message = 'Expected $.a = <div> to equal <div>.';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it('reports content when DOM node has non empty text node', function () {
var nodeA = this.doc.createElement('div'),
nodeB = this.doc.createElement('div');
nodeA.appendChild(this.doc.createTextNode('Hello Jasmine!'));
expect(nodeA.isEqualNode(nodeB)).toBe(false);
var actual = {a: nodeA},
expected = {a: nodeB},
message = 'Expected $.a = <div>...</div> to equal <div>.';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it('reports empty DOM attributes', function () {
var nodeA = this.doc.createElement('div'),
nodeB = this.doc.createElement('div');
nodeA.setAttribute('contenteditable', '');
expect(nodeA.isEqualNode(nodeB)).toBe(false);
var actual = {a: nodeA},
expected = {a: nodeB},
message = 'Expected $.a = <div contenteditable> to equal <div>.';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it('reports 0 attr value as non empty DOM attribute', function () {
var nodeA = this.doc.createElement('div'),
nodeB = this.doc.createElement('div');
nodeA.setAttribute('contenteditable', 0);
expect(nodeA.isEqualNode(nodeB)).toBe(false);
var actual = {a: nodeA},
expected = {a: nodeB},
message = 'Expected $.a = <div contenteditable="0"> to equal <div>.';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between a DOM node and a bare Object", function() {
var actual = {a: this.doc.createElement('div')},
expected = {a: {}},

View File

@@ -1,5 +1,5 @@
describe('toHaveClass', function() {
beforeEach(function(done) {
beforeEach(function() {
this.createElementWithClassName = function(className) {
var el = this.doc.createElement('div');
el.className = className;
@@ -8,18 +8,10 @@ describe('toHaveClass', function() {
if (typeof document !== 'undefined') {
this.doc = document;
done();
} else {
var jsdom = require('jsdom');
var self = this;
jsdom.env('', function(err, win) {
if (err) {
done.fail(err);
} else {
self.doc = win.document;
done();
}
});
var JSDOM = require('jsdom').JSDOM;
var dom = new JSDOM();
this.doc = dom.window.document;
}
});

View File

@@ -1,8 +1,6 @@
(function() {
var path = require("path"),
fs = require("fs");
var glob = require("glob");
fg = require("fast-glob");
var jasmineUnderTestRequire = require(path.join(__dirname, "../../src/core/requireCore.js"));
@@ -16,12 +14,12 @@
}
function getSourceFiles() {
var src_files = ['core/**/*.js', 'version.js'];
src_files.forEach(function(file) {
var filePath = path.join(__dirname, "../../", 'src/', file);
glob.sync(filePath).forEach(function(resolvedFile) {
require(resolvedFile);
});
var src_files = ['core/**/*.js', 'version.js'].map(function(file) {
return path.join(__dirname, "../../", 'src/', file);
});
fg.sync(src_files).forEach(function(resolvedFile) {
require(resolvedFile);
});
}

View File

@@ -28,7 +28,7 @@ describe('Printing a big object', function(){
return object;
}
it('takes a resonable amount of time', function(){
it('takes a reasonable amount of time', function(){
bigObject = generateObject(0);
expect(jasmineUnderTest.pp(bigObject)).toMatch(/cycle/);
});

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Jasmine suite</title>
<link rel="shortcut icon" href="images/jasmine_favicon.png" type="image/png" />
<link rel="stylesheet" href="lib/jasmine-core/jasmine.css" type="text/css" media="screen" />
<% files.forEach(function(file) { %>
<script src="<%= file %>" type="text/javascript"></script>
<% }) %>
<body>
</body>
</html>

View File

@@ -66,7 +66,7 @@ getJasmineRequireObj().Clock = function() {
* The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes.
* @name Clock#withMock
* @function
* @param {closure} Function The function to be called.
* @param {Function} closure The function to be called.
*/
self.withMock = function(closure) {
this.install();

View File

@@ -100,11 +100,12 @@ getJasmineRequireObj().Env = function(j$) {
if (!options.suppressLoadErrors) {
installGlobalErrors();
globalErrors.pushListener(function(message, filename, lineno) {
globalErrors.pushListener(function(message, filename, lineno, colNo, err) {
topSuite.result.failedExpectations.push({
passed: false,
globalErrorType: 'load',
message: message,
stack: err && err.stack,
filename: filename,
lineno: lineno
});
@@ -511,6 +512,7 @@ getJasmineRequireObj().Env = function(j$) {
currentlyExecutingSuites.push(suite);
defaultResourcesForRunnable(suite.id, suite.parentSuite.id);
reporter.suiteStarted(suite.result, next);
suite.startTimer();
},
nodeComplete: function(suite, result, next) {
if (suite !== currentSuite()) {
@@ -523,7 +525,7 @@ getJasmineRequireObj().Env = function(j$) {
if (result.status === 'failed') {
hasFailures = true;
}
suite.endTimer();
reporter.suiteDone(result, next);
},
orderChildren: function(node) {
@@ -570,7 +572,7 @@ getJasmineRequireObj().Env = function(j$) {
/**
* Information passed to the {@link Reporter#jasmineDone} event.
* @typedef JasmineDoneInfo
* @property {OverallStatus} overallStatus - The overall result of the sute: 'passed', 'failed', or 'incomplete'.
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
@@ -803,9 +805,9 @@ getJasmineRequireObj().Env = function(j$) {
fn: fn,
timeout: timeout || 0
},
throwOnExpectationFailure: config.oneFailurePerSpec
throwOnExpectationFailure: config.oneFailurePerSpec,
timer: new j$.Timer(),
});
return spec;
function specResultCallback(result, next) {

View File

@@ -1,11 +1,15 @@
getJasmineRequireObj().ExceptionFormatter = function(j$) {
var ignoredProperties = ['name', 'message', 'stack', 'fileName', 'sourceURL', 'line', 'lineNumber', 'column', 'description', 'jasmineMessage'];
function ExceptionFormatter(options) {
var jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile();
this.message = function(error) {
var message = '';
if (error.name && error.message) {
if (error.jasmineMessage) {
message += error.jasmineMessage;
} else if (error.name && error.message) {
message += error.name + ': ' + error.message;
} else if (error.message) {
message += error.message;
@@ -63,12 +67,11 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
return;
}
var ignored = ['name', 'message', 'stack', 'fileName', 'sourceURL', 'line', 'lineNumber', 'column', 'description'];
var result = {};
var empty = true;
for (var prop in error) {
if (j$.util.arrayContains(ignored, prop)) {
if (j$.util.arrayContains(ignoredProperties, prop)) {
continue;
}
result[prop] = error[prop];

View File

@@ -14,28 +14,44 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
};
this.originalHandlers = {};
this.installOne_ = function installOne_(errorType) {
this.jasmineHandlers = {};
this.installOne_ = function installOne_(errorType, jasmineMessage) {
function taggedOnError(error) {
error.jasmineMessage = jasmineMessage + ': ' + error;
var handler = handlers[handlers.length - 1];
if (handler) {
handler(error);
} else {
throw error;
}
}
this.originalHandlers[errorType] = global.process.listeners(errorType);
this.jasmineHandlers[errorType] = taggedOnError;
global.process.removeAllListeners(errorType);
global.process.on(errorType, onerror);
global.process.on(errorType, taggedOnError);
this.uninstall = function uninstall() {
var errorTypes = Object.keys(this.originalHandlers);
for (var iType = 0; iType < errorTypes.length; iType++) {
var errorType = errorTypes[iType];
global.process.removeListener(errorType, onerror);
global.process.removeListener(errorType, this.jasmineHandlers[errorType]);
for (var i = 0; i < this.originalHandlers[errorType].length; i++) {
global.process.on(errorType, this.originalHandlers[errorType][i]);
}
delete this.originalHandlers[errorType];
delete this.jasmineHandlers[errorType];
}
};
};
this.install = function install() {
if (global.process && global.process.listeners && j$.isFunction_(global.process.on)) {
this.installOne_('uncaughtException');
this.installOne_('unhandledRejection');
this.installOne_('uncaughtException', 'Uncaught exception');
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
} else {
var originalHandler = global.onerror;
global.onerror = onerror;

View File

@@ -1,10 +1,4 @@
getJasmineRequireObj().JsApiReporter = function() {
var noopTimer = {
start: function(){},
elapsed: function(){ return 0; }
};
getJasmineRequireObj().JsApiReporter = function(j$) {
/**
* @name jsApiReporter
* @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object.
@@ -12,7 +6,7 @@ getJasmineRequireObj().JsApiReporter = function() {
* @hideconstructor
*/
function JsApiReporter(options) {
var timer = options.timer || noopTimer,
var timer = options.timer || j$.noopTimer,
status = 'loaded';
this.started = false;

View File

@@ -23,7 +23,7 @@ getJasmineRequireObj().Order = function() {
}
// Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function
// used to get a different output when the key changes slighly.
// used to get a different output when the key changes slightly.
// We use your return to sort the children randomly in a consistent way when
// used in conjunction with a seed

View File

@@ -34,10 +34,12 @@ getJasmineRequireObj().pp = function(j$) {
this.emitScalar(value.toString());
} else if (typeof value === 'function') {
this.emitScalar('Function');
} else if (value.nodeType === 1) {
this.emitDomElement(value);
} else if (typeof value.nodeType === 'number') {
this.emitScalar('HTMLNode');
} else if (j$.isDomNode(value)) {
if (value.tagName) {
this.emitDomElement(value);
} else {
this.emitScalar('HTMLNode');
}
} else if (value instanceof Date) {
this.emitScalar('Date(' + value + ')');
} else if (j$.isSet(value)) {
@@ -228,15 +230,29 @@ getJasmineRequireObj().pp = function(j$) {
};
PrettyPrinter.prototype.emitDomElement = function(el) {
var closingTag = '</' + el.tagName.toLowerCase() + '>';
var tagName = el.tagName.toLowerCase(),
attrs = el.attributes,
i,
len = attrs.length,
out = '<' + tagName,
attr;
if (el.innerHTML === '') {
this.append(el.outerHTML.replace(closingTag, ''));
} else {
var tagEnd = el.outerHTML.indexOf('>');
this.append(el.outerHTML.substring(0, tagEnd + 1));
this.append('...' + closingTag);
for (i = 0; i < len; i++) {
attr = attrs[i];
out += ' ' + attr.name;
if (attr.value !== '') {
out += '="' + attr.value + '"';
}
}
out += '>';
if (el.childElementCount !== 0 || el.textContent !== '') {
out += '...</' + tagName + '>';
}
this.append(out);
};
PrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) {

View File

@@ -78,7 +78,7 @@ getJasmineRequireObj().QueueRunner = function(j$) {
cleanup();
if (j$.isError_(err)) {
if (!(err instanceof StopExecutionError)) {
if (!(err instanceof StopExecutionError) && !err.jasmineMessage) {
self.fail(err);
}
self.errored = errored = true;

View File

@@ -14,6 +14,7 @@ getJasmineRequireObj().Spec = function(j$) {
this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
this.timer = attrs.timer || j$.noopTimer;
if (!this.queueableFn.fn) {
this.pend();
@@ -29,6 +30,7 @@ getJasmineRequireObj().Spec = function(j$) {
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
*/
this.result = {
id: this.id,
@@ -37,7 +39,8 @@ getJasmineRequireObj().Spec = function(j$) {
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
pendingReason: ''
pendingReason: '',
duration: null,
};
}
@@ -67,6 +70,7 @@ getJasmineRequireObj().Spec = function(j$) {
var onStart = {
fn: function(done) {
self.timer.start();
self.onStart(self, done);
}
};
@@ -90,6 +94,7 @@ getJasmineRequireObj().Spec = function(j$) {
self.onException.apply(self, arguments);
},
onComplete: function() {
self.result.duration = self.timer.elapsed();
onComplete(self.result.status === 'failed' && new j$.StopExecutionError('spec failed'));
},
userContext: this.userContext()

View File

@@ -112,7 +112,7 @@ getJasmineRequireObj().Spy = function (j$) {
if (!strategy) {
if (argsStrategies.any() && !baseStrategy.isConfigured()) {
throw new Error('Spy \'' + strategyArgs.name + '\' receieved a call with arguments ' + j$.pp(Array.prototype.slice.call(args)) + ' but all configured strategies specify other arguments.');
throw new Error('Spy \'' + strategyArgs.name + '\' received a call with arguments ' + j$.pp(Array.prototype.slice.call(args)) + ' but all configured strategies specify other arguments.');
} else {
strategy = baseStrategy;
}

View File

@@ -14,6 +14,8 @@ getJasmineRequireObj().Suite = function(j$) {
this.beforeAllFns = [];
this.afterAllFns = [];
this.timer = attrs.timer || j$.noopTimer;
this.children = [];
/**
@@ -24,13 +26,15 @@ getJasmineRequireObj().Suite = function(j$) {
* @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
* @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach.
*/
this.result = {
id: this.id,
description: this.description,
fullName: this.getFullName(),
failedExpectations: [],
deprecationWarnings: []
deprecationWarnings: [],
duration: null,
};
}
@@ -72,6 +76,14 @@ getJasmineRequireObj().Suite = function(j$) {
this.afterAllFns.unshift(fn);
};
Suite.prototype.startTimer = function() {
this.timer.start();
};
Suite.prototype.endTimer = function() {
this.result.duration = this.timer.elapsed();
};
function removeFns(queueableFns) {
for(var i = 0; i < queueableFns.length; i++) {
queueableFns[i].fn = null;

View File

@@ -20,3 +20,10 @@ getJasmineRequireObj().Timer = function() {
return Timer;
};
getJasmineRequireObj().noopTimer = function() {
return {
start: function() {},
elapsed: function() { return 0; }
};
};

View File

@@ -17,7 +17,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
*/
j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50;
/**
* Maximum number of charasters to display when pretty printing objects.
* Maximum number of characters to display when pretty printing objects.
* Characters past this number will be ellipised.
* @name jasmine.MAX_PRETTY_PRINT_CHARS
*/
@@ -89,9 +89,15 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
if (value instanceof Error) {
return true;
}
if (value && value.constructor && value.constructor.constructor &&
(value instanceof (value.constructor.constructor('return this')()).Error)) {
return true;
if (value && value.constructor && value.constructor.constructor) {
var valueGlobal = value.constructor.constructor('return this');
if (j$.isFunction_(valueGlobal)) {
valueGlobal = valueGlobal();
}
if (valueGlobal.Error && value instanceof valueGlobal.Error) {
return true;
}
}
return false;
};

View File

@@ -41,7 +41,8 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
j$.Expector = jRequire.Expector(j$);
j$.Expectation = jRequire.Expectation(j$);
j$.buildExpectationResult = jRequire.buildExpectationResult();
j$.JsApiReporter = jRequire.JsApiReporter();
j$.noopTimer = jRequire.noopTimer();
j$.JsApiReporter = jRequire.JsApiReporter(j$);
j$.matchersUtil = jRequire.matchersUtil(j$);
j$.ObjectContaining = jRequire.ObjectContaining(j$);
j$.ArrayContaining = jRequire.ArrayContaining(j$);

View File

@@ -1,10 +1,4 @@
jasmineRequire.HtmlReporter = function(j$) {
var noopTimer = {
start: function() {},
elapsed: function() { return 0; }
};
function ResultsStateBuilder() {
this.topResults = new j$.ResultsNode({}, '', null);
this.currentParent = this.topResults;
@@ -58,7 +52,7 @@ jasmineRequire.HtmlReporter = function(j$) {
navigateWithNewParam = options.navigateWithNewParam || function() {},
addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
filterSpecs = options.filterSpecs,
timer = options.timer || noopTimer,
timer = options.timer || j$.noopTimer,
htmlReporterMain,
symbols,
deprecationWarnings = [];

View File

@@ -1,5 +1,3 @@
@import "compass";
$line-height: 14px;
$margin-unit: 14px;
@@ -68,9 +66,12 @@ body {
}
.jasmine-banner .jasmine-title {
background: inline-image('jasmine-horizontal.png') no-repeat;
background: inline-image('jasmine-horizontal.svg') no-repeat, none;
@include background-size(100%);
background: url('../../images/jasmine-horizontal.png') no-repeat;
background: url('../../images/jasmine-horizontal.svg') no-repeat, none;
-moz-background-size: 100%;
-o-background-size: 100%;
-webkit-background-size: 100%;
background-size: 100%;
display: block;
float: left;
width: 90px;
@@ -110,7 +111,7 @@ body {
//--- Symbol summary ---//
.jasmine-symbol-summary {
@include clearfix;
overflow: hidden;
margin: $line-height 0;
li {

View File

@@ -9,4 +9,4 @@ then
fi
fi
bundle exec rake jasmine:ci
node ci.js

View File

@@ -1,10 +0,0 @@
#!/bin/bash -e
rm -rf ~/.nvm
git clone https://github.com/creationix/nvm.git ~/.nvm
(cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`)
source ~/.nvm/nvm.sh
nvm install ${1:-"v0.12.18"}
npm install
npm test