Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
483d4ab3c3 | ||
|
|
663dfe5932 | ||
|
|
ce9c752899 | ||
|
|
d5e7bc9fd6 | ||
|
|
744e765d6f | ||
|
|
29551ba4f3 | ||
|
|
bd9a3b2305 |
2
LICENSE
2
LICENSE
@@ -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
|
||||
|
||||
@@ -60,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).
|
||||
|
||||
@@ -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>.
|
||||
|
||||
@@ -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) {
|
||||
@@ -5253,7 +5263,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
|
||||
diffBuilder.recordMismatch(
|
||||
objectKeysAreDifferentFormatter.bind(null, this.pp)
|
||||
);
|
||||
return false;
|
||||
result = false;
|
||||
}
|
||||
|
||||
for (const key of aKeys) {
|
||||
@@ -10272,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_) {
|
||||
@@ -10509,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();
|
||||
@@ -10518,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 =
|
||||
@@ -10999,5 +11041,5 @@ getJasmineRequireObj().UserContext = function(j$) {
|
||||
};
|
||||
|
||||
getJasmineRequireObj().version = function() {
|
||||
return '5.4.0';
|
||||
return '5.5.0';
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jasmine-core",
|
||||
"license": "MIT",
|
||||
"version": "5.4.0",
|
||||
"version": "5.5.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jasmine/jasmine.git"
|
||||
|
||||
34
release_notes/5.5.0.md
Normal file
34
release_notes/5.5.0.md
Normal 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)_
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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: [] },
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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 } },
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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_) {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -483,7 +483,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
|
||||
diffBuilder.recordMismatch(
|
||||
objectKeysAreDifferentFormatter.bind(null, this.pp)
|
||||
);
|
||||
return false;
|
||||
result = false;
|
||||
}
|
||||
|
||||
for (const key of aKeys) {
|
||||
|
||||
Reference in New Issue
Block a user