From 18053374243a8b5c1a3945cb012d4c6c379f6dda Mon Sep 17 00:00:00 2001 From: Alex Yorkovich Date: Wed, 4 Dec 2024 12:19:28 -0600 Subject: [PATCH 1/3] Added new toHaveClasses matcher; tests included --- spec/core/integration/MatchersSpec.js | 14 ++++++++ spec/core/matchers/toHaveClassesSpec.js | 48 +++++++++++++++++++++++++ src/core/matchers/requireMatchers.js | 1 + src/core/matchers/toHaveClasses.js | 36 +++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 spec/core/matchers/toHaveClassesSpec.js create mode 100644 src/core/matchers/toHaveClasses.js diff --git a/spec/core/integration/MatchersSpec.js b/spec/core/integration/MatchersSpec.js index f09656cd..51b5e148 100755 --- a/spec/core/integration/MatchersSpec.js +++ b/spec/core/integration/MatchersSpec.js @@ -621,6 +621,20 @@ describe('Matchers (Integration)', function() { }); }); + describe('toHaveClasses', function() { + verifyPasses(function(env) { + const el = specHelpers.domHelpers.createElementWithClassName( + 'foo bar baz' + ); + env.expect(el).toHaveClasses(['bar', 'baz']); + }); + + verifyFails(function(env) { + const el = specHelpers.domHelpers.createElementWithClassName('foo bar'); + env.expect(el).toHaveClasses(['bar', 'baz']); + }); + }); + describe('toHaveSpyInteractions', function() { let spyObj; beforeEach(function() { diff --git a/spec/core/matchers/toHaveClassesSpec.js b/spec/core/matchers/toHaveClassesSpec.js new file mode 100644 index 00000000..0a55edc3 --- /dev/null +++ b/spec/core/matchers/toHaveClassesSpec.js @@ -0,0 +1,48 @@ +describe('toHaveClasses', function() { + it('fails for a DOM element that lacks all the expected classes', function() { + const matcher = jasmineUnderTest.matchers.toHaveClasses(), + result = matcher.compare( + specHelpers.domHelpers.createElementWithClassName(''), + ['foo', 'bar'] + ); + + expect(result.pass).toBe(false); + }); + + it('passes for a DOM element that has all the expected classes', function() { + const matcher = jasmineUnderTest.matchers.toHaveClasses(), + el = specHelpers.domHelpers.createElementWithClassName('foo bar baz'); + + expect(matcher.compare(el, ['foo', 'bar']).pass).toBe(true); + }); + + it('fails for a DOM element that only has some matching classes', function() { + const matcher = jasmineUnderTest.matchers.toHaveClasses(), + el = specHelpers.domHelpers.createElementWithClassName('foo bar'); + + expect(matcher.compare(el, ['foo', 'can']).pass).toBe(false); + }); + + it('throws an exception when actual is not a DOM element', function() { + const matcher = jasmineUnderTest.matchers.toHaveClasses({ + pp: jasmineUnderTest.makePrettyPrinter() + }); + + expect(function() { + matcher.compare('x', ['foo']); + }).toThrowError("'x' is not a DOM element"); + + expect(function() { + matcher.compare(undefined, ['foo']); + }).toThrowError('undefined is not a DOM element'); + + const textNode = specHelpers.domHelpers.document.createTextNode(''); + expect(function() { + matcher.compare(textNode, ['foo']); + }).toThrowError('HTMLNode is not a DOM element'); + + expect(function() { + matcher.compare({ classList: '' }, ['foo']); + }).toThrowError("Object({ classList: '' }) is not a DOM element"); + }); +}); diff --git a/src/core/matchers/requireMatchers.js b/src/core/matchers/requireMatchers.js index b15435c4..c8fb4de3 100755 --- a/src/core/matchers/requireMatchers.js +++ b/src/core/matchers/requireMatchers.js @@ -27,6 +27,7 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) { 'toHaveBeenCalledTimes', 'toHaveBeenCalledWith', 'toHaveClass', + 'toHaveClasses', 'toHaveSpyInteractions', 'toMatch', 'toThrow', diff --git a/src/core/matchers/toHaveClasses.js b/src/core/matchers/toHaveClasses.js new file mode 100644 index 00000000..42aad807 --- /dev/null +++ b/src/core/matchers/toHaveClasses.js @@ -0,0 +1,36 @@ +getJasmineRequireObj().toHaveClasses = function(j$) { + /** + * {@link expect} the actual value to be a DOM element that has the expected classes + * @function + * @name matchers#toHaveClasses + * @since 3.0.0 + * @param {Object} expected - The class names to test for + * @example + * const el = document.createElement('div'); + * el.className = 'foo bar baz'; + * expect(el).toHaveClasses(['bar', 'baz']); + */ + function toHaveClasses(matchersUtil) { + return { + compare: function(actual, expected) { + if (!isElement(actual)) { + throw new Error(matchersUtil.pp(actual) + ' is not a DOM element'); + } + + return { + pass: expected.every(e => + actual.classList.value.split(' ').some(c => c === e) + ) + }; + } + }; + } + + function isElement(maybeEl) { + return ( + maybeEl && maybeEl.classList && j$.isFunction_(maybeEl.classList.contains) + ); + } + + return toHaveClasses; +}; From c3650ea7c774a558a65837ffb3a9b865974262de Mon Sep 17 00:00:00 2001 From: Alex Yorkovich Date: Wed, 4 Dec 2024 12:34:36 -0600 Subject: [PATCH 2/3] updated release number --- src/core/matchers/toHaveClasses.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/matchers/toHaveClasses.js b/src/core/matchers/toHaveClasses.js index 42aad807..d4034e17 100644 --- a/src/core/matchers/toHaveClasses.js +++ b/src/core/matchers/toHaveClasses.js @@ -3,7 +3,7 @@ getJasmineRequireObj().toHaveClasses = function(j$) { * {@link expect} the actual value to be a DOM element that has the expected classes * @function * @name matchers#toHaveClasses - * @since 3.0.0 + * @since 5.6.0 * @param {Object} expected - The class names to test for * @example * const el = document.createElement('div'); From 819fab7b58ff0441cdb96b9e4b9c7f4786fc3b66 Mon Sep 17 00:00:00 2001 From: Alex Yorkovich Date: Sat, 7 Dec 2024 13:14:25 -0600 Subject: [PATCH 3/3] simplified match condition --- src/core/matchers/toHaveClasses.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/matchers/toHaveClasses.js b/src/core/matchers/toHaveClasses.js index d4034e17..e8275b35 100644 --- a/src/core/matchers/toHaveClasses.js +++ b/src/core/matchers/toHaveClasses.js @@ -18,9 +18,7 @@ getJasmineRequireObj().toHaveClasses = function(j$) { } return { - pass: expected.every(e => - actual.classList.value.split(' ').some(c => c === e) - ) + pass: expected.every(e => actual.classList.contains(e)) }; } };