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
+
+ 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]