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.
This commit is contained in:
@@ -1295,6 +1295,15 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
autoCleanClosures: 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
|
* Whether or not to issue warnings for certain deprecated functionality
|
||||||
* every time it's used. If not set or set to false, deprecation warnings
|
* every time it's used. If not set or set to false, deprecation warnings
|
||||||
@@ -1343,7 +1352,8 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
'hideDisabled',
|
'hideDisabled',
|
||||||
'stopOnSpecFailure',
|
'stopOnSpecFailure',
|
||||||
'stopSpecOnExpectationFailure',
|
'stopSpecOnExpectationFailure',
|
||||||
'autoCleanClosures'
|
'autoCleanClosures',
|
||||||
|
'forbidDuplicateNames',
|
||||||
];
|
];
|
||||||
|
|
||||||
booleanProps.forEach(function(prop) {
|
booleanProps.forEach(function(prop) {
|
||||||
@@ -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', {
|
Object.defineProperty(Suite.prototype, 'metadata', {
|
||||||
get: function() {
|
get: function() {
|
||||||
if (!this.metadata_) {
|
if (!this.metadata_) {
|
||||||
@@ -10509,6 +10529,8 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
j$.util.validateTimeout(timeout);
|
j$.util.validateTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.checkDuplicate_(description, 'spec');
|
||||||
|
|
||||||
const spec = this.specFactory_(description, fn, timeout, filename);
|
const spec = this.specFactory_(description, fn, timeout, filename);
|
||||||
if (this.currentDeclarationSuite_.markedExcluding) {
|
if (this.currentDeclarationSuite_.markedExcluding) {
|
||||||
spec.exclude();
|
spec.exclude();
|
||||||
@@ -10518,7 +10540,27 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
return spec;
|
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) {
|
suiteFactory_(description, filename) {
|
||||||
|
if (this.topSuite) {
|
||||||
|
this.checkDuplicate_(description, 'suite');
|
||||||
|
}
|
||||||
|
|
||||||
const config = this.env_.configuration();
|
const config = this.env_.configuration();
|
||||||
const parentSuite = this.currentDeclarationSuite_;
|
const parentSuite = this.currentDeclarationSuite_;
|
||||||
const reportedParentSuiteId =
|
const reportedParentSuiteId =
|
||||||
|
|||||||
@@ -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() {
|
describe('#parallelReset', function() {
|
||||||
it('resets the top suite result', function() {
|
it('resets the top suite result', function() {
|
||||||
jasmineUnderTest.Suite.prototype.handleException.and.callThrough();
|
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() {
|
function browserEventMethods() {
|
||||||
return {
|
return {
|
||||||
listeners_: { error: [], unhandledrejection: [] },
|
listeners_: { error: [], unhandledrejection: [] },
|
||||||
|
|||||||
@@ -138,6 +138,15 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
autoCleanClosures: 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
|
* Whether or not to issue warnings for certain deprecated functionality
|
||||||
* every time it's used. If not set or set to false, deprecation warnings
|
* every time it's used. If not set or set to false, deprecation warnings
|
||||||
@@ -186,7 +195,8 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
'hideDisabled',
|
'hideDisabled',
|
||||||
'stopOnSpecFailure',
|
'stopOnSpecFailure',
|
||||||
'stopSpecOnExpectationFailure',
|
'stopSpecOnExpectationFailure',
|
||||||
'autoCleanClosures'
|
'autoCleanClosures',
|
||||||
|
'forbidDuplicateNames'
|
||||||
];
|
];
|
||||||
|
|
||||||
booleanProps.forEach(function(prop) {
|
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', {
|
Object.defineProperty(Suite.prototype, 'metadata', {
|
||||||
get: function() {
|
get: function() {
|
||||||
if (!this.metadata_) {
|
if (!this.metadata_) {
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
j$.util.validateTimeout(timeout);
|
j$.util.validateTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.checkDuplicate_(description, 'spec');
|
||||||
|
|
||||||
const spec = this.specFactory_(description, fn, timeout, filename);
|
const spec = this.specFactory_(description, fn, timeout, filename);
|
||||||
if (this.currentDeclarationSuite_.markedExcluding) {
|
if (this.currentDeclarationSuite_.markedExcluding) {
|
||||||
spec.exclude();
|
spec.exclude();
|
||||||
@@ -170,7 +172,27 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
return spec;
|
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) {
|
suiteFactory_(description, filename) {
|
||||||
|
if (this.topSuite) {
|
||||||
|
this.checkDuplicate_(description, 'suite');
|
||||||
|
}
|
||||||
|
|
||||||
const config = this.env_.configuration();
|
const config = this.env_.configuration();
|
||||||
const parentSuite = this.currentDeclarationSuite_;
|
const parentSuite = this.currentDeclarationSuite_;
|
||||||
const reportedParentSuiteId =
|
const reportedParentSuiteId =
|
||||||
|
|||||||
Reference in New Issue
Block a user