Compare commits

...

15 Commits

Author SHA1 Message Date
Steve Gravrock
483d4ab3c3 Bump version to 5.5.0 2024-11-23 10:58:56 -08:00
Steve Gravrock
663dfe5932 Updated release instructions 2024-11-23 10:42:20 -08:00
Steve Gravrock
ce9c752899 Added debug logging to flaky test 2024-11-12 20:25:20 -08:00
Steve Gravrock
d5e7bc9fd6 Optionally enforce uniqueness of spec and suite names
This is off by default for backwards compatibility but can be enabled
by setting the forbidDuplicateNames env config property to true.

Fixes #1633.
2024-11-10 09:54:51 -08:00
Steve Gravrock
744e765d6f Update copyright date 2024-11-09 13:37:46 -08:00
Steve Gravrock
29551ba4f3 Prettier 2024-11-09 13:17:32 -08:00
Steve Gravrock
bd9a3b2305 Include property value mismatches in diffs even when there are missing or extra properties 2024-11-09 11:22:27 -08:00
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
22 changed files with 430 additions and 88 deletions

View File

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

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
@@ -56,5 +60,5 @@ To find out what environments work with a particular Jasmine release, see the [r
* Sheel Choksi
Copyright (c) 2008-2019 Pivotal Labs<br>
Copyright (c) 2008-2023 The Jasmine developers<br>
Copyright (c) 2008-2024 The Jasmine developers<br>
This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/LICENSE).

View File

@@ -46,8 +46,8 @@ When ready to release - specs are all green and the stories are done:
### Release the core NPM module
1. `npm adduser` to save your credentials locally
1. `npm publish .` to publish what's in `package.json`
1. `npm login` to save your credentials locally
2. `npm publish .` to publish what's in `package.json`
### Release the docs
@@ -55,11 +55,6 @@ Probably only need to do this when releasing a minor version, and not a patch
version. See [the README file in the docs repo](https://github.com/jasmine/jasmine.github.io/blob/master/README.md)
for instructions.
1. `rake update_edge_jasmine`
1. `npm run jsdoc`
1. `rake release[${version}]` to copy the current edge docs to the new version
1. Commit and push.
### Release the `jasmine` NPM package
See <https://github.com/jasmine/jasmine-npm/blob/main/RELEASE.md>.

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.
@@ -1295,6 +1295,15 @@ getJasmineRequireObj().Env = function(j$) {
* @default true
*/
autoCleanClosures: true,
/**
* Whether to forbid duplicate spec or suite names. If set to true, using
* the same name multiple times in the same immediate parent suite is an
* error.
* @name Configuration#forbidDuplicateNames
* @type boolean
* @default false
*/
forbidDuplicateNames: false,
/**
* Whether or not to issue warnings for certain deprecated functionality
* every time it's used. If not set or set to false, deprecation warnings
@@ -1343,7 +1352,8 @@ getJasmineRequireObj().Env = function(j$) {
'hideDisabled',
'stopOnSpecFailure',
'stopSpecOnExpectationFailure',
'autoCleanClosures'
'autoCleanClosures',
'forbidDuplicateNames'
];
booleanProps.forEach(function(prop) {
@@ -1429,12 +1439,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;
@@ -2667,13 +2684,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".
@@ -5238,7 +5263,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
diffBuilder.recordMismatch(
objectKeysAreDifferentFormatter.bind(null, this.pp)
);
return false;
result = false;
}
for (const key of aKeys) {
@@ -9029,8 +9054,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 = {
@@ -9874,9 +9899,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);
@@ -9885,6 +9908,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;
@@ -10109,8 +10136,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}
@@ -10255,6 +10282,16 @@ getJasmineRequireObj().Suite = function(j$) {
);
};
Suite.prototype.hasChildWithDescription = function(description) {
for (const child of this.children) {
if (child.description === description) {
return true;
}
}
return false;
};
Object.defineProperty(Suite.prototype, 'metadata', {
get: function() {
if (!this.metadata_) {
@@ -10492,6 +10529,8 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
j$.util.validateTimeout(timeout);
}
this.checkDuplicate_(description, 'spec');
const spec = this.specFactory_(description, fn, timeout, filename);
if (this.currentDeclarationSuite_.markedExcluding) {
spec.exclude();
@@ -10501,7 +10540,27 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
return spec;
}
checkDuplicate_(description, type) {
if (!this.env_.configuration().forbidDuplicateNames) {
return;
}
if (this.currentDeclarationSuite_.hasChildWithDescription(description)) {
const parentDesc =
this.currentDeclarationSuite_ === this.topSuite
? 'top suite'
: `"${this.currentDeclarationSuite_.getFullName()}"`;
throw new Error(
`Duplicate ${type} name "${description}" found in ${parentDesc}`
);
}
}
suiteFactory_(description, filename) {
if (this.topSuite) {
this.checkDuplicate_(description, 'suite');
}
const config = this.env_.configuration();
const parentSuite = this.currentDeclarationSuite_;
const reportedParentSuiteId =
@@ -10982,5 +11041,5 @@ getJasmineRequireObj().UserContext = function(j$) {
};
getJasmineRequireObj().version = function() {
return '5.3.0';
return '5.5.0';
};

View File

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

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)_

34
release_notes/5.5.0.md Normal file
View File

@@ -0,0 +1,34 @@
# Jasmine Core 5.5.0 Release Notes
## Changes
* Optionally enforce uniqueness of spec and suite names
This is off by default for backwards compatibility but can be enabled
by setting the `forbidDuplicateNames` env config property to true.
Fixes [#1633](https://github.com/jasmine/jasmine/issues/1633).
* Include property value mismatches in diffs even when there are missing or
extra properties
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|-------------------------|
| Node | 18, 20, 22 |
| Safari | 15-17 |
| Chrome | 131* |
| Firefox | 102**, 115**, 128, 132* |
| Edge | 131* |
\* 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

@@ -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

@@ -176,6 +176,117 @@ describe('SuiteBuilder', function() {
};
}
describe('Duplicate name handling', function() {
describe('When forbidDuplicateNames is true', function() {
let env;
beforeEach(function() {
env = { configuration: () => ({ forbidDuplicateNames: true }) };
});
it('forbids duplicate spec names', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('a suite', function() {
suiteBuilder.describe('a nested suite', function() {
suiteBuilder.it('a spec');
suiteBuilder.it('a spec');
});
});
}).toThrowError(
'Duplicate spec name "a spec" found in "a suite a nested suite"'
);
});
it('forbids duplicate spec names in the top suite', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.it('another spec');
suiteBuilder.it('another spec');
}).toThrowError(
'Duplicate spec name "another spec" found in top suite'
);
});
it('forbids duplicate suite names', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('a suite', function() {
suiteBuilder.describe('a nested suite', function() {
suiteBuilder.describe('another suite', function() {
suiteBuilder.it('a spec');
});
suiteBuilder.describe('another suite', function() {
suiteBuilder.it('a spec');
});
});
});
}).toThrowError(
'Duplicate suite name "another suite" found in "a suite a nested suite"'
);
});
it('forbids duplicate suite names in the top suite', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a spec');
});
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a spec');
});
}).toThrowError('Duplicate suite name "a suite" found in top suite');
});
it('allows spec and suite names to be duplicated in different suites', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('suite a', function() {
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
suiteBuilder.describe('child suite', function() {
suiteBuilder.it('dupe spec');
});
});
});
suiteBuilder.describe('suite b', function() {
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
});
});
}).not.toThrow();
});
});
describe('When forbidDuplicateNames is false', function() {
let env;
beforeEach(function() {
env = { configuration: () => ({ forbidDuplicateNames: false }) };
});
it('allows duplicate spec and suite names', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
suiteBuilder.it('dupe spec');
});
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
suiteBuilder.it('dupe spec');
});
}).not.toThrow();
});
});
});
describe('#parallelReset', function() {
it('resets the top suite result', function() {
jasmineUnderTest.Suite.prototype.handleException.and.callThrough();

View File

@@ -378,4 +378,31 @@ describe('Suite', function() {
);
});
});
describe('#hasChildWithDescription', function() {
it('returns true if there is a child with the given description', function() {
const subject = new jasmineUnderTest.Suite({});
const description = 'a spec';
subject.addChild({ description });
expect(subject.hasChildWithDescription(description)).toBeTrue();
});
it('returns false if there is no child with the given description', function() {
const subject = new jasmineUnderTest.Suite({});
subject.addChild({ description: 'a different spec' });
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
});
it('does not recurse into child suites', function() {
const subject = new jasmineUnderTest.Suite({});
const childSuite = new jasmineUnderTest.Suite({});
subject.addChild(childSuite);
const description = 'a spec';
childSuite.addChild(description);
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
});
});
});

View File

@@ -4444,6 +4444,15 @@ describe('Env integration', function() {
});
});
it('forbids duplicates when forbidDuplicateNames is true', function() {
env.configure({ forbidDuplicateNames: true });
env.it('a spec');
expect(function() {
env.it('a spec');
}).toThrowError('Duplicate spec name "a spec" found in top suite');
});
function browserEventMethods() {
return {
listeners_: { error: [], unhandledrejection: [] },

View File

@@ -757,7 +757,9 @@ describe('matchersUtil', function() {
const a2 = new TypedArrayCtor(2);
a1[0] = a2[0] = 0;
a1[1] = a2[1] = 1;
expect(matchersUtil.equals(a1, a2)).toBe(true);
const diffBuilder = new jasmineUnderTest.DiffBuilder();
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(true);
jasmine.debugLog('Diff: ' + diffBuilder.getMessage());
}
);

View File

@@ -96,6 +96,17 @@ describe('toEqual', function() {
expect(compareEquals(actual, expected).message).toEqual(message);
});
it('reports mismatches as well as missing or extra properties', function() {
const actual = { x: { z: 2 } },
expected = { x: { y: 1, z: 3 } },
message =
'Expected $.x to have properties\n' +
' y: 1\n' +
'Expected $.x.z = 2 to equal 3.';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it('reports missing symbol properties', function() {
const actual = { x: {} },
expected = { x: { [Symbol('y')]: 1 } },

View File

@@ -138,6 +138,15 @@ getJasmineRequireObj().Env = function(j$) {
* @default true
*/
autoCleanClosures: true,
/**
* Whether to forbid duplicate spec or suite names. If set to true, using
* the same name multiple times in the same immediate parent suite is an
* error.
* @name Configuration#forbidDuplicateNames
* @type boolean
* @default false
*/
forbidDuplicateNames: false,
/**
* Whether or not to issue warnings for certain deprecated functionality
* every time it's used. If not set or set to false, deprecation warnings
@@ -186,7 +195,8 @@ getJasmineRequireObj().Env = function(j$) {
'hideDisabled',
'stopOnSpecFailure',
'stopSpecOnExpectationFailure',
'autoCleanClosures'
'autoCleanClosures',
'forbidDuplicateNames'
];
booleanProps.forEach(function(prop) {
@@ -272,12 +282,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

@@ -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}
@@ -251,6 +251,16 @@ getJasmineRequireObj().Suite = function(j$) {
);
};
Suite.prototype.hasChildWithDescription = function(description) {
for (const child of this.children) {
if (child.description === description) {
return true;
}
}
return false;
};
Object.defineProperty(Suite.prototype, 'metadata', {
get: function() {
if (!this.metadata_) {

View File

@@ -161,6 +161,8 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
j$.util.validateTimeout(timeout);
}
this.checkDuplicate_(description, 'spec');
const spec = this.specFactory_(description, fn, timeout, filename);
if (this.currentDeclarationSuite_.markedExcluding) {
spec.exclude();
@@ -170,7 +172,27 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
return spec;
}
checkDuplicate_(description, type) {
if (!this.env_.configuration().forbidDuplicateNames) {
return;
}
if (this.currentDeclarationSuite_.hasChildWithDescription(description)) {
const parentDesc =
this.currentDeclarationSuite_ === this.topSuite
? 'top suite'
: `"${this.currentDeclarationSuite_.getFullName()}"`;
throw new Error(
`Duplicate ${type} name "${description}" found in ${parentDesc}`
);
}
}
suiteFactory_(description, filename) {
if (this.topSuite) {
this.checkDuplicate_(description, 'suite');
}
const config = this.env_.configuration();
const parentSuite = this.currentDeclarationSuite_;
const reportedParentSuiteId =

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

@@ -483,7 +483,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
diffBuilder.recordMismatch(
objectKeysAreDifferentFormatter.bind(null, this.pp)
);
return false;
result = false;
}
for (const key of aKeys) {