diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index e6b270ac..d80796e9 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -16,7 +16,7 @@ jobs:
if: ${{ !contains(github.head_ref, 'all-contributors') }}
strategy:
matrix:
- node: [10.14, 12, 14, 15, 16]
+ node: [14, 16, 18]
runs-on: ubuntu-latest
steps:
- name: ⬇️ Checkout repo
diff --git a/README.md b/README.md
index 4a4c2caf..1778f8fe 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,6 @@ clear to read and to maintain.
-
- [Installation](#installation)
- [Usage](#usage)
- [With TypeScript](#with-typescript)
@@ -424,6 +423,14 @@ An element is visible if **all** the following conditions are met:
Visible Example
Hidden Attribute Example
+
+ Title of hidden text
+ Hidden Details Example
+
+
+ Title of visible text
+ Visible Details Example
+
```
```javascript
@@ -433,6 +440,8 @@ expect(getByText('Display None Example')).not.toBeVisible()
expect(getByText('Hidden Parent Example')).not.toBeVisible()
expect(getByText('Visible Example')).toBeVisible()
expect(getByText('Hidden Attribute Example')).not.toBeVisible()
+expect(getByText('Hidden Details Example')).not.toBeVisible()
+expect(getByText('Visible Details Example')).toBeVisible()
```
@@ -1204,12 +1213,8 @@ To perform a partial match, you can pass a `RegExp` or use
#### Examples
```html
-
-
- Closing will discard any changes
-
+
+Closing will discard any changes
```
diff --git a/package.json b/package.json
index 8c7ddfc9..31c03b1f 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
}
},
"engines": {
- "node": ">=8",
+ "node": ">=14",
"npm": ">=6",
"yarn": ">=1"
},
@@ -46,7 +46,7 @@
"@types/testing-library__jest-dom": "^5.9.1",
"aria-query": "^5.0.0",
"chalk": "^3.0.0",
- "css": "^3.0.0",
+ "@adobe/css-tools": "^4.0.1",
"css.escape": "^1.5.1",
"dom-accessibility-api": "^0.5.6",
"lodash": "^4.17.15",
diff --git a/src/__tests__/to-be-visible.js b/src/__tests__/to-be-visible.js
index f2b4277a..177a938c 100644
--- a/src/__tests__/to-be-visible.js
+++ b/src/__tests__/to-be-visible.js
@@ -134,6 +134,74 @@ describe('.toBeVisible', () => {
})
})
+ describe('when the inner text does not have an enclosing element', () => {
+ describe('when the details is not opened', () => {
+ beforeEach(() => {
+ subject = render(`
+
+ Title of hidden innerText
+ hidden innerText
+
+ `)
+ })
+
+ it('returns false to the details content', () => {
+ expect(subject.container.querySelector('details')).not.toBeVisible()
+ })
+
+ it('returns true to the details summary', () => {
+ expect(subject.container.querySelector('summary')).toBeVisible()
+ })
+
+ describe('when the user clicks on the summary', () => {
+ beforeEach(() => subject.container.querySelector('summary').click())
+
+ it('returns true to the details content', () => {
+ expect(subject.container.querySelector('details')).toBeVisible()
+ })
+
+ it('returns true to the details summary', () => {
+ expect(subject.container.querySelector('summary')).toBeVisible()
+ })
+ })
+ })
+
+ describe('when the details is opened', () => {
+ beforeEach(() => {
+ subject = render(`
+
+ Title of visible innerText
+ visible innerText
+
+ `)
+ })
+
+ it('returns true to the details content', () => {
+ expect(subject.container.querySelector('details')).toBeVisible()
+ })
+
+ it('returns true to inner small content', () => {
+ expect(subject.container.querySelector('small')).toBeVisible()
+ })
+
+ describe('when the user clicks on the summary', () => {
+ beforeEach(() => subject.container.querySelector('summary').click())
+
+ it('returns false to the details content', () => {
+ expect(subject.container.querySelector('details')).not.toBeVisible()
+ })
+
+ it('returns false to the inner small content', () => {
+ expect(subject.container.querySelector('small')).not.toBeVisible()
+ })
+
+ it('returns true to the details summary', () => {
+ expect(subject.container.querySelector('summary')).toBeVisible()
+ })
+ })
+ })
+ })
+
describe('with a nested element', () => {
describe('when the nested is opened', () => {
beforeEach(() => {
@@ -247,6 +315,113 @@ describe('.toBeVisible', () => {
).toBeVisible()
})
})
+
+ describe('with nested details (unenclosed outer, enclosed inner)', () => {
+ describe('when both outer and inner are opened', () => {
+ beforeEach(() => {
+ subject = render(`
+
+ Title of outer unenclosed
+ Unenclosed innerText
+
+ Title of inner enclosed
+ Enclosed innerText
+
+
+ `)
+ })
+
+ it('returns true to outer unenclosed innerText', () => {
+ expect(subject.container.querySelector('details')).toBeVisible()
+ })
+
+ it('returns true to outer summary', () => {
+ expect(subject.container.querySelector('summary')).toBeVisible()
+ })
+
+ it('returns true to inner enclosed innerText', () => {
+ expect(
+ subject.container.querySelector('details > details > div'),
+ ).toBeVisible()
+ })
+
+ it('returns true to inner summary', () => {
+ expect(
+ subject.container.querySelector('details > details > summary'),
+ ).toBeVisible()
+ })
+ })
+
+ describe('when outer is opened and inner is not opened', () => {
+ beforeEach(() => {
+ subject = render(`
+
+ Title of outer unenclosed
+ Unenclosed innerText
+
+ Title of inner enclosed
+ Enclosed innerText
+
+
+ `)
+ })
+
+ it('returns true to outer unenclosed innerText', () => {
+ expect(subject.container.querySelector('details')).toBeVisible()
+ })
+
+ it('returns true to outer summary', () => {
+ expect(subject.container.querySelector('summary')).toBeVisible()
+ })
+
+ it('returns false to inner enclosed innerText', () => {
+ expect(
+ subject.container.querySelector('details > details > div'),
+ ).not.toBeVisible()
+ })
+
+ it('returns true to inner summary', () => {
+ expect(
+ subject.container.querySelector('details > details > summary'),
+ ).toBeVisible()
+ })
+ })
+
+ describe('when outer is not opened and inner is opened', () => {
+ beforeEach(() => {
+ subject = render(`
+
+ Title of outer unenclosed
+ Unenclosed innerText
+
+ Title of inner enclosed
+ Enclosed innerText
+
+
+ `)
+ })
+
+ it('returns true to outer unenclosed innerText', () => {
+ expect(subject.container.querySelector('details')).not.toBeVisible()
+ })
+
+ it('returns true to outer summary', () => {
+ expect(subject.container.querySelector('summary')).toBeVisible()
+ })
+
+ it('returns false to inner enclosed innerText', () => {
+ expect(
+ subject.container.querySelector('details > details > div'),
+ ).not.toBeVisible()
+ })
+
+ it('returns true to inner summary', () => {
+ expect(
+ subject.container.querySelector('details > details > summary'),
+ ).not.toBeVisible()
+ })
+ })
+ })
})
})
})
diff --git a/src/to-be-visible.js b/src/to-be-visible.js
index 518b7d56..9ea39938 100644
--- a/src/to-be-visible.js
+++ b/src/to-be-visible.js
@@ -14,12 +14,19 @@ function isStyleVisible(element) {
}
function isAttributeVisible(element, previousElement) {
- return (
- !element.hasAttribute('hidden') &&
- (element.nodeName === 'DETAILS' && previousElement.nodeName !== 'SUMMARY'
- ? element.hasAttribute('open')
- : true)
- )
+ let detailsVisibility
+
+ if (previousElement) {
+ detailsVisibility =
+ element.nodeName === 'DETAILS' && previousElement.nodeName !== 'SUMMARY'
+ ? element.hasAttribute('open')
+ : true
+ } else {
+ detailsVisibility =
+ element.nodeName === 'DETAILS' ? element.hasAttribute('open') : true
+ }
+
+ return !element.hasAttribute('hidden') && detailsVisibility
}
function isElementVisible(element, previousElement) {
diff --git a/src/to-have-focus.js b/src/to-have-focus.js
index 3b1b1acc..a792645f 100644
--- a/src/to-have-focus.js
+++ b/src/to-have-focus.js
@@ -13,10 +13,19 @@ export function toHaveFocus(element) {
'',
),
'',
- 'Expected element with focus:',
- ` ${this.utils.printExpected(element)}`,
- 'Received element with focus:',
- ` ${this.utils.printReceived(element.ownerDocument.activeElement)}`,
+ ...(this.isNot
+ ? [
+ 'Received element is focused:',
+ ` ${this.utils.printReceived(element)}`,
+ ]
+ : [
+ 'Expected element with focus:',
+ ` ${this.utils.printExpected(element)}`,
+ 'Received element with focus:',
+ ` ${this.utils.printReceived(
+ element.ownerDocument.activeElement,
+ )}`,
+ ]),
].join('\n')
},
}
diff --git a/src/utils.js b/src/utils.js
index 2f07fb8e..cdbb1088 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,6 +1,6 @@
import redent from 'redent'
-import cssParse from 'css/lib/parse'
import isEqual from 'lodash/isEqual'
+import {parse} from '@adobe/css-tools'
class GenericTypeError extends Error {
constructor(expectedString, received, matcherFn, context) {
@@ -100,7 +100,7 @@ class InvalidCSSError extends Error {
}
function parseCSS(css, ...args) {
- const ast = cssParse(`selector { ${css} }`, {silent: true}).stylesheet
+ const ast = parse(`selector { ${css} }`, {silent: true}).stylesheet
if (ast.parsingErrors && ast.parsingErrors.length > 0) {
const {reason, line} = ast.parsingErrors[0]