Compare commits

...

24 Commits

Author SHA1 Message Date
Steve Gravrock
c8c3325b56 Bump version to 5.4.0 2024-10-12 10:31:35 -07:00
Steve Gravrock
84c7e2b21b Fixed de-duplication of exception messages containing blank lines on Node and Chrome
This is particularly helpful when reporting testing-library errors, which
have messages that contain blank lines and can be hundreds or even thousands
of lines long.
2024-10-07 20:04:07 -07:00
Steve Gravrock
dda25bb29e Removed references to PhantomJS from StackTraceSpec.js 2024-10-07 19:55:34 -07:00
Steve Gravrock
9ccf2ef96b Also deprecate the expected and actual properties of ThrowUnlessFailure 2024-10-05 13:55:49 -07:00
Steve Gravrock
c6fa55bfc8 Clarify support status of old Firefox ESRs 2024-10-05 13:46:49 -07:00
Steve Gravrock
06bcf1c2e1 Fixed broken docs link 2024-10-05 13:38:52 -07:00
Steve Gravrock
40f402d117 Deprecate the expected and actual properties of expectation results 2024-10-04 07:54:20 -07:00
Steve Gravrock
71f6a95ce5 Added Firefox 128 (current ESR) to supported browsers 2024-09-24 06:54:36 -07:00
Steve Gravrock
5cd7d47f72 Bump version to 5.3.0 2024-09-07 13:18:21 -07:00
Steve Gravrock
d0fe5c4712 Require curly braces around loop and conditonal bodies 2024-09-02 11:30:36 -07:00
Steve Gravrock
f602c4911c Merge branch 'dave-unclamp-safari' of https://github.com/dcsaszar/jasmine
* Significantly improves performance in Safari
* Merges #2040 from @dcsaszar
* Fixes #2008
2024-09-02 11:22:56 -07:00
Steve Gravrock
7aaf7eaf30 API reference for reporter capabilities 2024-09-02 10:53:56 -07:00
David Császár
35f16e8125 Add test for unclamping setTimeout in Safari 2024-09-01 10:22:16 +02:00
David Császár
acc777c267 Unclamp setTimeout in Safari
Fixes #2008

wrapping setTimeout in postMessage is a trade-off between
* slowdown due to postMessage (slow on Safari)
* speedup due to no setTimeout clamping (can be severe on Safari)
2024-09-01 10:21:57 +02:00
David Császár
1c74356691 Add unclampedSetTimeout helper 2024-09-01 09:54:55 +02:00
David Császár
bbebea0fa5 Refactor: extract postMessage 2024-09-01 09:31:52 +02:00
Steve Gravrock
66eb27b0af Throw if spying has no effect
This provides a useful diagnostic in cases where assigning to a property
is a no-op, like localStorage in Firefox and Safari 17.

See #2036 and #2007.
2024-08-17 09:07:18 -07:00
Steve Gravrock
7a63c06a65 Merge branch 'webkit-performance' of https://github.com/m-akinc/jasmine
* Merges #2034 from @m-akinc
2024-08-10 07:26:00 -07:00
Mert Akinc
554dfd4923 Update comments as requested 2024-08-05 12:27:11 -05:00
Mert Akinc
36a6e2aa1d Merge branch 'main' into webkit-performance 2024-08-05 12:19:53 -05:00
Steve Gravrock
3c4b73f136 Fixed globbing in own test suite when running on Windows outside of c:\Users
The previous code used path.join() to construct glob input. That should't
work, but it did as long as the working directory was under c:\Users.
2024-08-03 18:06:20 -07:00
Mert Akinc
bc3ed74336 Formatting fix 2024-07-26 17:54:04 -05:00
Mert Akinc
97b6f33cc2 Add test, update rexex pattern and constant name 2024-07-26 17:45:23 -05:00
Mert Akinc
a9889ddb31 Improve performance on Playwright Windows WebKit 2024-07-25 09:54:32 -05:00
24 changed files with 437 additions and 124 deletions

View File

@@ -11,6 +11,7 @@
"ecmaVersion": 2018
},
"rules": {
"curly": "error",
"quotes": [
"error",
"single",

View File

@@ -27,18 +27,22 @@ for information on writing specs, and [the FAQ](https://jasmine.github.io/pages/
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
Microsoft Edge) as well as Node.
| Environment | Supported versions |
|-------------------|---------------------|
| Node | 18, 20, 22 |
| Safari | 15-17 |
| Chrome | Evergreen |
| Firefox | Evergreen, 102, 115 |
| Edge | Evergreen |
| Environment | Supported versions |
|-------------------|----------------------------|
| Node | 18, 20, 22 |
| Safari | 15-17 |
| Chrome | Evergreen |
| Firefox | Evergreen, 102*, 115*, 128 |
| Edge | Evergreen |
For evergreen browsers, each version of Jasmine is tested against the version of the browser that is available to us
at the time of release. Other browsers, as well as older & newer versions of some supported browsers, are likely to work.
However, Jasmine isn't tested against them and they aren't actively supported.
\* Environments that are past end of life are supported on a best-effort basis.
They may be dropped in a future minor release of Jasmine if continued support
becomes impractical.
To find out what environments work with a particular Jasmine release, see the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes).
## Maintainers

View File

@@ -915,9 +915,9 @@ getJasmineRequireObj().Spec = function(j$) {
* @property {String} fullName - The full description including all ancestors of this spec.
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
* @property {String} filename - The name of the file the spec was defined in.
* @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec.
* @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
* @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {ExpectationResult[]} 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.
@@ -1429,12 +1429,19 @@ getJasmineRequireObj().Env = function(j$) {
* @extends Error
* @description Represents a failure of an expectation evaluated with
* {@link throwUnless}. Properties of this error are a subset of the
* properties of {@link Expectation} and have the same values.
* properties of {@link ExpectationResult} and have the same values.
*
* Note: The expected and actual properties are deprecated and may be removed
* in a future release. In many Jasmine configurations they are passed
* through JSON serialization and deserialization, which is inherently
* lossy. In such cases, the expected and actual values may be placeholders
* or approximations of the original objects.
*
* @property {String} matcherName - The name of the matcher that was executed for this expectation.
* @property {String} message - The failure message for the expectation.
* @property {Boolean} passed - Whether the expectation passed or failed.
* @property {Object} expected - If the expectation failed, what was the expected value.
* @property {Object} actual - If the expectation failed, what actual value was produced.
* @property {Object} expected - Deprecated. If the expectation failed, what was the expected value.
* @property {Object} actual - Deprecated. If the expectation failed, what actual value was produced.
*/
const error = new Error(result.message);
error.passed = result.passed;
@@ -2430,7 +2437,9 @@ getJasmineRequireObj().MapContaining = function(j$) {
}
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 each key/value pair in `sample`
@@ -2569,7 +2578,9 @@ getJasmineRequireObj().SetContaining = function(j$) {
}
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 each item in `sample` there should be at least one matching item in `other`
@@ -2663,13 +2674,21 @@ getJasmineRequireObj().buildExpectationResult = function(j$) {
const exceptionFormatter = new j$.ExceptionFormatter();
/**
* @typedef Expectation
* Describes the result of evaluating an expectation
*
* Note: The expected and actual properties are deprecated and may be removed
* in a future release. In many Jasmine configurations they are passed
* through JSON serialization and deserialization, which is inherently
* lossy. In such cases, the expected and actual values may be placeholders
* or approximations of the original objects.
*
* @typedef ExpectationResult
* @property {String} matcherName - The name of the matcher that was executed for this expectation.
* @property {String} message - The failure message for the expectation.
* @property {String} stack - The stack trace for the failure if available.
* @property {Boolean} passed - Whether the expectation passed or failed.
* @property {Object} expected - If the expectation failed, what was the expected value.
* @property {Object} actual - If the expectation failed, what actual value was produced.
* @property {Object} expected - Deprecated. If the expectation failed, what was the expected value.
* @property {Object} actual - Deprecated. If the expectation failed, what actual value was produced.
* @property {String|undefined} globalErrorType - The type of an error that
* is reported on the top suite. Valid values are undefined, "afterAll",
* "load", "lateExpectation", and "lateError".
@@ -2876,7 +2895,8 @@ getJasmineRequireObj().clearStack = function(j$) {
const maxInlineCallCount = 10;
function browserQueueMicrotaskImpl(global) {
const { setTimeout, queueMicrotask } = global;
const unclampedSetTimeout = getUnclampedSetTimeout(global);
const { queueMicrotask } = global;
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
@@ -2885,7 +2905,7 @@ getJasmineRequireObj().clearStack = function(j$) {
queueMicrotask(fn);
} else {
currentCallCount = 0;
setTimeout(fn);
unclampedSetTimeout(fn);
}
};
}
@@ -2899,6 +2919,37 @@ getJasmineRequireObj().clearStack = function(j$) {
}
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 channel = new MessageChannel();
let head = {};
@@ -2922,17 +2973,9 @@ getJasmineRequireObj().clearStack = function(j$) {
}
};
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
} else {
currentCallCount = 0;
setTimeout(fn);
}
return function postMessage(fn) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
};
}
@@ -2942,20 +2985,25 @@ getJasmineRequireObj().clearStack = function(j$) {
global.process.versions &&
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 &&
/^((?!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) {
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
// so we avoid the overhead.
return nodeQueueMicrotaskImpl(global);
} else if (
SAFARI ||
SAFARI_OR_WIN_WEBKIT ||
j$.util.isUndefined(global.MessageChannel) /* tests */
) {
// queueMicrotask is dramatically faster than MessageChannel in Safari,
// at least through version 16.
// queueMicrotask is dramatically faster than MessageChannel in Safari
// 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
// environments because it's simpler to mock than MessageChannel.
return browserQueueMicrotaskImpl(global);
@@ -6650,7 +6698,10 @@ getJasmineRequireObj().toHaveSpyInteractions = function(j$) {
let hasSpy = false;
const calledSpies = [];
for (const spy of Object.values(actual)) {
if (!j$.isSpy(spy)) continue;
if (!j$.isSpy(spy)) {
continue;
}
hasSpy = true;
if (spy.calls.any()) {
@@ -8051,6 +8102,32 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
};
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 = [
/**
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
@@ -8967,8 +9044,8 @@ getJasmineRequireObj().Runner = function(j$) {
* @property {String} incompleteCode - Machine-readable explanation of why the suite was incomplete: 'focused', 'noSpecsFound', or undefined.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite. Note that this property is not present when Jasmine is run in parallel mode.
* @property {Int} numWorkers - Number of parallel workers. Note that this property is only present when Jasmine is run in parallel mode.
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
* @property {ExpectationResult[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
* @property {ExpectationResult[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
* @since 2.4.0
*/
const jasmineDoneInfo = {
@@ -9449,6 +9526,16 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
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;
};
@@ -9802,9 +9889,7 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
getJasmineRequireObj().StackTrace = function(j$) {
function StackTrace(error) {
let lines = error.stack.split('\n').filter(function(line) {
return line !== '';
});
let lines = error.stack.split('\n');
const extractResult = extractMessage(error.message, lines);
@@ -9813,6 +9898,10 @@ getJasmineRequireObj().StackTrace = function(j$) {
lines = extractResult.remainder;
}
lines = lines.filter(function(line) {
return line !== '';
});
const parseResult = tryParseFrames(lines);
this.frames = parseResult.frames;
this.style = parseResult.style;
@@ -10037,8 +10126,8 @@ getJasmineRequireObj().Suite = function(j$) {
* @property {String} fullName - The full description including all ancestors of this suite.
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
* @property {String} filename - The name of the file the suite was defined in.
* @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 {ExpectationResult[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
* @property {ExpectationResult[]} 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.
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty}
@@ -10910,5 +10999,5 @@ getJasmineRequireObj().UserContext = function(j$) {
};
getJasmineRequireObj().version = function() {
return '5.2.0';
return '5.4.0';
};

View File

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

35
release_notes/5.3.0.md Normal file
View 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)_

39
release_notes/5.4.0.md Normal file
View File

@@ -0,0 +1,39 @@
# Jasmine Core 5.4.0 Release Notes
## Changes
* Fixed de-duplication of exception messages containing blank lines on Node and Chrome
This is particularly helpful when reporting testing-library errors, which
have messages that contain blank lines and can be hundreds or even thousands
of lines long.
* Document that the expected and actual properties of expectation results are deprecated
The values of these properties are not reliable in configurations where
reporter messages are JSON serialized. They appear to have been seldom if ever
used. They will be removed in the next major release.
* Added Firefox 128 (current ESR) to supported browsers
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|-------------------------|
| Node | 18, 20, 22 |
| Safari | 15-17 |
| Chrome | 129* |
| Firefox | 102**, 115**, 128, 131* |
| Edge | 129* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Environments that are past end of life are supported on a best-effort basis.
They may be dropped in a future minor release of Jasmine if continued support
becomes impractical.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -36,6 +36,7 @@ failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
run_browser chrome latest "macOS 12"
run_browser firefox latest
run_browser firefox 128
run_browser firefox 115
run_browser firefox 102
run_browser safari 17

View File

@@ -20,6 +20,47 @@ describe('ClearStack', function() {
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() {

View File

@@ -94,6 +94,30 @@ describe('SpyRegistry', function() {
}).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() {
const originalFunctionWasCalled = false,
spyRegistry = new jasmineUnderTest.SpyRegistry({

View File

@@ -51,6 +51,27 @@ describe('StackTrace', function() {
]);
});
it('understands Chrome/Edge style traces with messages containing blank lines', function() {
const error = {
message: 'line 1\n\nline 2',
stack:
'Error: line 1\n\nline 2\n' +
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toEqual('Error: line 1\n\nline 2');
const rawFrames = result.frames.map(function(f) {
return f.raw;
});
expect(rawFrames).toEqual([
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
]);
});
it('understands Node style traces', function() {
const error = {
message: 'nope',
@@ -95,7 +116,7 @@ describe('StackTrace', function() {
]);
});
it('understands Safari <=14/Firefox/Phantom-OS X style traces', function() {
it('understands Safari <=14/Firefox style traces', function() {
const error = {
message: 'nope',
stack:
@@ -149,7 +170,7 @@ describe('StackTrace', function() {
]);
});
it('does not mistake gibberish for Safari/Firefox/Phantom-OS X style traces', function() {
it('does not mistake gibberish for Safari/Firefox style traces', function() {
const error = {
message: 'nope',
stack: 'randomcharsnotincludingwhitespace'
@@ -159,36 +180,6 @@ describe('StackTrace', function() {
expect(result.frames).toEqual([{ raw: error.stack }]);
});
it('understands Phantom-Linux style traces', function() {
const error = {
message: 'nope',
stack:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toBeFalsy();
expect(result.style).toEqual('v8');
expect(result.frames).toEqual([
{
raw:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
func: 'UserContext.<anonymous>',
file: 'http://localhost:8888/__spec__/core/UtilSpec.js',
line: 115
},
{
raw:
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)',
func: 'QueueRunner.run',
file: 'http://localhost:8888/__jasmine__/jasmine.js',
line: 4320
}
]);
});
it('ignores blank lines', function() {
const error = {
message: 'nope',
@@ -241,7 +232,7 @@ describe('StackTrace', function() {
]);
});
it('consideres different types of errors', function() {
it('considers different types of errors', function() {
const error = {
message: 'nope',
stack:

View File

@@ -1315,8 +1315,9 @@ describe('Env integration', function() {
'works with constructors when using callThrough spy strategy',
function() {
function MyClass(foo) {
if (!(this instanceof MyClass))
if (!(this instanceof MyClass)) {
throw new Error('You must use the new keyword.');
}
this.foo = foo;
}
const subject = { MyClass: MyClass };

View File

@@ -12,14 +12,12 @@
};
function getSourceFiles() {
const src_files = ['core/**/*.js', 'version.js'].map(function(file) {
return path.join(__dirname, '../../', 'src/', file);
});
const globs = ['../../src/core/**/*.js', '../../src/version.js'];
const srcFiles = globs.flatMap(g => glob.sync(g, { cwd: __dirname }));
const files = src_files.flatMap(g => glob.sync(g));
files.forEach(function(resolvedFile) {
require(resolvedFile);
});
for (const file of srcFiles) {
require(file);
}
}
getSourceFiles();

View File

@@ -2,7 +2,8 @@ getJasmineRequireObj().clearStack = function(j$) {
const maxInlineCallCount = 10;
function browserQueueMicrotaskImpl(global) {
const { setTimeout, queueMicrotask } = global;
const unclampedSetTimeout = getUnclampedSetTimeout(global);
const { queueMicrotask } = global;
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
@@ -11,7 +12,7 @@ getJasmineRequireObj().clearStack = function(j$) {
queueMicrotask(fn);
} else {
currentCallCount = 0;
setTimeout(fn);
unclampedSetTimeout(fn);
}
};
}
@@ -25,6 +26,37 @@ getJasmineRequireObj().clearStack = function(j$) {
}
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 channel = new MessageChannel();
let head = {};
@@ -48,17 +80,9 @@ getJasmineRequireObj().clearStack = function(j$) {
}
};
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
} else {
currentCallCount = 0;
setTimeout(fn);
}
return function postMessage(fn) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
};
}
@@ -68,20 +92,25 @@ getJasmineRequireObj().clearStack = function(j$) {
global.process.versions &&
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 &&
/^((?!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) {
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
// so we avoid the overhead.
return nodeQueueMicrotaskImpl(global);
} else if (
SAFARI ||
SAFARI_OR_WIN_WEBKIT ||
j$.util.isUndefined(global.MessageChannel) /* tests */
) {
// queueMicrotask is dramatically faster than MessageChannel in Safari,
// at least through version 16.
// queueMicrotask is dramatically faster than MessageChannel in Safari
// 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
// environments because it's simpler to mock than MessageChannel.
return browserQueueMicrotaskImpl(global);

View File

@@ -272,12 +272,19 @@ getJasmineRequireObj().Env = function(j$) {
* @extends Error
* @description Represents a failure of an expectation evaluated with
* {@link throwUnless}. Properties of this error are a subset of the
* properties of {@link Expectation} and have the same values.
* properties of {@link ExpectationResult} and have the same values.
*
* Note: The expected and actual properties are deprecated and may be removed
* in a future release. In many Jasmine configurations they are passed
* through JSON serialization and deserialization, which is inherently
* lossy. In such cases, the expected and actual values may be placeholders
* or approximations of the original objects.
*
* @property {String} matcherName - The name of the matcher that was executed for this expectation.
* @property {String} message - The failure message for the expectation.
* @property {Boolean} passed - Whether the expectation passed or failed.
* @property {Object} expected - If the expectation failed, what was the expected value.
* @property {Object} actual - If the expectation failed, what actual value was produced.
* @property {Object} expected - Deprecated. If the expectation failed, what was the expected value.
* @property {Object} actual - Deprecated. If the expectation failed, what actual value was produced.
*/
const error = new Error(result.message);
error.passed = result.passed;

View File

@@ -177,8 +177,8 @@ getJasmineRequireObj().Runner = function(j$) {
* @property {String} incompleteCode - Machine-readable explanation of why the suite was incomplete: 'focused', 'noSpecsFound', or undefined.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite. Note that this property is not present when Jasmine is run in parallel mode.
* @property {Int} numWorkers - Number of parallel workers. Note that this property is only present when Jasmine is run in parallel mode.
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
* @property {ExpectationResult[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
* @property {ExpectationResult[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
* @since 2.4.0
*/
const jasmineDoneInfo = {

View File

@@ -148,9 +148,9 @@ getJasmineRequireObj().Spec = function(j$) {
* @property {String} fullName - The full description including all ancestors of this spec.
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
* @property {String} filename - The name of the file the spec was defined in.
* @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec.
* @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
* @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {ExpectationResult[]} 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.

View File

@@ -84,6 +84,16 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
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;
};

View File

@@ -1,8 +1,6 @@
getJasmineRequireObj().StackTrace = function(j$) {
function StackTrace(error) {
let lines = error.stack.split('\n').filter(function(line) {
return line !== '';
});
let lines = error.stack.split('\n');
const extractResult = extractMessage(error.message, lines);
@@ -11,6 +9,10 @@ getJasmineRequireObj().StackTrace = function(j$) {
lines = extractResult.remainder;
}
lines = lines.filter(function(line) {
return line !== '';
});
const parseResult = tryParseFrames(lines);
this.frames = parseResult.frames;
this.style = parseResult.style;

View File

@@ -105,8 +105,8 @@ getJasmineRequireObj().Suite = function(j$) {
* @property {String} fullName - The full description including all ancestors of this suite.
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
* @property {String} filename - The name of the file the suite was defined in.
* @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 {ExpectationResult[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
* @property {ExpectationResult[]} 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.
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty}

View File

@@ -11,7 +11,9 @@ getJasmineRequireObj().MapContaining = function(j$) {
}
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 each key/value pair in `sample`

View File

@@ -11,7 +11,9 @@ getJasmineRequireObj().SetContaining = function(j$) {
}
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 each item in `sample` there should be at least one matching item in `other`

View File

@@ -4,13 +4,21 @@ getJasmineRequireObj().buildExpectationResult = function(j$) {
const exceptionFormatter = new j$.ExceptionFormatter();
/**
* @typedef Expectation
* Describes the result of evaluating an expectation
*
* Note: The expected and actual properties are deprecated and may be removed
* in a future release. In many Jasmine configurations they are passed
* through JSON serialization and deserialization, which is inherently
* lossy. In such cases, the expected and actual values may be placeholders
* or approximations of the original objects.
*
* @typedef ExpectationResult
* @property {String} matcherName - The name of the matcher that was executed for this expectation.
* @property {String} message - The failure message for the expectation.
* @property {String} stack - The stack trace for the failure if available.
* @property {Boolean} passed - Whether the expectation passed or failed.
* @property {Object} expected - If the expectation failed, what was the expected value.
* @property {Object} actual - If the expectation failed, what actual value was produced.
* @property {Object} expected - Deprecated. If the expectation failed, what was the expected value.
* @property {Object} actual - Deprecated. If the expectation failed, what actual value was produced.
* @property {String|undefined} globalErrorType - The type of an error that
* is reported on the top suite. Valid values are undefined, "afterAll",
* "load", "lateExpectation", and "lateError".

View File

@@ -32,7 +32,10 @@ getJasmineRequireObj().toHaveSpyInteractions = function(j$) {
let hasSpy = false;
const calledSpies = [];
for (const spy of Object.values(actual)) {
if (!j$.isSpy(spy)) continue;
if (!j$.isSpy(spy)) {
continue;
}
hasSpy = true;
if (spy.calls.any()) {

View File

@@ -1,4 +1,30 @@
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 = [
/**
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.