Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4be08b657 | ||
|
|
50ef882a1a | ||
|
|
c1cd5c6291 | ||
|
|
63ed2b3948 | ||
|
|
0183acc682 | ||
|
|
e15819c0dd | ||
|
|
f694194b2b | ||
|
|
94c00886a6 | ||
|
|
6a7c0e6368 |
@@ -4,6 +4,10 @@
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
node24:
|
||||
docker:
|
||||
- image: cimg/node:24.0.0
|
||||
working_directory: ~/workspace
|
||||
node22:
|
||||
docker:
|
||||
- image: cimg/node:22.0.0
|
||||
@@ -100,6 +104,9 @@ workflows:
|
||||
|
||||
push:
|
||||
jobs:
|
||||
- build:
|
||||
executor: node24
|
||||
name: build_node_24
|
||||
- build:
|
||||
executor: node22
|
||||
name: build_node_22
|
||||
@@ -125,10 +132,10 @@ workflows:
|
||||
requires:
|
||||
- build_node_18
|
||||
- test_parallel:
|
||||
executor: node18
|
||||
name: test_parallel_node_18
|
||||
executor: node24
|
||||
name: test_parallel_node_24
|
||||
requires:
|
||||
- build_node_18
|
||||
- build_node_24
|
||||
- test_parallel:
|
||||
executor: node22
|
||||
name: test_parallel_node_22
|
||||
@@ -139,6 +146,11 @@ workflows:
|
||||
name: test_parallel_node_20
|
||||
requires:
|
||||
- build_node_20
|
||||
- test_parallel:
|
||||
executor: node18
|
||||
name: test_parallel_node_18
|
||||
requires:
|
||||
- build_node_18
|
||||
- test_browsers:
|
||||
requires:
|
||||
- build_node_18
|
||||
|
||||
@@ -29,7 +29,7 @@ Microsoft Edge) as well as Node.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|----------------------------|
|
||||
| Node | 18, 20, 22 |
|
||||
| Node | 18, 20, 22, 24 |
|
||||
| Safari | 15*, 16*, 17* |
|
||||
| Chrome | Evergreen |
|
||||
| Firefox | Evergreen, 102*, 115*, 128 |
|
||||
|
||||
@@ -2803,7 +2803,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
||||
|
||||
this.track = function(context) {
|
||||
if (opts.cloneArgs) {
|
||||
context.args = j$.util.cloneArgs(context.args);
|
||||
context.args = opts.argsCloner(context.args);
|
||||
}
|
||||
calls.push(context);
|
||||
};
|
||||
@@ -2911,13 +2911,15 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Set this spy to do a shallow clone of arguments passed to each invocation.
|
||||
* Set this spy to do a clone of arguments passed to each invocation.
|
||||
* @name Spy#calls#saveArgumentsByValue
|
||||
* @since 2.5.0
|
||||
* @param {Function} [argsCloner] A function to use to clone the arguments. Defaults to a shallow cloning function.
|
||||
* @function
|
||||
*/
|
||||
this.saveArgumentsByValue = function() {
|
||||
this.saveArgumentsByValue = function(argsCloner = j$.util.cloneArgs) {
|
||||
opts.cloneArgs = true;
|
||||
opts.argsCloner = argsCloner;
|
||||
};
|
||||
|
||||
this.unverifiedCount = function() {
|
||||
@@ -3282,6 +3284,12 @@ callbacks to execute _before_ running the next one.
|
||||
//
|
||||
// @return {!Promise<undefined>}
|
||||
async function newMacrotask() {
|
||||
if (NODE_JS) {
|
||||
// setImmediate is generally faster than setTimeout in Node
|
||||
// https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout
|
||||
return new Promise(resolve => void setImmediate(resolve));
|
||||
}
|
||||
|
||||
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
||||
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
||||
@@ -4875,7 +4883,10 @@ getJasmineRequireObj().DiffBuilder = function(j$) {
|
||||
);
|
||||
|
||||
if (useCustom) {
|
||||
messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path));
|
||||
const prettyActual = actualCustom || this.prettyPrinter_(actual);
|
||||
const prettyExpected =
|
||||
expectedCustom || this.prettyPrinter_(expected);
|
||||
messages.push(wrapPrettyPrinted(prettyActual, prettyExpected, path));
|
||||
return false; // don't recurse further
|
||||
}
|
||||
|
||||
@@ -7752,7 +7763,11 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
||||
} else if (value instanceof RegExp) {
|
||||
this.emitScalar(value.toString());
|
||||
} else if (typeof value === 'function') {
|
||||
this.emitScalar('Function');
|
||||
if (value.name) {
|
||||
this.emitScalar(`Function '${value.name}'`);
|
||||
} else {
|
||||
this.emitScalar('Function');
|
||||
}
|
||||
} else if (j$.isDomNode(value)) {
|
||||
if (value.tagName) {
|
||||
this.emitDomElement(value);
|
||||
@@ -9668,7 +9683,7 @@ getJasmineRequireObj().Spy = function(j$) {
|
||||
"Spy '" +
|
||||
strategyArgs.name +
|
||||
"' received a call with arguments " +
|
||||
j$.basicPrettyPrinter_(Array.prototype.slice.call(args)) +
|
||||
matchersUtil.pp(Array.prototype.slice.call(args)) +
|
||||
' but all configured strategies specify other arguments.'
|
||||
);
|
||||
} else {
|
||||
@@ -11395,5 +11410,5 @@ getJasmineRequireObj().UserContext = function(j$) {
|
||||
};
|
||||
|
||||
getJasmineRequireObj().version = function() {
|
||||
return '5.7.1';
|
||||
return '5.8.0';
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jasmine-core",
|
||||
"license": "MIT",
|
||||
"version": "5.7.1",
|
||||
"version": "5.8.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jasmine/jasmine.git"
|
||||
|
||||
44
release_notes/5.8.0.md
Normal file
44
release_notes/5.8.0.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Jasmine Core 5.8.0 Release Notes
|
||||
|
||||
## New Features
|
||||
|
||||
* Allow passing a function to `saveArgumentsByValue` to customize how arguments
|
||||
are saved
|
||||
* Merges [#2062](https://github.com/jasmine/jasmine/pull/2062) from @evanwaslh
|
||||
* Fixes [#1886](https://github.com/jasmine/jasmine/issues/1886)
|
||||
* Use custom object formatters in spy strategy mismatch errors
|
||||
* Include function names in pretty printer output
|
||||
* Improve performance of autoTick in Node
|
||||
* Merges [#2058](https://github.com/jasmine/jasmine/pull/2058) from @atscott
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fix diff building when only one side has a custom object formatter
|
||||
* Fixes [#2061](https://github.com/jasmine/jasmine/issues/2061)
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
* Added Node 24 to supported environments
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|-------------------------|
|
||||
| Node | 18**, 20, 22, 24 |
|
||||
| Safari | 15**, 16**, 17** |
|
||||
| Chrome | 137* |
|
||||
| Firefox | 102**, 115**, 128, 139* |
|
||||
| Edge | 137* |
|
||||
|
||||
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
||||
version available at release time.<br>
|
||||
\** Supported on a best-effort basis. Support for these versions may be dropped
|
||||
if it becomes impractical, and bugs affecting only these versions may not be
|
||||
treated as release blockers.
|
||||
|
||||
|
||||
------
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
@@ -134,6 +134,42 @@ describe('CallTracker', function() {
|
||||
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
|
||||
});
|
||||
|
||||
it('allows object arguments to be deep cloned', function() {
|
||||
const callTracker = new jasmineUnderTest.CallTracker();
|
||||
callTracker.saveArgumentsByValue(args => JSON.parse(JSON.stringify(args)));
|
||||
|
||||
const objectArg = { foo: { bar: { baz: ['qux'] } } },
|
||||
arrayArg = ['foo', 'bar'];
|
||||
|
||||
callTracker.track({
|
||||
object: {},
|
||||
args: [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0]
|
||||
});
|
||||
|
||||
objectArg.foo.bar.baz.push('quux');
|
||||
|
||||
expect(callTracker.mostRecent().args[0]).not.toBe(objectArg);
|
||||
expect(callTracker.mostRecent().args[0]).not.toEqual(objectArg);
|
||||
expect(callTracker.mostRecent().args[0]).toEqual({
|
||||
foo: { bar: { baz: ['qux'] } }
|
||||
});
|
||||
expect(callTracker.mostRecent().args[1]).not.toBe(arrayArg);
|
||||
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
|
||||
});
|
||||
|
||||
it('can take any function to transform arguments when saving by value', function() {
|
||||
const callTracker = new jasmineUnderTest.CallTracker();
|
||||
callTracker.saveArgumentsByValue(JSON.stringify);
|
||||
|
||||
const objectArg = { foo: { bar: { baz: ['qux'] } } },
|
||||
arrayArg = ['foo', 'bar'],
|
||||
args = [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0];
|
||||
|
||||
callTracker.track({ object: {}, args });
|
||||
|
||||
expect(callTracker.mostRecent().args).toEqual(JSON.stringify(args));
|
||||
});
|
||||
|
||||
it('saves primitive arguments by value', function() {
|
||||
const callTracker = new jasmineUnderTest.CallTracker(),
|
||||
args = [undefined, null, false, '', /\s/, 0, 1.2, NaN];
|
||||
|
||||
@@ -718,6 +718,20 @@ describe('Clock (acceptance)', function() {
|
||||
clock.uninstall();
|
||||
});
|
||||
|
||||
it('flushes microtask queue between macrotasks', async () => {
|
||||
const log = [];
|
||||
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
|
||||
log.push(1);
|
||||
Promise.resolve().then(() => log.push(2));
|
||||
Promise.resolve().then(() => log.push(3));
|
||||
});
|
||||
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
|
||||
log.push(4);
|
||||
Promise.resolve().then(() => log.push(5));
|
||||
});
|
||||
expect(log).toEqual([1, 2, 3, 4, 5]);
|
||||
});
|
||||
|
||||
it('can run setTimeouts/setIntervals asynchronously', function() {
|
||||
const recurring = jasmine.createSpy('recurring'),
|
||||
fn1 = jasmine.createSpy('fn1'),
|
||||
|
||||
@@ -164,7 +164,7 @@ describe('PrettyPrinter', function() {
|
||||
"Object({ foo: 'bar', baz: 3, nullValue: null, undefinedValue: undefined })"
|
||||
);
|
||||
expect(pp({ foo: function() {}, bar: [1, 2, 3] })).toEqual(
|
||||
'Object({ foo: Function, bar: [ 1, 2, 3 ] })'
|
||||
"Object({ foo: Function 'foo', bar: [ 1, 2, 3 ] })"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -450,7 +450,7 @@ describe('PrettyPrinter', function() {
|
||||
};
|
||||
|
||||
expect(pp(objFromOtherContext)).toEqual(
|
||||
"Object({ foo: 'bar', toString: Function })"
|
||||
"Object({ foo: 'bar', toString: Function 'toString' })"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -477,6 +477,17 @@ describe('PrettyPrinter', function() {
|
||||
expect(pp(a)).toEqual('<anonymous>({ })');
|
||||
});
|
||||
|
||||
it('stringifies functions with names', function() {
|
||||
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||
expect(pp(foo)).toEqual("Function 'foo'");
|
||||
function foo() {}
|
||||
});
|
||||
|
||||
it('stringifies functions without names', function() {
|
||||
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||
expect(pp(function() {})).toEqual('Function');
|
||||
});
|
||||
|
||||
it('should handle objects with null prototype', function() {
|
||||
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||
const obj = Object.create(null);
|
||||
|
||||
@@ -3855,6 +3855,35 @@ describe('Env integration', function() {
|
||||
expect(failedExpectations).toEqual([]);
|
||||
});
|
||||
|
||||
it('uses custom object formatters in spy strategy argument mismatch errors', async function() {
|
||||
env.it('a spec', function() {
|
||||
env.addCustomObjectFormatter(function(value) {
|
||||
if (typeof value === 'string') {
|
||||
return 'custom:' + value;
|
||||
}
|
||||
});
|
||||
const spy = env
|
||||
.createSpy('foo')
|
||||
.withArgs('x')
|
||||
.and.returnValue('');
|
||||
spy('y');
|
||||
});
|
||||
|
||||
let failedExpectations;
|
||||
env.addReporter({
|
||||
specDone: r => (failedExpectations = r.failedExpectations)
|
||||
});
|
||||
|
||||
await env.execute();
|
||||
expect(failedExpectations).toEqual([
|
||||
jasmine.objectContaining({
|
||||
message: jasmine.stringContaining(
|
||||
'received a call with arguments [ custom:y ]'
|
||||
)
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
describe('#spyOnGlobalErrorsAsync', function() {
|
||||
const leftInstalledMessage =
|
||||
'Global error spy was not uninstalled. ' +
|
||||
|
||||
@@ -161,6 +161,63 @@ describe('DiffBuilder', function() {
|
||||
expect(diffBuilder.getMessage()).toEqual(expectedMsg);
|
||||
});
|
||||
|
||||
it('handles cases where only the expected has a custom object formatter', function() {
|
||||
const formatter = function(x) {
|
||||
if (typeof x === 'number') {
|
||||
return '[number:' + x + ']';
|
||||
}
|
||||
};
|
||||
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
|
||||
const diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||
prettyPrinter: prettyPrinter
|
||||
});
|
||||
|
||||
diffBuilder.setRoots('five', 4);
|
||||
diffBuilder.recordMismatch();
|
||||
|
||||
expect(diffBuilder.getMessage()).toEqual(
|
||||
"Expected 'five' to equal [number:4]."
|
||||
);
|
||||
});
|
||||
|
||||
it('handles cases where only the actual has a custom object formatter', function() {
|
||||
const formatter = function(x) {
|
||||
if (typeof x === 'number') {
|
||||
return '[number:' + x + ']';
|
||||
}
|
||||
};
|
||||
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
|
||||
const diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||
prettyPrinter: prettyPrinter
|
||||
});
|
||||
|
||||
diffBuilder.setRoots(5, 'four');
|
||||
diffBuilder.recordMismatch();
|
||||
|
||||
expect(diffBuilder.getMessage()).toEqual(
|
||||
"Expected [number:5] to equal 'four'."
|
||||
);
|
||||
});
|
||||
|
||||
it('handles complex cases where only one side has a custom object formatter', function() {
|
||||
const formatter = function(x) {
|
||||
if (typeof x === 'number') {
|
||||
return '[number:' + x + ']';
|
||||
}
|
||||
};
|
||||
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
|
||||
const diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||
prettyPrinter: prettyPrinter
|
||||
});
|
||||
|
||||
diffBuilder.setRoots(5, { foo: 'bar', fnord: { graults: ['wombat'] } });
|
||||
diffBuilder.recordMismatch();
|
||||
|
||||
expect(diffBuilder.getMessage()).toEqual(
|
||||
"Expected [number:5] to equal Object({ foo: 'bar', fnord: Object({ graults: [ 'wombat' ] }) })."
|
||||
);
|
||||
});
|
||||
|
||||
it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ at the root', function() {
|
||||
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([]),
|
||||
diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||
|
||||
@@ -458,9 +458,9 @@ describe('toEqual', function() {
|
||||
});
|
||||
|
||||
it('reports mismatches between Functions', function() {
|
||||
const actual = { x: function() {} },
|
||||
expected = { x: function() {} },
|
||||
message = 'Expected $.x = Function to equal Function.';
|
||||
const actual = { x: function() {} };
|
||||
const expected = { x: function() {} };
|
||||
const message = "Expected $.x = Function 'x' to equal Function 'x'.";
|
||||
|
||||
expect(compareEquals(actual, expected).message).toEqual(message);
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
||||
|
||||
this.track = function(context) {
|
||||
if (opts.cloneArgs) {
|
||||
context.args = j$.util.cloneArgs(context.args);
|
||||
context.args = opts.argsCloner(context.args);
|
||||
}
|
||||
calls.push(context);
|
||||
};
|
||||
@@ -117,13 +117,15 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Set this spy to do a shallow clone of arguments passed to each invocation.
|
||||
* Set this spy to do a clone of arguments passed to each invocation.
|
||||
* @name Spy#calls#saveArgumentsByValue
|
||||
* @since 2.5.0
|
||||
* @param {Function} [argsCloner] A function to use to clone the arguments. Defaults to a shallow cloning function.
|
||||
* @function
|
||||
*/
|
||||
this.saveArgumentsByValue = function() {
|
||||
this.saveArgumentsByValue = function(argsCloner = j$.util.cloneArgs) {
|
||||
opts.cloneArgs = true;
|
||||
opts.argsCloner = argsCloner;
|
||||
};
|
||||
|
||||
this.unverifiedCount = function() {
|
||||
|
||||
@@ -226,6 +226,12 @@ callbacks to execute _before_ running the next one.
|
||||
//
|
||||
// @return {!Promise<undefined>}
|
||||
async function newMacrotask() {
|
||||
if (NODE_JS) {
|
||||
// setImmediate is generally faster than setTimeout in Node
|
||||
// https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout
|
||||
return new Promise(resolve => void setImmediate(resolve));
|
||||
}
|
||||
|
||||
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
||||
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
||||
|
||||
@@ -35,7 +35,11 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
||||
} else if (value instanceof RegExp) {
|
||||
this.emitScalar(value.toString());
|
||||
} else if (typeof value === 'function') {
|
||||
this.emitScalar('Function');
|
||||
if (value.name) {
|
||||
this.emitScalar(`Function '${value.name}'`);
|
||||
} else {
|
||||
this.emitScalar('Function');
|
||||
}
|
||||
} else if (j$.isDomNode(value)) {
|
||||
if (value.tagName) {
|
||||
this.emitDomElement(value);
|
||||
|
||||
@@ -161,7 +161,7 @@ getJasmineRequireObj().Spy = function(j$) {
|
||||
"Spy '" +
|
||||
strategyArgs.name +
|
||||
"' received a call with arguments " +
|
||||
j$.basicPrettyPrinter_(Array.prototype.slice.call(args)) +
|
||||
matchersUtil.pp(Array.prototype.slice.call(args)) +
|
||||
' but all configured strategies specify other arguments.'
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -37,7 +37,10 @@ getJasmineRequireObj().DiffBuilder = function(j$) {
|
||||
);
|
||||
|
||||
if (useCustom) {
|
||||
messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path));
|
||||
const prettyActual = actualCustom || this.prettyPrinter_(actual);
|
||||
const prettyExpected =
|
||||
expectedCustom || this.prettyPrinter_(expected);
|
||||
messages.push(wrapPrettyPrinted(prettyActual, prettyExpected, path));
|
||||
return false; // don't recurse further
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user