From 0dc954e41dda1a58006c2c4485d0ae0481db46b7 Mon Sep 17 00:00:00 2001 From: Ernesto Garcia Date: Fri, 6 Apr 2018 12:47:44 -0300 Subject: [PATCH 1/3] Add toHaveClass custom matcher --- src/__tests__/element-queries.js | 34 ++++++++++++++++++++++++++++++++ src/extend-expect.js | 9 +++++++-- src/jest-extensions.js | 31 +++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index 39a0eecc..7bbcd605 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -150,4 +150,38 @@ test('using jest helpers to check element attributes', () => { ).toThrowError() }) +test('using jest helpers to check element class names', () => { + const {getByTestId} = render(` + + `) + + expect(getByTestId('delete-button')).toHaveClass('btn') + expect(getByTestId('delete-button')).toHaveClass('btn-danger') + expect(getByTestId('delete-button')).toHaveClass('extra') + expect(getByTestId('delete-button')).not.toHaveClass('xtra') + expect(getByTestId('delete-button')).toHaveClass('btn btn-danger') + expect(getByTestId('delete-button')).not.toHaveClass('btn-link') + + expect(() => + expect(getByTestId('delete-button')).not.toHaveClass('btn'), + ).toThrowError() + expect(() => + expect(getByTestId('delete-button')).not.toHaveClass('btn-danger'), + ).toThrowError() + expect(() => + expect(getByTestId('delete-button')).not.toHaveClass('extra'), + ).toThrowError() + expect(() => + expect(getByTestId('delete-button')).toHaveClass('xtra'), + ).toThrowError() + expect(() => + expect(getByTestId('delete-button')).not.toHaveClass('btn btn-danger'), + ).toThrowError() + expect(() => + expect(getByTestId('delete-button')).toHaveClass('btn-link'), + ).toThrowError() +}) + /* eslint jsx-a11y/label-has-for:0 */ diff --git a/src/extend-expect.js b/src/extend-expect.js index a2ddc2b4..a18d54f3 100644 --- a/src/extend-expect.js +++ b/src/extend-expect.js @@ -1,4 +1,9 @@ import extensions from './jest-extensions' -const {toBeInTheDOM, toHaveTextContent, toHaveAttribute} = extensions -expect.extend({toBeInTheDOM, toHaveTextContent, toHaveAttribute}) +const { + toBeInTheDOM, + toHaveTextContent, + toHaveAttribute, + toHaveClass, +} = extensions +expect.extend({toBeInTheDOM, toHaveTextContent, toHaveAttribute, toHaveClass}) diff --git a/src/jest-extensions.js b/src/jest-extensions.js index 228a40e2..266a7a1c 100644 --- a/src/jest-extensions.js +++ b/src/jest-extensions.js @@ -49,6 +49,14 @@ function getAttributeComment(name, value) { : `element.getAttribute(${stringify(name)}) === ${stringify(value)}` } +function splitClassNames(str) { + return str.split(/\s+/).filter(s => s.length > 0) +} + +function isSubset(subset, superset) { + return subset.every(item => superset.includes(item)) +} + const extensions = { toBeInTheDOM(received) { if (received) { @@ -130,6 +138,29 @@ const extensions = { }, } }, + + toHaveClass(htmlElement, expectedClassNames) { + checkHtmlElement(htmlElement) + const received = splitClassNames(htmlElement.getAttribute('class')) + const expected = splitClassNames(expectedClassNames) + return { + pass: isSubset(expected, received), + message: () => { + const to = this.isNot ? 'not to' : 'to' + return getMessage( + matcherHint( + `${this.isNot ? '.not' : ''}.toHaveClass`, + 'element', + printExpected(expected.join(' ')), + ), + `Expected the element ${to} have class`, + expected.join(' '), + 'Received', + received.join(' '), + ) + }, + } + }, } export default extensions From 110dca8f9c1f241128d78ddd25b962a6fb9dd42d Mon Sep 17 00:00:00 2001 From: Ernesto Garcia Date: Fri, 6 Apr 2018 13:12:40 -0300 Subject: [PATCH 2/3] Add documentation in the README --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index c38741a7..fb439350 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ when a real user uses it. * [`toBeInTheDOM`](#tobeinthedom) * [`toHaveTextContent`](#tohavetextcontent) * [`toHaveAttribute`](#tohaveattribute) + * [`toHaveClass`](#tohaveclass) * [Custom Jest Matchers - Typescript](#custom-jest-matchers---typescript) * [`TextMatch`](#textmatch) * [`query` APIs](#query-apis) @@ -364,6 +365,25 @@ expect(getByTestId(container, 'ok-button')).not.toHaveAttribute( // ... ``` +### `toHaveClass` + +This allows you to check wether the given element has certain classes within its +`class` attribute. + +```javascript +// add the custom expect matchers +import 'dom-testing-library/extend-expect' + +// ... +// +expect(getByTestId(container, 'delete-button')).toHaveClass('extra') +expect(getByTestId(container, 'delete-button')).toHaveClass('btn-danger btn') +expect(getByTestId(container, 'delete-button')).not.toHaveClass('btn-link') +// ... +``` + ### Custom Jest Matchers - Typescript When you use custom Jest Matchers with Typescript, you will need to extend the From f897f7a5f03759b0618eb53ab2a5a2e2eb8f878c Mon Sep 17 00:00:00 2001 From: Ernesto Garcia Date: Fri, 6 Apr 2018 15:29:58 -0300 Subject: [PATCH 3/3] Handle the case where an element has no class --- src/__tests__/element-queries.js | 15 ++++++++++++--- src/jest-extensions.js | 3 +++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index 7bbcd605..33ff6100 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -152,9 +152,14 @@ test('using jest helpers to check element attributes', () => { test('using jest helpers to check element class names', () => { const {getByTestId} = render(` - +
+ + +
`) expect(getByTestId('delete-button')).toHaveClass('btn') @@ -163,6 +168,7 @@ test('using jest helpers to check element class names', () => { expect(getByTestId('delete-button')).not.toHaveClass('xtra') expect(getByTestId('delete-button')).toHaveClass('btn btn-danger') expect(getByTestId('delete-button')).not.toHaveClass('btn-link') + expect(getByTestId('cancel-button')).not.toHaveClass('btn-danger') expect(() => expect(getByTestId('delete-button')).not.toHaveClass('btn'), @@ -182,6 +188,9 @@ test('using jest helpers to check element class names', () => { expect(() => expect(getByTestId('delete-button')).toHaveClass('btn-link'), ).toThrowError() + expect(() => + expect(getByTestId('cancel-button')).toHaveClass('btn-danger'), + ).toThrowError() }) /* eslint jsx-a11y/label-has-for:0 */ diff --git a/src/jest-extensions.js b/src/jest-extensions.js index 266a7a1c..a44e728d 100644 --- a/src/jest-extensions.js +++ b/src/jest-extensions.js @@ -50,6 +50,9 @@ function getAttributeComment(name, value) { } function splitClassNames(str) { + if (!str) { + return [] + } return str.split(/\s+/).filter(s => s.length > 0) }