From 18053374243a8b5c1a3945cb012d4c6c379f6dda Mon Sep 17 00:00:00 2001 From: Alex Yorkovich Date: Wed, 4 Dec 2024 12:19:28 -0600 Subject: [PATCH] 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; +};