Compare commits

..

40 Commits

Author SHA1 Message Date
Steve Gravrock
04133264e0 Bump version to 4.1.0 2022-04-11 20:24:42 -07:00
Steve Gravrock
d2bf6062cb README updates
* Replaced redundant installation instructions with link to docs
* Added links to usage instructions
* Fixes #1941
2022-04-09 11:20:54 -07:00
Steve Gravrock
fb639d0f55 toBeCloseTo treats Infinity and -Infinity as close to themselves
* Fixes #1957
2022-04-09 11:14:24 -07:00
Steve Gravrock
52b1a3a7f8 Merge branch 'refactor/rm-deprecated-substr' of https://github.com/CommanderRoot/jasmine
* Merges #1962 from @CommanderRoot
2022-04-02 10:32:59 -07:00
Steve Gravrock
1f7880ee8c Updated Sauce Connect 2022-03-29 20:57:49 -07:00
Steve Gravrock
a6e1aef8d7 Added debug logging to flaky spec 2022-03-29 20:57:49 -07:00
Steve Gravrock
d2815982ce Removed obsolete vendor-specific background-size rules
* Fixes #1961
2022-03-29 07:20:02 -07:00
Tobias Speicher
aa979277fb refactor: replace deprecated String.prototype.substr()
.substr() is deprecated so we replace it with functions which work similarily but aren't deprecated

Signed-off-by: Tobias Speicher <rootcommander@gmail.com>
2022-03-25 16:03:46 +01:00
Steve Gravrock
1e0c312f9f Merge branch 'main' of https://github.com/nitobuendia/jasmine
* Adds toHaveSpyInteractions matcher
* Merges #1959 from @nitobuenida
* Fixes #1568
2022-03-18 11:10:39 -07:00
Nito Buendia
c5db939886 Run cleanup 2022-03-17 21:09:14 +08:00
Nito Buendia
e470fb56d7 Refactor error message to account for negate comparisons
The message return on negate clause was not expected. This makes it negative to match expectation. This also add tests for the change, and renames some tests to make it more clear.
2022-03-17 21:06:51 +08:00
Nito Buendia
a8a6577cd7 Replace parameterized test with different expectations
This approach makes it hard to scale and goes against DRY and debuggability vs the previous approach which followed Python parameterized testing, but this was the recommendation of the Jasmine team to keep it consistent with other tests.

Further tests here could be adding other types like Array, Map, WeakMap, Set, WeakSet...
2022-03-17 20:16:22 +08:00
Nito Buendia
1660015c12 Run formatter 2022-03-16 23:01:20 +08:00
Nito Buendia
faf210ab4c Remove dependency on matchersUtil for test 2022-03-16 23:00:27 +08:00
Nito Buendia
00fd4a819f Refactor tests to depend on jasmineUnderTest 2022-03-16 21:48:03 +08:00
Nito Buendia
091cd8c3b6 Remove spyObj setup from test that does not require it 2022-03-16 21:34:28 +08:00
Nito Buendia
aba0c98eb9 Fix unit test to include testValue instead of a constant value 2022-03-16 21:31:40 +08:00
Nito Buendia
a7eff79db0 Simplify test for arguments passed 2022-03-16 21:26:59 +08:00
Nito Buendia
2a5673e6ab Change jasmine to jasmineUnderTest 2022-03-16 21:20:21 +08:00
Nito Buendia
c13dd26c4b Change set up to each of the formats
This goes against DRY principle, but it was recommended by Jasmine team to reduce coupling between tests.
2022-03-16 21:18:25 +08:00
Nito Buendia
2e8b477489 Change arrow functions with anonymous functions 2022-03-16 21:14:43 +08:00
Nito Buendia
b2c2e08641 Remove @since tag from JSDoc 2022-03-16 20:15:09 +08:00
Nito Buendia
d7d75abc42 Rename spec to include Spec to allow running 2022-03-16 20:14:06 +08:00
Nito Buendia
0b71d0a2a0 Merge branch 'jasmine:main' into main 2022-03-16 20:09:19 +08:00
Steve Gravrock
dfa5b6a53d Pretty-print [new String("")] as "[ '' ]" not "[]" 2022-03-14 18:47:23 -07:00
Steve Gravrock
7f75f23e5b Use new Circle CI images
The old images are deprecated:
<https://discuss.circleci.com/t/legacy-convenience-image-deprecation/41034>
2022-03-09 13:05:34 -08:00
Steve Gravrock
502cb24bb8 Merge branch '4.0.1' 2022-02-21 17:08:11 -08:00
Nito Buendia
7b01003d0b Add specs for the new toHaveSpyInteractions matcher 2022-02-16 21:11:42 +08:00
Nito Buendia
8e85f3df74 Create toHaveSpyInteractions matcher
This matcher checks all the properties of a given spy object and checks whether at least one of the spies has been called. It returns true if one or more of the spies of the spy object has been called and false otherwise.
2022-02-16 21:10:31 +08:00
Steve Gravrock
b0034797fe Merge branch 'fix-save-args-by-value' of https://github.com/coyoteecd/jasmine
* Fixes cloning of Date objects in saveArgumentsByValue()
* Merges #1955 from @coyoteecd
* Fixes #1885
2022-02-02 21:18:38 -08:00
Dan Cortel
83e9f88952 Make saveArgumentsByValue clone Date objects correctly
* Fixes #1885
2022-01-26 18:32:05 +02:00
Gwendolyn Van Hove
de13b47809 Update maintainers list 2022-01-20 15:57:48 -08:00
Steve Gravrock
9e10743a1c Include the name of the suite in the empty suite error message 2022-01-17 15:08:56 -08:00
Steve Gravrock
5327157832 Always run formerly flaky specs 2022-01-17 14:54:21 -08:00
Steve Gravrock
6e3128c792 Merge branch 'SymboltoEqual' of https://github.com/laeleoni/jasmine
* Merges #1879 from @laeleoni
* Fixes #1879
2022-01-15 11:44:53 -08:00
Steve Gravrock
bd2e877f4e Removed scheduled workflows from Circle CI config
Scheduled pipelines (configured via the Circle web UI) will be used instead.
2022-01-15 10:50:03 -08:00
Steve Gravrock
5b23ffe174 Merge pull request #1951 from ath0mas/patch-1
Update html example in README
2022-01-04 20:37:03 -08:00
Alexis THOMAS
384e0859c7 Update html example in README
Follow SpecRunner.html: Simplifies types (197cb13817) and Split boot.js in two (286524959b)
2022-01-04 23:51:30 +01:00
Steve Gravrock
616682e487 Stop cron builds on 3.99 and 4.0 2022-01-01 13:29:39 -08:00
leoni laetitia
3234d475cd expect...toEqual check for Symbols 2021-01-31 22:32:58 +01:00
29 changed files with 596 additions and 194 deletions

View File

@@ -3,25 +3,22 @@
version: 2.1
orbs:
node: circleci/node@3.0.0
executors:
node16:
docker:
- image: cimg/node:16.1.0-browsers
- image: cimg/node:16.14.0 # Latest 16.x
working_directory: ~/workspace
node14:
docker:
- image: circleci/node:14
- image: cimg/node:14.17.4 # Latest 14.x
working_directory: ~/workspace
node12_latest:
docker:
- image: circleci/node:12
- image: cimg/node:12.22.10 # Latest 12.x
working_directory: ~/workspace
node12_17:
docker:
- image: circleci/node:12.17
- image: cimg/node:12.17.0 # Oldest version supported by Jasmine
working_directory: ~/workspace
jobs:
@@ -60,8 +57,6 @@ jobs:
test_browsers: &test_browsers
executor: node14
environment:
SKIP_JASMINE_BROWSER_FLAKES: "true"
steps:
- attach_workspace:
at: .
@@ -69,10 +64,10 @@ jobs:
name: Install Sauce Connect
command: |
cd /tmp
curl https://saucelabs.com/downloads/sc-4.6.4-linux.tar.gz | tar zxf -
chmod +x sc-4.6.4-linux/bin/sc
curl https://saucelabs.com/downloads/sc-4.7.1-linux.tar.gz | tar zxf -
chmod +x sc-4.7.1-linux/bin/sc
mkdir ~/workspace/bin
cp sc-4.6.4-linux/bin/sc ~/workspace/bin
cp sc-4.7.1-linux/bin/sc ~/workspace/bin
~/workspace/bin/sc --version
- run:
name: Run tests
@@ -90,58 +85,8 @@ jobs:
scripts/stop-sauce-connect $(cat sauce-pidfile)
exit $exitcode
test_browser_flakes:
<<: *test_browsers
environment:
SKIP_JASMINE_BROWSER_FLAKES: "false"
workflows:
version: 2
cron:
triggers:
- schedule:
# Times are UTC.
cron: "0 11 * * *"
filters:
branches:
only:
- main
- "3.99"
- "4.0"
jobs:
- build:
executor: node16
name: build_node_16
- build:
executor: node14
name: build_node_14
- build:
executor: node12_latest
name: build_node_12_latest
- build:
executor: node12_17
name: build_node_12_17
- test_node:
executor: node16
name: test_node_16
requires:
- build_node_16
- test_node:
executor: node12_latest
name: test_node_12_latest
requires:
- build_node_12_latest
- test_node:
executor: node12_17
name: test_node_12_17
requires:
- build_node_12_17
- test_browsers:
requires:
- build_node_14
filters:
branches:
ignore: /pull\/.*/ # Don't run on pull requests.
push:
jobs:
@@ -183,23 +128,3 @@ workflows:
filters:
branches:
ignore: /pull\/.*/ # Don't run on pull requests.
browser-flakes:
triggers:
- schedule:
# Times are UTC.
cron: "0 10 * * *"
filters:
branches:
only:
- browser-flakes
jobs:
- build:
executor: node14
name: build_node_14
- test_browser_flakes:
requires:
- build_node_14
filters:
branches:
ignore: /pull\/.*/ # Don't run on pull requests.

View File

@@ -20,35 +20,20 @@ Please read the [contributors' guide](https://github.com/jasmine/jasmine/blob/ma
## Installation
For the Jasmine NPM module:<br>
[https://github.com/jasmine/jasmine-npm](https://github.com/jasmine/jasmine-npm).
There are several different ways to install Jasmine, depending on your
environment and how you'd like to use it. See the [Getting Started page](https://jasmine.github.io/pages/getting_started.html)
for details.
For the Jasmine browser runner:<br>
[https://github.com/jasmine/jasmine-browser](https://github.com/jasmine/jasmine-browser).
## Usage
To install Jasmine standalone on your local box (where **_{#.#.#}_** below is substituted by the release number downloaded):
* Download the standalone distribution for your desired release from the [releases page](https://github.com/jasmine/jasmine/releases).
* Create a Jasmine directory in your project. - `mkdir my-project/jasmine`
* Move the dist to your project directory. - `mv jasmine/dist/jasmine-standalone-{#.#.#}.zip my-project/jasmine`
* Change directory. - `cd my-project/jasmine`
* Unzip the dist. - `unzip jasmine-standalone-{#.#.#}.zip`
Add the following to your HTML file:
```html
<link rel="shortcut icon" type="image/png" href="lib/jasmine-{#.#.#}/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="lib/jasmine-{#.#.#}/jasmine.css">
<script type="text/javascript" src="lib/jasmine-{#.#.#}/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-{#.#.#}/jasmine-html.js"></script>
<script type="text/javascript" src="lib/jasmine-{#.#.#}/boot.js"></script>
```
See the [documentation site](https://jasmine.github.io/pages/docs_home.html),
particularly the [Your First Suite tutorial](https://jasmine.github.io/tutorials/your_first_suite)
for information on writing specs.
## Supported environments
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
Microsoft Edge) as well as nodejs.
Microsoft Edge) as well as Node.
| Environment | Supported versions |
|-------------------|--------------------|
@@ -74,18 +59,19 @@ for the supported environments for each Jasmine release.
## Maintainers
* [Gregg Van Hove](mailto:gvanhove@pivotal.io), Pivotal Labs
* [Gwendolyn Van Hove](mailto:gwen@slackersoft.net)
* [Steve Gravrock](mailto:sdg@panix.com)
### Maintainers Emeritus
* [Davis W. Frank](mailto:dwfrank@pivotal.io), Pivotal Labs
* [Rajan Agaskar](mailto:rajan@pivotal.io), Pivotal Labs
* [Greg Cobb](mailto:gcobb@pivotal.io), Pivotal Labs
* [Chris Amavisca](mailto:camavisca@pivotal.io), Pivotal Labs
* [Christian Williams](mailto:antixian666@gmail.com), Cloud Foundry
* [Davis W. Frank](mailto:dwfrank@pivotal.io)
* [Rajan Agaskar](mailto:rajan@pivotal.io)
* [Greg Cobb](mailto:gcobb@pivotal.io)
* [Chris Amavisca](mailto:camavisca@pivotal.io)
* [Christian Williams](mailto:antixian666@gmail.com)
* Sheel Choksi
Copyright (c) 2008-2018 Pivotal Labs. This software is licensed under the MIT License.
Copyright (c) 2008-2022 Jasmine Maintainers. This software is licensed under the MIT License.
## License

File diff suppressed because one or more lines are too long

View File

@@ -152,6 +152,7 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
'toHaveBeenCalledTimes',
'toHaveBeenCalledWith',
'toHaveClass',
'toHaveSpyInteractions',
'toMatch',
'toThrow',
'toThrowError',
@@ -640,6 +641,8 @@ getJasmineRequireObj().util = function(j$) {
// All falsey values are either primitives, `null`, or `undefined.
if (!argsAsArray[i] || str.match(primitives)) {
clonedArgs.push(argsAsArray[i]);
} else if (str === '[object Date]') {
clonedArgs.push(new Date(argsAsArray[i].valueOf()));
} else {
clonedArgs.push(j$.util.clone(argsAsArray[i]));
}
@@ -1039,7 +1042,7 @@ getJasmineRequireObj().Spec = function(j$) {
boilerplateEnd =
boilerplateStart + Spec.pendingSpecExceptionMessage.length;
return fullMessage.substr(boilerplateEnd);
return fullMessage.slice(boilerplateEnd);
};
Spec.pendingSpecExceptionMessage = '=> marked Pending';
@@ -2169,7 +2172,10 @@ getJasmineRequireObj().Env = function(j$) {
}
addSpecsToSuite(suite, specDefinitions);
if (suite.parentSuite && !suite.children.length) {
throw new Error('describe with no children (describe() or it())');
throw new Error(
'describe with no children (describe() or it()): ' +
suite.getFullName()
);
}
return suite.metadata;
};
@@ -5576,17 +5582,16 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
};
function keys(obj, isArray) {
var allKeys = Object.keys
? Object.keys(obj)
: (function(o) {
var keys = [];
for (var key in o) {
if (j$.util.has(o, key)) {
keys.push(key);
}
}
return keys;
})(obj);
var allKeys = (function(o) {
var keys = [];
for (var key in o) {
if (j$.util.has(o, key)) {
keys.push(key);
}
}
// eslint-disable-next-line compat/compat
return keys.concat(Object.getOwnPropertySymbols(o));
})(obj);
if (!isArray) {
return allKeys;
@@ -5956,6 +5961,14 @@ getJasmineRequireObj().toBeCloseTo = function() {
);
}
// Infinity is close to Infinity and -Infinity is close to -Infinity,
// regardless of the precision.
if (expected === Infinity || expected === -Infinity) {
return {
pass: actual === expected
};
}
var pow = Math.pow(10, precision + 1);
var delta = Math.abs(expected - actual);
var maxDelta = Math.pow(10, -precision) / 2;
@@ -6926,6 +6939,82 @@ getJasmineRequireObj().toHaveSize = function(j$) {
return toHaveSize;
};
getJasmineRequireObj().toHaveSpyInteractions = function(j$) {
var getErrorMsg = j$.formatErrorMsg(
'<toHaveSpyInteractions>',
'expect(<spyObj>).toHaveSpyInteractions()'
);
/**
* {@link expect} the actual (a {@link SpyObj}) spies to have been called.
* @function
* @name matchers#toHaveSpyInteractions
* @since 4.1.0
* @example
* expect(mySpyObj).toHaveSpyInteractions();
* expect(mySpyObj).not.toHaveSpyInteractions();
*/
function toHaveSpyInteractions(matchersUtil) {
return {
compare: function(actual) {
var result = {};
if (!j$.isObject_(actual)) {
throw new Error(
getErrorMsg('Expected a spy object, but got ' + typeof actual + '.')
);
}
if (arguments.length > 1) {
throw new Error(getErrorMsg('Does not take arguments'));
}
result.pass = false;
let hasSpy = false;
const calledSpies = [];
for (const spy of Object.values(actual)) {
if (!j$.isSpy(spy)) continue;
hasSpy = true;
if (spy.calls.any()) {
result.pass = true;
calledSpies.push([spy.and.identity, spy.calls.count()]);
}
}
if (!hasSpy) {
throw new Error(
getErrorMsg(
'Expected a spy object with spies, but object has no spies.'
)
);
}
let resultMessage;
if (result.pass) {
resultMessage =
'Expected spy object spies not to have been called, ' +
'but the following spies were called: ';
resultMessage += calledSpies
.map(([spyName, spyCount]) => {
return `${spyName} called ${spyCount} time(s)`;
})
.join(', ');
} else {
resultMessage =
'Expected spy object spies to have been called, ' +
'but no spies were called.';
}
result.message = resultMessage;
return result;
}
};
}
return toHaveSpyInteractions;
};
getJasmineRequireObj().toMatch = function(j$) {
var getErrorMsg = j$.formatErrorMsg(
'<toMatch>',
@@ -7488,7 +7577,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
this.emitScalar('<global>');
} else if (value.jasmineToString) {
this.emitScalar(value.jasmineToString(this.pp_));
} else if (typeof value === 'string') {
} else if (j$.isString_(value)) {
this.emitString(value);
} else if (j$.isSpy(value)) {
this.emitScalar('spy on ' + value.and.identity);
@@ -10199,5 +10288,5 @@ getJasmineRequireObj().UserContext = function(j$) {
};
getJasmineRequireObj().version = function() {
return '4.0.1';
return '4.1.0';
};

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "4.0.1",
"version": "4.1.0",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"

38
release_notes/4.1.0.md Normal file
View File

@@ -0,0 +1,38 @@
# Jasmine 4.1.0 Release Notes
## New Features and Bug Fixes
* toBeCloseTo treats Infinity and -Infinity as close to themselves
* Fixes [#1957](https://github.com/jasmine/jasmine/issues/1957)
* Replaced uses of deprecated String.prototype.substr()
* Merges [#1962](https://github.com/jasmine/jasmine/pull/1962) from @CommanderRoot
* Removed obsolete vendor-specific background-size CSS rules
* Fixes [#1961](https://github.com/jasmine/jasmine/issues/1961)
* Added toHaveSpyInteractions matcher
* Merges [#1959](https://github.com/jasmine/jasmine/pull/1959) from @nitobuenida
* Fixes [#1568](https://github.com/jasmine/jasmine/issues/1568)
* Pretty-print [new String("")] as "[ '' ]", not "[]"
* Fixed cloning of Date objects in Spy#calls#saveArgumentsByValue
* Merges [#1955](https://github.com/jasmine/jasmine/pull/1955) from @coyoteecd
* Fixes [#1885](https://github.com/jasmine/jasmine/issues/1885)
* Include the name of the suite in the empty suite error message
* toEqual checks keys that are Symbols
* Merges [#1879](https://github.com/jasmine/jasmine/pull/1879) from @laeleoni
* Fixes [#1811](https://github.com/jasmine/jasmine/issues/1811)
## Documentation Updates
* Replaced redundant installation instructions in README with link to docs
* Added links to usage instructions to README
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -235,9 +235,17 @@ describe('Env', function() {
});
it('throws an error when it has no children', function() {
expect(function() {
env.describe('done method', function() {});
}).toThrowError('describe with no children (describe() or it())');
let ran = false;
env.describe('parent suite', function() {
expect(function() {
env.describe('child suite', function() {});
}).toThrowError(
'describe with no children (describe() or it()): parent suite child suite'
);
ran = true;
});
expect(ran).toBeTrue();
});
});

View File

@@ -5,6 +5,14 @@ describe('PrettyPrinter', function() {
expect(pp("som' string")).toEqual("'som' string'");
});
it('stringifies empty string primitives and objects recognizably', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(new String(''))).toEqual(pp(''));
expect(pp(new String(''))).toEqual("''");
expect(pp([new String('')])).toEqual(pp(['']));
expect(pp([new String('')])).toEqual("[ '' ]");
});
it('should stringify primitives properly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(true)).toEqual('true');

View File

@@ -139,6 +139,32 @@ describe('jasmineUnderTest.util', function() {
});
});
describe('cloneArgs', function() {
it('clones primitives as-is', function() {
expect(jasmineUnderTest.util.cloneArgs([true, false])).toEqual([
true,
false
]);
expect(jasmineUnderTest.util.cloneArgs([0, 1])).toEqual([0, 1]);
expect(jasmineUnderTest.util.cloneArgs(['str'])).toEqual(['str']);
});
it('clones Regexp objects as-is', function() {
var regex = /match/;
expect(jasmineUnderTest.util.cloneArgs([regex])).toEqual([regex]);
});
it('clones Date objects as-is', function() {
var date = new Date(2022, 1, 1);
expect(jasmineUnderTest.util.cloneArgs([date])).toEqual([date]);
});
it('clones null and undefined', function() {
expect(jasmineUnderTest.util.cloneArgs([null])).toEqual([null]);
expect(jasmineUnderTest.util.cloneArgs([undefined])).toEqual([undefined]);
});
});
describe('getPropertyDescriptor', function() {
it('get property descriptor from object', function() {
var obj = { prop: 1 },

View File

@@ -58,8 +58,8 @@ describe('ArrayContaining', function() {
if (
typeof a == 'string' &&
typeof b == 'string' &&
a.substr(0, 3) == 'foo' &&
b.substr(0, 3) == 'foo'
a.slice(0, 3) == 'foo' &&
b.slice(0, 3) == 'foo'
) {
return true;
}

View File

@@ -48,8 +48,8 @@ describe('ArrayWithExactContents', function() {
if (
typeof a == 'string' &&
typeof b == 'string' &&
a.substr(0, 3) == 'foo' &&
b.substr(0, 3) == 'foo'
a.slice(0, 3) == 'foo' &&
b.slice(0, 3) == 'foo'
) {
return true;
}

View File

@@ -121,8 +121,8 @@ describe('ObjectContaining', function() {
if (
typeof a == 'string' &&
typeof b == 'string' &&
a.substr(0, 3) == 'foo' &&
b.substr(0, 3) == 'foo'
a.slice(0, 3) == 'foo' &&
b.slice(0, 3) == 'foo'
) {
return true;
}

View File

@@ -81,8 +81,8 @@ describe('Custom Matchers (Integration)', function() {
if (
typeof a == 'string' &&
typeof b == 'string' &&
a.substr(0, 3) == 'foo' &&
b.substr(0, 3) == 'foo'
a.slice(0, 3) == 'foo' &&
b.slice(0, 3) == 'foo'
) {
return true;
}

View File

@@ -455,10 +455,6 @@ describe('Env integration', function() {
});
it('copes with async failures after done has been called', function(done) {
if (jasmine.getEnv().skipBrowserFlake) {
jasmine.getEnv().skipBrowserFlake();
}
var global = {
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
@@ -1174,10 +1170,6 @@ describe('Env integration', function() {
});
it('Mock clock can be installed and used in tests', function(done) {
if (jasmine.getEnv().skipBrowserFlake) {
jasmine.getEnv().skipBrowserFlake();
}
var globalSetTimeout = jasmine
.createSpy('globalSetTimeout')
.and.callFake(function(cb, t) {
@@ -1318,16 +1310,13 @@ describe('Env integration', function() {
});
it('should not use the mock clock for asynchronous timeouts', function(done) {
if (jasmine.getEnv().skipBrowserFlake) {
jasmine.getEnv().skipBrowserFlake();
}
createMockedEnv();
var reporter = jasmine.createSpyObj('fakeReporter', ['specDone']),
clock = env.clock;
reporter.specDone.and.callFake(function() {
realSetTimeout(function() {
jasmine.debugLog('Ticking after specDone');
jasmine.clock().tick(1);
}, 0);
});
@@ -1346,24 +1335,24 @@ describe('Env integration', function() {
env.it('spec that should not time out', function(innerDone) {
clock.tick(6);
expect(true).toEqual(true);
realSetTimeout(innerDone);
jasmine.debugLog('Calling realSetTimeout in spec');
realSetTimeout(function() {
jasmine.debugLog('Calling innerDone');
innerDone();
});
});
env.execute(null, function() {
expect(reporter.specDone).toHaveBeenCalledTimes(1);
expect(reporter.specDone.calls.argsFor(0)[0]).toEqual(
jasmine.objectContaining({ status: 'passed' })
);
const event = reporter.specDone.calls.argsFor(0)[0];
jasmine.debugLog('Spec result: ' + jasmine.basicPrettyPrinter_(event));
expect(event).toEqual(jasmine.objectContaining({ status: 'passed' }));
jasmine.clock().tick(1);
realSetTimeout(done);
});
});
it('should wait a custom interval before reporting async functions that fail to complete', function(done) {
if (jasmine.getEnv().skipBrowserFlake) {
jasmine.getEnv().skipBrowserFlake();
}
createMockedEnv();
var reporter = jasmine.createSpyObj('fakeReport', [
'jasmineDone',
@@ -2939,10 +2928,6 @@ describe('Env integration', function() {
});
it('provides custom equality testers to async matchers', function(done) {
if (jasmine.getEnv().skipBrowserFlake) {
jasmine.getEnv().skipBrowserFlake();
}
var specDone = jasmine.createSpy('specDone');
env.addReporter({ specDone: specDone });

22
spec/core/integration/MatchersSpec.js Normal file → Executable file
View File

@@ -625,6 +625,28 @@ describe('Matchers (Integration)', function() {
});
});
describe('toHaveSpyInteractions', function() {
let spyObj;
beforeEach(function() {
spyObj = env.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.otherMethod = function() {};
});
verifyPasses(function(env) {
spyObj.spyA();
env.expect(spyObj).toHaveSpyInteractions();
});
verifyFails(function(env) {
env.expect(spyObj).toHaveSpyInteractions();
});
verifyFails(function(env) {
spyObj.otherMethod();
env.expect(spyObj).toHaveSpyInteractions();
});
});
describe('toMatch', function() {
verifyPasses(function(env) {
env.expect('foo').toMatch(/oo$/);

View File

@@ -110,4 +110,42 @@ describe('toBeCloseTo', function() {
result = matcher.compare(-2.82665525779431, -2.82666, 5);
expect(result.pass).toBe(true);
});
describe('Infinity handling', function() {
it('passes when the actual and expected are both Infinity', function() {
const matcher = jasmineUnderTest.matchers.toBeCloseTo();
const result = matcher.compare(Infinity, Infinity, 0);
expect(result.pass).toBe(true);
});
it('passes when the actual and expected are both -Infinity', function() {
const matcher = jasmineUnderTest.matchers.toBeCloseTo();
const result = matcher.compare(-Infinity, -Infinity, 0);
expect(result.pass).toBe(true);
});
it('fails when the actual is Infinity and the expected is -Infinity', function() {
const matcher = jasmineUnderTest.matchers.toBeCloseTo();
const result = matcher.compare(Infinity, -Infinity, 0);
expect(result.pass).toBe(false);
});
it('fails when the actual is -Infinity and the expected is Infinity', function() {
const matcher = jasmineUnderTest.matchers.toBeCloseTo();
const result = matcher.compare(-Infinity, Infinity, 0);
expect(result.pass).toBe(false);
});
it('fails when the actual is a number and the expected is Infinity', function() {
const matcher = jasmineUnderTest.matchers.toBeCloseTo();
const result = matcher.compare(42, Infinity, 0);
expect(result.pass).toBe(false);
});
it('fails when the actual is a number and the expected is -Infinity', function() {
const matcher = jasmineUnderTest.matchers.toBeCloseTo();
const result = matcher.compare(42, -Infinity, 0);
expect(result.pass).toBe(false);
});
});
});

View File

@@ -1,3 +1,4 @@
/* eslint-disable compat/compat */
describe('toEqual', function() {
'use strict';
@@ -1039,4 +1040,94 @@ describe('toEqual', function() {
expect(compareEquals(actual, expected).message).toEqual(message);
});
});
// == Symbols ==
describe('Symbols', function() {
it('Fails if Symbol compared to Object', function() {
var sym = Symbol('foo');
var obj = {};
expect(sym).not.toEqual(obj);
});
it('Passes Symbol with itself', function() {
var sym = Symbol('foo');
expect(sym).toEqual(sym);
});
it('Fails if two Symbols with same value are compared', function() {
var symA = Symbol('foo');
var symB = Symbol('foo');
expect(symA).not.toEqual(symB);
});
it('Fails if two Symbols with different value are compared', function() {
var symA = Symbol('foo');
var symB = Symbol('bar');
expect(symA).not.toEqual(symB);
});
it('Fails if Symbol compared to NaN', function() {
var sym = Symbol('foo');
expect(sym).not.toEqual(NaN);
});
it('Fails if Symbol compared to Infinity', function() {
var sym = Symbol('foo');
expect(sym).not.toEqual(Infinity);
});
it('Fails if Symbol compared to String', function() {
var sym = Symbol('foo');
var str = 'foo';
expect(sym).not.toEqual(str);
});
it('Fails if Symbol compared to Number', function() {
var sym = Symbol('foo');
var num = Math.random();
expect(sym).not.toEqual(num);
});
it('Fails if Symbol compared to Boolean', function() {
var sym = Symbol('foo');
expect(sym).not.toEqual(true);
expect(sym).not.toEqual(false);
});
it('Fails if Symbol compared to Undefined', function() {
var sym = Symbol('foo');
expect(sym).not.toEqual(undefined);
});
it('Fails if Symbol compared to null', function() {
var sym = Symbol('foo');
expect(sym).not.toEqual(null);
});
it('Fails if Symbol compared to []', function() {
var sym = Symbol('foo');
var arr = ['foo'];
expect(sym).not.toEqual(arr);
});
it('Fails if Symbol compared to Function', function() {
var sym = Symbol('foo');
var f = function func() {};
expect(sym).not.toEqual(f);
});
});
});

View File

@@ -0,0 +1,115 @@
describe('toHaveSpyInteractions', function() {
it('passes when there are spy interactions', function() {
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.spyA();
let result = matcher.compare(spyObj);
expect(result.pass).toBe(true);
});
it('passes when there are multiple spy interactions', function() {
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.spyA();
spyObj.spyB();
spyObj.spyA();
let result = matcher.compare(spyObj);
expect(result.pass).toBe(true);
});
it('fails when there are no spy interactions', function() {
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
let result = matcher.compare(spyObj);
expect(result.pass).toBe(false);
expect(result.message).toContain(
'Expected spy object spies to have been called'
);
});
it('shows the right message is negated', function() {
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.spyA();
let result = matcher.compare(spyObj);
expect(result.pass).toBe(true);
expect(result.message).toContain(
// Will be shown only on negate.
'Expected spy object spies not to have been called'
);
});
it('fails when only non-observed spy object interactions are interacted', function() {
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.otherMethod = function() {};
spyObj.otherMethod();
let result = matcher.compare(spyObj);
expect(result.pass).toBe(false);
expect(result.message).toContain(
'Expected spy object spies to have been called'
);
});
it(`throws an error if a non-object is passed`, function() {
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
expect(function() {
matcher.compare(true);
}).toThrowError(Error, /Expected a spy object, but got/);
expect(function() {
matcher.compare(123);
}).toThrowError(Error, /Expected a spy object, but got/);
expect(function() {
matcher.compare('string');
}).toThrowError(Error, /Expected a spy object, but got/);
});
it('throws an error if arguments are passed', function() {
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
expect(function() {
matcher.compare(spyObj, 'an argument');
}).toThrowError(Error, /Does not take arguments/);
});
it('throws an error if the spy object has no spies', function() {
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
const spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['notSpy']);
// Removing spy since spy objects cannot be created without spies.
spyObj.notSpy = function() {};
expect(function() {
matcher.compare(spyObj);
}).toThrowError(
Error,
/Expected a spy object with spies, but object has no spies/
);
});
});

View File

@@ -1,7 +0,0 @@
(function(env) {
env.skipBrowserFlake = function() {
pending(
'Skipping specs that are known to be flaky in browsers in this run'
);
};
})(jasmine.getEnv());

View File

@@ -41,7 +41,3 @@ module.exports = {
}
}
};
if (process.env.SKIP_JASMINE_BROWSER_FLAKES === 'true') {
module.exports.helpers.push('helpers/disableBrowserFlakes.js');
}

View File

@@ -1028,7 +1028,10 @@ getJasmineRequireObj().Env = function(j$) {
}
addSpecsToSuite(suite, specDefinitions);
if (suite.parentSuite && !suite.children.length) {
throw new Error('describe with no children (describe() or it())');
throw new Error(
'describe with no children (describe() or it()): ' +
suite.getFullName()
);
}
return suite.metadata;
};

View File

@@ -40,7 +40,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
this.emitScalar('<global>');
} else if (value.jasmineToString) {
this.emitScalar(value.jasmineToString(this.pp_));
} else if (typeof value === 'string') {
} else if (j$.isString_(value)) {
this.emitString(value);
} else if (j$.isSpy(value)) {
this.emitScalar('spy on ' + value.and.identity);

View File

@@ -316,7 +316,7 @@ getJasmineRequireObj().Spec = function(j$) {
boilerplateEnd =
boilerplateStart + Spec.pendingSpecExceptionMessage.length;
return fullMessage.substr(boilerplateEnd);
return fullMessage.slice(boilerplateEnd);
};
Spec.pendingSpecExceptionMessage = '=> marked Pending';

View File

@@ -525,17 +525,16 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
};
function keys(obj, isArray) {
var allKeys = Object.keys
? Object.keys(obj)
: (function(o) {
var keys = [];
for (var key in o) {
if (j$.util.has(o, key)) {
keys.push(key);
}
}
return keys;
})(obj);
var allKeys = (function(o) {
var keys = [];
for (var key in o) {
if (j$.util.has(o, key)) {
keys.push(key);
}
}
// eslint-disable-next-line compat/compat
return keys.concat(Object.getOwnPropertySymbols(o));
})(obj);
if (!isArray) {
return allKeys;

1
src/core/matchers/requireMatchers.js Normal file → Executable file
View File

@@ -27,6 +27,7 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
'toHaveBeenCalledTimes',
'toHaveBeenCalledWith',
'toHaveClass',
'toHaveSpyInteractions',
'toMatch',
'toThrow',
'toThrowError',

View File

@@ -27,6 +27,14 @@ getJasmineRequireObj().toBeCloseTo = function() {
);
}
// Infinity is close to Infinity and -Infinity is close to -Infinity,
// regardless of the precision.
if (expected === Infinity || expected === -Infinity) {
return {
pass: actual === expected
};
}
var pow = Math.pow(10, precision + 1);
var delta = Math.abs(expected - actual);
var maxDelta = Math.pow(10, -precision) / 2;

View File

@@ -0,0 +1,75 @@
getJasmineRequireObj().toHaveSpyInteractions = function(j$) {
var getErrorMsg = j$.formatErrorMsg(
'<toHaveSpyInteractions>',
'expect(<spyObj>).toHaveSpyInteractions()'
);
/**
* {@link expect} the actual (a {@link SpyObj}) spies to have been called.
* @function
* @name matchers#toHaveSpyInteractions
* @since 4.1.0
* @example
* expect(mySpyObj).toHaveSpyInteractions();
* expect(mySpyObj).not.toHaveSpyInteractions();
*/
function toHaveSpyInteractions(matchersUtil) {
return {
compare: function(actual) {
var result = {};
if (!j$.isObject_(actual)) {
throw new Error(
getErrorMsg('Expected a spy object, but got ' + typeof actual + '.')
);
}
if (arguments.length > 1) {
throw new Error(getErrorMsg('Does not take arguments'));
}
result.pass = false;
let hasSpy = false;
const calledSpies = [];
for (const spy of Object.values(actual)) {
if (!j$.isSpy(spy)) continue;
hasSpy = true;
if (spy.calls.any()) {
result.pass = true;
calledSpies.push([spy.and.identity, spy.calls.count()]);
}
}
if (!hasSpy) {
throw new Error(
getErrorMsg(
'Expected a spy object with spies, but object has no spies.'
)
);
}
let resultMessage;
if (result.pass) {
resultMessage =
'Expected spy object spies not to have been called, ' +
'but the following spies were called: ';
resultMessage += calledSpies
.map(([spyName, spyCount]) => {
return `${spyName} called ${spyCount} time(s)`;
})
.join(', ');
} else {
resultMessage =
'Expected spy object spies to have been called, ' +
'but no spies were called.';
}
result.message = resultMessage;
return result;
}
};
}
return toHaveSpyInteractions;
};

View File

@@ -54,6 +54,8 @@ getJasmineRequireObj().util = function(j$) {
// All falsey values are either primitives, `null`, or `undefined.
if (!argsAsArray[i] || str.match(primitives)) {
clonedArgs.push(argsAsArray[i]);
} else if (str === '[object Date]') {
clonedArgs.push(new Date(argsAsArray[i].valueOf()));
} else {
clonedArgs.push(j$.util.clone(argsAsArray[i]));
}

View File

@@ -71,9 +71,6 @@ body {
.jasmine-banner .jasmine-title {
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;