1- import { matcherHint , printReceived , printExpected } from 'jest-matcher-utils' //eslint-disable-line import/no-extraneous-dependencies
1+ //eslint-disable-next-line import/no-extraneous-dependencies
2+ import {
3+ matcherHint ,
4+ printReceived ,
5+ printExpected ,
6+ stringify ,
7+ RECEIVED_COLOR as receivedColor ,
8+ EXPECTED_COLOR as expectedColor ,
9+ } from 'jest-matcher-utils'
210import { matches } from './utils'
311
412function getDisplayName ( subject ) {
@@ -9,10 +17,30 @@ function getDisplayName(subject) {
917 }
1018}
1119
20+ function checkHtmlElement ( htmlElement ) {
21+ if ( ! ( htmlElement instanceof HTMLElement ) ) {
22+ throw new Error (
23+ `The given subject is a ${ getDisplayName (
24+ htmlElement ,
25+ ) } , not an HTMLElement`,
26+ )
27+ }
28+ }
29+
1230const assertMessage = ( assertionName , message , received , expected ) =>
1331 `${ matcherHint ( `${ assertionName } ` , 'received' , '' ) } \n${ message } : ` +
1432 `${ printExpected ( expected ) } \nReceived: ${ printReceived ( received ) } `
1533
34+ function printAttribute ( name , value ) {
35+ return value === undefined ? name : `${ name } =${ stringify ( value ) } `
36+ }
37+
38+ function getAttributeComment ( name , value ) {
39+ return value === undefined
40+ ? `element.hasAttribute(${ stringify ( name ) } )`
41+ : `element.getAttribute(${ stringify ( name ) } ) === ${ stringify ( value ) } `
42+ }
43+
1644const extensions = {
1745 toBeInTheDOM ( received ) {
1846 getDisplayName ( received )
@@ -42,13 +70,7 @@ const extensions = {
4270 } ,
4371
4472 toHaveTextContent ( htmlElement , checkWith ) {
45- if ( ! ( htmlElement instanceof HTMLElement ) )
46- throw new Error (
47- `The given subject is a ${ getDisplayName (
48- htmlElement ,
49- ) } , not an HTMLElement`,
50- )
51-
73+ checkHtmlElement ( htmlElement )
5274 const textContent = htmlElement . textContent
5375 const pass = matches ( textContent , htmlElement , checkWith )
5476 if ( pass ) {
@@ -75,6 +97,41 @@ const extensions = {
7597 }
7698 }
7799 } ,
100+
101+ toHaveAttribute ( htmlElement , name , expectedValue ) {
102+ checkHtmlElement ( htmlElement )
103+ const isExpectedValuePresent = expectedValue !== undefined
104+ const hasAttribute = htmlElement . hasAttribute ( name )
105+ const receivedValue = htmlElement . getAttribute ( name )
106+ return {
107+ pass : isExpectedValuePresent
108+ ? hasAttribute && receivedValue === expectedValue
109+ : hasAttribute ,
110+ message : ( ) => {
111+ const to = this . isNot ? 'not to' : 'to'
112+ const receivedAttribute = receivedColor (
113+ hasAttribute
114+ ? printAttribute ( name , receivedValue )
115+ : 'attribute was not found' ,
116+ )
117+ const expectedMsg = `Expected the element ${ to } have attribute:\n ${ expectedColor (
118+ printAttribute ( name , expectedValue ) ,
119+ ) } `
120+ const matcher = matcherHint (
121+ `${ this . isNot ? '.not' : '' } .toHaveAttribute` ,
122+ 'element' ,
123+ printExpected ( name ) ,
124+ {
125+ secondArgument : isExpectedValuePresent
126+ ? printExpected ( expectedValue )
127+ : undefined ,
128+ comment : getAttributeComment ( name , expectedValue ) ,
129+ } ,
130+ )
131+ return `${ matcher } \n\n${ expectedMsg } \nReceived:\n ${ receivedAttribute } `
132+ } ,
133+ }
134+ } ,
78135}
79136
80137export default extensions
0 commit comments