Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cd7d47f72 | ||
|
|
d0fe5c4712 | ||
|
|
f602c4911c | ||
|
|
7aaf7eaf30 | ||
|
|
35f16e8125 | ||
|
|
acc777c267 | ||
|
|
1c74356691 | ||
|
|
bbebea0fa5 | ||
|
|
66eb27b0af | ||
|
|
7a63c06a65 | ||
|
|
554dfd4923 | ||
|
|
36a6e2aa1d | ||
|
|
3c4b73f136 | ||
|
|
bc3ed74336 | ||
|
|
97b6f33cc2 | ||
|
|
a9889ddb31 |
@@ -11,6 +11,7 @@
|
|||||||
"ecmaVersion": 2018
|
"ecmaVersion": 2018
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"curly": "error",
|
||||||
"quotes": [
|
"quotes": [
|
||||||
"error",
|
"error",
|
||||||
"single",
|
"single",
|
||||||
|
|||||||
@@ -2430,7 +2430,9 @@ getJasmineRequireObj().MapContaining = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
|
MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
|
||||||
if (!j$.isMap(other)) return false;
|
if (!j$.isMap(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (const [key, value] of this.sample) {
|
for (const [key, value] of this.sample) {
|
||||||
// for each key/value pair in `sample`
|
// for each key/value pair in `sample`
|
||||||
@@ -2569,7 +2571,9 @@ getJasmineRequireObj().SetContaining = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
|
SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
|
||||||
if (!j$.isSet(other)) return false;
|
if (!j$.isSet(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (const item of this.sample) {
|
for (const item of this.sample) {
|
||||||
// for each item in `sample` there should be at least one matching item in `other`
|
// for each item in `sample` there should be at least one matching item in `other`
|
||||||
@@ -2876,7 +2880,8 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
const maxInlineCallCount = 10;
|
const maxInlineCallCount = 10;
|
||||||
|
|
||||||
function browserQueueMicrotaskImpl(global) {
|
function browserQueueMicrotaskImpl(global) {
|
||||||
const { setTimeout, queueMicrotask } = global;
|
const unclampedSetTimeout = getUnclampedSetTimeout(global);
|
||||||
|
const { queueMicrotask } = global;
|
||||||
let currentCallCount = 0;
|
let currentCallCount = 0;
|
||||||
return function clearStack(fn) {
|
return function clearStack(fn) {
|
||||||
currentCallCount++;
|
currentCallCount++;
|
||||||
@@ -2885,7 +2890,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
queueMicrotask(fn);
|
queueMicrotask(fn);
|
||||||
} else {
|
} else {
|
||||||
currentCallCount = 0;
|
currentCallCount = 0;
|
||||||
setTimeout(fn);
|
unclampedSetTimeout(fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -2899,6 +2904,37 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function messageChannelImpl(global) {
|
function messageChannelImpl(global) {
|
||||||
|
const { setTimeout } = global;
|
||||||
|
const postMessage = getPostMessage(global);
|
||||||
|
|
||||||
|
let currentCallCount = 0;
|
||||||
|
return function clearStack(fn) {
|
||||||
|
currentCallCount++;
|
||||||
|
|
||||||
|
if (currentCallCount < maxInlineCallCount) {
|
||||||
|
postMessage(fn);
|
||||||
|
} else {
|
||||||
|
currentCallCount = 0;
|
||||||
|
setTimeout(fn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnclampedSetTimeout(global) {
|
||||||
|
const { setTimeout } = global;
|
||||||
|
if (j$.util.isUndefined(global.MessageChannel)) {
|
||||||
|
return setTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
const postMessage = getPostMessage(global);
|
||||||
|
return function unclampedSetTimeout(fn) {
|
||||||
|
postMessage(function() {
|
||||||
|
setTimeout(fn);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPostMessage(global) {
|
||||||
const { MessageChannel, setTimeout } = global;
|
const { MessageChannel, setTimeout } = global;
|
||||||
const channel = new MessageChannel();
|
const channel = new MessageChannel();
|
||||||
let head = {};
|
let head = {};
|
||||||
@@ -2922,17 +2958,9 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let currentCallCount = 0;
|
return function postMessage(fn) {
|
||||||
return function clearStack(fn) {
|
tail = tail.next = { task: fn };
|
||||||
currentCallCount++;
|
channel.port2.postMessage(0);
|
||||||
|
|
||||||
if (currentCallCount < maxInlineCallCount) {
|
|
||||||
tail = tail.next = { task: fn };
|
|
||||||
channel.port2.postMessage(0);
|
|
||||||
} else {
|
|
||||||
currentCallCount = 0;
|
|
||||||
setTimeout(fn);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2942,20 +2970,25 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
global.process.versions &&
|
global.process.versions &&
|
||||||
typeof global.process.versions.node === 'string';
|
typeof global.process.versions.node === 'string';
|
||||||
|
|
||||||
const SAFARI =
|
// Windows builds of WebKit have a fairly generic user agent string when no application name is provided:
|
||||||
|
// e.g. "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko)"
|
||||||
|
const SAFARI_OR_WIN_WEBKIT =
|
||||||
global.navigator &&
|
global.navigator &&
|
||||||
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
|
/(^((?!chrome|android).)*safari)|(Win64; x64\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)$)/i.test(
|
||||||
|
global.navigator.userAgent
|
||||||
|
);
|
||||||
|
|
||||||
if (NODE_JS) {
|
if (NODE_JS) {
|
||||||
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||||
// so we avoid the overhead.
|
// so we avoid the overhead.
|
||||||
return nodeQueueMicrotaskImpl(global);
|
return nodeQueueMicrotaskImpl(global);
|
||||||
} else if (
|
} else if (
|
||||||
SAFARI ||
|
SAFARI_OR_WIN_WEBKIT ||
|
||||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||||
) {
|
) {
|
||||||
// queueMicrotask is dramatically faster than MessageChannel in Safari,
|
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
||||||
// at least through version 16.
|
// and other WebKit-based browsers, such as the one distributed by Playwright
|
||||||
|
// to test Safari-like behavior on Windows.
|
||||||
// Some of our own integration tests provide a mock queueMicrotask in all
|
// Some of our own integration tests provide a mock queueMicrotask in all
|
||||||
// environments because it's simpler to mock than MessageChannel.
|
// environments because it's simpler to mock than MessageChannel.
|
||||||
return browserQueueMicrotaskImpl(global);
|
return browserQueueMicrotaskImpl(global);
|
||||||
@@ -6650,7 +6683,10 @@ getJasmineRequireObj().toHaveSpyInteractions = function(j$) {
|
|||||||
let hasSpy = false;
|
let hasSpy = false;
|
||||||
const calledSpies = [];
|
const calledSpies = [];
|
||||||
for (const spy of Object.values(actual)) {
|
for (const spy of Object.values(actual)) {
|
||||||
if (!j$.isSpy(spy)) continue;
|
if (!j$.isSpy(spy)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
hasSpy = true;
|
hasSpy = true;
|
||||||
|
|
||||||
if (spy.calls.any()) {
|
if (spy.calls.any()) {
|
||||||
@@ -8051,6 +8087,32 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getJasmineRequireObj().reporterEvents = function() {
|
getJasmineRequireObj().reporterEvents = function() {
|
||||||
|
/**
|
||||||
|
* Used to tell Jasmine what optional or uncommonly implemented features
|
||||||
|
* the reporter supports. If not specified, the defaults described in
|
||||||
|
* {@link ReporterCapabilities} will apply.
|
||||||
|
* @name Reporter#reporterCapabilities
|
||||||
|
* @type ReporterCapabilities | undefined
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Used to tell Jasmine what optional or uncommonly implemented features
|
||||||
|
* the reporter supports.
|
||||||
|
* @interface ReporterCapabilities
|
||||||
|
* @see Reporter#reporterCapabilities
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Indicates whether the reporter supports parallel execution. Jasmine will
|
||||||
|
* not allow parallel execution unless all reporters that are in use set this
|
||||||
|
* capability to true.
|
||||||
|
* @name ReporterCapabilities#parallel
|
||||||
|
* @type boolean | undefined
|
||||||
|
* @default false
|
||||||
|
* @see running_specs_in_parallel
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
|
||||||
const events = [
|
const events = [
|
||||||
/**
|
/**
|
||||||
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
|
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
|
||||||
@@ -9449,6 +9511,16 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
|
|
||||||
obj[methodName] = spiedMethod;
|
obj[methodName] = spiedMethod;
|
||||||
|
|
||||||
|
// Check if setting the property actually worked. Some objects, such as
|
||||||
|
// localStorage in Firefox and later Safari versions, have no-op setters.
|
||||||
|
if (obj[methodName] !== spiedMethod) {
|
||||||
|
throw new Error(
|
||||||
|
j$.formatErrorMsg('<spyOn>')(
|
||||||
|
`Can't spy on ${methodName} because assigning to it had no effect`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return spiedMethod;
|
return spiedMethod;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -10910,5 +10982,5 @@ getJasmineRequireObj().UserContext = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getJasmineRequireObj().version = function() {
|
getJasmineRequireObj().version = function() {
|
||||||
return '5.2.0';
|
return '5.3.0';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jasmine-core",
|
"name": "jasmine-core",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "5.2.0",
|
"version": "5.3.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/jasmine/jasmine.git"
|
"url": "https://github.com/jasmine/jasmine.git"
|
||||||
|
|||||||
35
release_notes/5.3.0.md
Normal file
35
release_notes/5.3.0.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Jasmine Core 5.3.0 Release Notes
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
* Improved performance in Safari
|
||||||
|
* Merges [#2040](https://github.com/jasmine/jasmine/pull/2040) from @dcsaszar
|
||||||
|
* Fixes [#2008](https://github.com/jasmine/jasmine/issues/2008)
|
||||||
|
|
||||||
|
* Improved performance in Playwright Webkit on Windows
|
||||||
|
* Merges [#2034](https://github.com/jasmine/jasmine/pull/2034) from @m-akinc
|
||||||
|
|
||||||
|
* Throw if spying has no effect, as when spying on localStorage methods in Firefox and Safari 17
|
||||||
|
* See [#2036](https://github.com/jasmine/jasmine/issues/2036) and [#2007](https://github.com/jasmine/jasmine/issues/2007)
|
||||||
|
|
||||||
|
|
||||||
|
## Documentation improvements
|
||||||
|
|
||||||
|
* Added API reference for reporter capabilities
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 18, 20, 22 |
|
||||||
|
| Safari | 15-17 |
|
||||||
|
| Chrome | 128 |
|
||||||
|
| Firefox | 102, 115, 130 |
|
||||||
|
| Edge | 128 |
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
@@ -20,6 +20,47 @@ describe('ClearStack', function() {
|
|||||||
MessageChannel: fakeMessageChannel
|
MessageChannel: fakeMessageChannel
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses MessageChannel to reduce setTimeout clamping', function() {
|
||||||
|
const fakeChannel = fakeMessageChannel();
|
||||||
|
spyOn(fakeChannel.port2, 'postMessage');
|
||||||
|
const queueMicrotask = jasmine.createSpy('queueMicrotask');
|
||||||
|
const global = {
|
||||||
|
navigator: {
|
||||||
|
userAgent:
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.0.8 (KHTML, like Gecko) Version/15.1 Safari/605.0.8'
|
||||||
|
},
|
||||||
|
MessageChannel: function() {
|
||||||
|
return fakeChannel;
|
||||||
|
},
|
||||||
|
queueMicrotask
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
|
|
||||||
|
for (let i = 0; i < 9; i++) {
|
||||||
|
clearStack(function() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(fakeChannel.port2.postMessage).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
clearStack(function() {});
|
||||||
|
|
||||||
|
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("in WebKit (Playwright's build for Windows)", function() {
|
||||||
|
usesQueueMicrotaskWithSetTimeout(function() {
|
||||||
|
return {
|
||||||
|
navigator: {
|
||||||
|
userAgent:
|
||||||
|
'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko)'
|
||||||
|
},
|
||||||
|
// queueMicrotask should be used even though MessageChannel is present
|
||||||
|
MessageChannel: fakeMessageChannel
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('in browsers other than Safari', function() {
|
describe('in browsers other than Safari', function() {
|
||||||
|
|||||||
@@ -94,6 +94,30 @@ describe('SpyRegistry', function() {
|
|||||||
}).not.toThrowError(/is not declared writable or has no setter/);
|
}).not.toThrowError(/is not declared writable or has no setter/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throws if assigning to the property is a no-op', function() {
|
||||||
|
const scope = {};
|
||||||
|
|
||||||
|
function original() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(scope, 'myFunc', {
|
||||||
|
get() {
|
||||||
|
return original;
|
||||||
|
},
|
||||||
|
set() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const spyRegistry = new jasmineUnderTest.SpyRegistry({
|
||||||
|
createSpy: createSpy
|
||||||
|
});
|
||||||
|
expect(function() {
|
||||||
|
spyRegistry.spyOn(scope, 'myFunc');
|
||||||
|
}).toThrowError(
|
||||||
|
"<spyOn> : Can't spy on myFunc because assigning to it had no effect"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('overrides the method on the object and returns the spy', function() {
|
it('overrides the method on the object and returns the spy', function() {
|
||||||
const originalFunctionWasCalled = false,
|
const originalFunctionWasCalled = false,
|
||||||
spyRegistry = new jasmineUnderTest.SpyRegistry({
|
spyRegistry = new jasmineUnderTest.SpyRegistry({
|
||||||
|
|||||||
@@ -1315,8 +1315,9 @@ describe('Env integration', function() {
|
|||||||
'works with constructors when using callThrough spy strategy',
|
'works with constructors when using callThrough spy strategy',
|
||||||
function() {
|
function() {
|
||||||
function MyClass(foo) {
|
function MyClass(foo) {
|
||||||
if (!(this instanceof MyClass))
|
if (!(this instanceof MyClass)) {
|
||||||
throw new Error('You must use the new keyword.');
|
throw new Error('You must use the new keyword.');
|
||||||
|
}
|
||||||
this.foo = foo;
|
this.foo = foo;
|
||||||
}
|
}
|
||||||
const subject = { MyClass: MyClass };
|
const subject = { MyClass: MyClass };
|
||||||
|
|||||||
@@ -12,14 +12,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
function getSourceFiles() {
|
function getSourceFiles() {
|
||||||
const src_files = ['core/**/*.js', 'version.js'].map(function(file) {
|
const globs = ['../../src/core/**/*.js', '../../src/version.js'];
|
||||||
return path.join(__dirname, '../../', 'src/', file);
|
const srcFiles = globs.flatMap(g => glob.sync(g, { cwd: __dirname }));
|
||||||
});
|
|
||||||
|
|
||||||
const files = src_files.flatMap(g => glob.sync(g));
|
for (const file of srcFiles) {
|
||||||
files.forEach(function(resolvedFile) {
|
require(file);
|
||||||
require(resolvedFile);
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSourceFiles();
|
getSourceFiles();
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
const maxInlineCallCount = 10;
|
const maxInlineCallCount = 10;
|
||||||
|
|
||||||
function browserQueueMicrotaskImpl(global) {
|
function browserQueueMicrotaskImpl(global) {
|
||||||
const { setTimeout, queueMicrotask } = global;
|
const unclampedSetTimeout = getUnclampedSetTimeout(global);
|
||||||
|
const { queueMicrotask } = global;
|
||||||
let currentCallCount = 0;
|
let currentCallCount = 0;
|
||||||
return function clearStack(fn) {
|
return function clearStack(fn) {
|
||||||
currentCallCount++;
|
currentCallCount++;
|
||||||
@@ -11,7 +12,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
queueMicrotask(fn);
|
queueMicrotask(fn);
|
||||||
} else {
|
} else {
|
||||||
currentCallCount = 0;
|
currentCallCount = 0;
|
||||||
setTimeout(fn);
|
unclampedSetTimeout(fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -25,6 +26,37 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function messageChannelImpl(global) {
|
function messageChannelImpl(global) {
|
||||||
|
const { setTimeout } = global;
|
||||||
|
const postMessage = getPostMessage(global);
|
||||||
|
|
||||||
|
let currentCallCount = 0;
|
||||||
|
return function clearStack(fn) {
|
||||||
|
currentCallCount++;
|
||||||
|
|
||||||
|
if (currentCallCount < maxInlineCallCount) {
|
||||||
|
postMessage(fn);
|
||||||
|
} else {
|
||||||
|
currentCallCount = 0;
|
||||||
|
setTimeout(fn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnclampedSetTimeout(global) {
|
||||||
|
const { setTimeout } = global;
|
||||||
|
if (j$.util.isUndefined(global.MessageChannel)) {
|
||||||
|
return setTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
const postMessage = getPostMessage(global);
|
||||||
|
return function unclampedSetTimeout(fn) {
|
||||||
|
postMessage(function() {
|
||||||
|
setTimeout(fn);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPostMessage(global) {
|
||||||
const { MessageChannel, setTimeout } = global;
|
const { MessageChannel, setTimeout } = global;
|
||||||
const channel = new MessageChannel();
|
const channel = new MessageChannel();
|
||||||
let head = {};
|
let head = {};
|
||||||
@@ -48,17 +80,9 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let currentCallCount = 0;
|
return function postMessage(fn) {
|
||||||
return function clearStack(fn) {
|
tail = tail.next = { task: fn };
|
||||||
currentCallCount++;
|
channel.port2.postMessage(0);
|
||||||
|
|
||||||
if (currentCallCount < maxInlineCallCount) {
|
|
||||||
tail = tail.next = { task: fn };
|
|
||||||
channel.port2.postMessage(0);
|
|
||||||
} else {
|
|
||||||
currentCallCount = 0;
|
|
||||||
setTimeout(fn);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,20 +92,25 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
global.process.versions &&
|
global.process.versions &&
|
||||||
typeof global.process.versions.node === 'string';
|
typeof global.process.versions.node === 'string';
|
||||||
|
|
||||||
const SAFARI =
|
// Windows builds of WebKit have a fairly generic user agent string when no application name is provided:
|
||||||
|
// e.g. "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko)"
|
||||||
|
const SAFARI_OR_WIN_WEBKIT =
|
||||||
global.navigator &&
|
global.navigator &&
|
||||||
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
|
/(^((?!chrome|android).)*safari)|(Win64; x64\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)$)/i.test(
|
||||||
|
global.navigator.userAgent
|
||||||
|
);
|
||||||
|
|
||||||
if (NODE_JS) {
|
if (NODE_JS) {
|
||||||
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||||
// so we avoid the overhead.
|
// so we avoid the overhead.
|
||||||
return nodeQueueMicrotaskImpl(global);
|
return nodeQueueMicrotaskImpl(global);
|
||||||
} else if (
|
} else if (
|
||||||
SAFARI ||
|
SAFARI_OR_WIN_WEBKIT ||
|
||||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||||
) {
|
) {
|
||||||
// queueMicrotask is dramatically faster than MessageChannel in Safari,
|
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
||||||
// at least through version 16.
|
// and other WebKit-based browsers, such as the one distributed by Playwright
|
||||||
|
// to test Safari-like behavior on Windows.
|
||||||
// Some of our own integration tests provide a mock queueMicrotask in all
|
// Some of our own integration tests provide a mock queueMicrotask in all
|
||||||
// environments because it's simpler to mock than MessageChannel.
|
// environments because it's simpler to mock than MessageChannel.
|
||||||
return browserQueueMicrotaskImpl(global);
|
return browserQueueMicrotaskImpl(global);
|
||||||
|
|||||||
@@ -84,6 +84,16 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
|
|
||||||
obj[methodName] = spiedMethod;
|
obj[methodName] = spiedMethod;
|
||||||
|
|
||||||
|
// Check if setting the property actually worked. Some objects, such as
|
||||||
|
// localStorage in Firefox and later Safari versions, have no-op setters.
|
||||||
|
if (obj[methodName] !== spiedMethod) {
|
||||||
|
throw new Error(
|
||||||
|
j$.formatErrorMsg('<spyOn>')(
|
||||||
|
`Can't spy on ${methodName} because assigning to it had no effect`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return spiedMethod;
|
return spiedMethod;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ getJasmineRequireObj().MapContaining = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
|
MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
|
||||||
if (!j$.isMap(other)) return false;
|
if (!j$.isMap(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (const [key, value] of this.sample) {
|
for (const [key, value] of this.sample) {
|
||||||
// for each key/value pair in `sample`
|
// for each key/value pair in `sample`
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ getJasmineRequireObj().SetContaining = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
|
SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
|
||||||
if (!j$.isSet(other)) return false;
|
if (!j$.isSet(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (const item of this.sample) {
|
for (const item of this.sample) {
|
||||||
// for each item in `sample` there should be at least one matching item in `other`
|
// for each item in `sample` there should be at least one matching item in `other`
|
||||||
|
|||||||
@@ -32,7 +32,10 @@ getJasmineRequireObj().toHaveSpyInteractions = function(j$) {
|
|||||||
let hasSpy = false;
|
let hasSpy = false;
|
||||||
const calledSpies = [];
|
const calledSpies = [];
|
||||||
for (const spy of Object.values(actual)) {
|
for (const spy of Object.values(actual)) {
|
||||||
if (!j$.isSpy(spy)) continue;
|
if (!j$.isSpy(spy)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
hasSpy = true;
|
hasSpy = true;
|
||||||
|
|
||||||
if (spy.calls.any()) {
|
if (spy.calls.any()) {
|
||||||
|
|||||||
@@ -1,4 +1,30 @@
|
|||||||
getJasmineRequireObj().reporterEvents = function() {
|
getJasmineRequireObj().reporterEvents = function() {
|
||||||
|
/**
|
||||||
|
* Used to tell Jasmine what optional or uncommonly implemented features
|
||||||
|
* the reporter supports. If not specified, the defaults described in
|
||||||
|
* {@link ReporterCapabilities} will apply.
|
||||||
|
* @name Reporter#reporterCapabilities
|
||||||
|
* @type ReporterCapabilities | undefined
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Used to tell Jasmine what optional or uncommonly implemented features
|
||||||
|
* the reporter supports.
|
||||||
|
* @interface ReporterCapabilities
|
||||||
|
* @see Reporter#reporterCapabilities
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Indicates whether the reporter supports parallel execution. Jasmine will
|
||||||
|
* not allow parallel execution unless all reporters that are in use set this
|
||||||
|
* capability to true.
|
||||||
|
* @name ReporterCapabilities#parallel
|
||||||
|
* @type boolean | undefined
|
||||||
|
* @default false
|
||||||
|
* @see running_specs_in_parallel
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
|
||||||
const events = [
|
const events = [
|
||||||
/**
|
/**
|
||||||
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
|
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
|
||||||
|
|||||||
Reference in New Issue
Block a user