Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESLint: Enable testing-library/no-node-access rule #46703

Merged
merged 1 commit into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,6 @@ module.exports = {
'plugin:jest-dom/recommended',
'plugin:testing-library/react',
],
rules: {
'testing-library/no-node-access': 'off',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this as it's now enabled as part of the plugin:testing-library/react recommended rules.

},
},
{
files: [ 'packages/e2e-test*/**/*.js' ],
Expand Down
14 changes: 9 additions & 5 deletions packages/block-editor/src/components/block-icon/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import { image } from '@wordpress/icons';
*/
import BlockIcon from '../';

function getIconWrapper( container ) {
return container.firstChild;
}
Comment on lines +16 to +18
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a wrapper function for this doesn't only allow us to reduce code repetition and have the violation only in one place; it actually tricks the linter - there's no need for a rule ignore here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh, sneaky 😂


describe( 'BlockIcon', () => {
it( 'renders a Icon', () => {
const { container } = render( <BlockIcon icon={ image } /> );
Expand All @@ -23,21 +27,21 @@ describe( 'BlockIcon', () => {
it( 'renders a span without the has-colors classname', () => {
const { container } = render( <BlockIcon icon={ image } /> );

expect( container.firstChild ).not.toHaveClass( 'has-colors' );
expect( getIconWrapper( container ) ).not.toHaveClass( 'has-colors' );
} );

it( 'renders a span with the has-colors classname', () => {
const { container } = render( <BlockIcon icon={ image } showColors /> );

expect( container.firstChild ).toHaveClass( 'has-colors' );
expect( getIconWrapper( container ) ).toHaveClass( 'has-colors' );
} );

it( 'supports adding a className to the wrapper', () => {
const { container } = render(
<BlockIcon icon={ image } className="foo-bar" />
);

expect( container.firstChild ).toHaveClass( 'foo-bar' );
expect( getIconWrapper( container ) ).toHaveClass( 'foo-bar' );
} );

it( 'skips adding background and foreground styles when colors are not enabled', () => {
Expand All @@ -51,7 +55,7 @@ describe( 'BlockIcon', () => {
/>
);

expect( container.firstChild ).not.toHaveAttribute( 'style' );
expect( getIconWrapper( container ) ).not.toHaveAttribute( 'style' );
} );

it( 'adds background and foreground styles when colors are enabled', () => {
Expand All @@ -66,7 +70,7 @@ describe( 'BlockIcon', () => {
/>
);

expect( container.firstChild ).toHaveStyle( {
expect( getIconWrapper( container ) ).toHaveStyle( {
backgroundColor: 'white',
color: 'black',
} );
Expand Down
22 changes: 18 additions & 4 deletions packages/block-editor/src/components/link-control/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,15 @@ describe( 'Searching for a link', () => {
const searchResults = await screen.findByRole( 'listbox', {
name: /Search results for.*/,
} );
const searchResultTextHighlightElements = Array.from(
searchResults.querySelectorAll( 'button[role="option"] mark' )
);

const searchResultTextHighlightElements = within( searchResults )
.getAllByRole( 'option' )
// TODO: Change to `getByRole( 'mark' )` when officially supported by
// WAI-ARIA 1.3 - see https://w3c.github.io/aria/#mark
// eslint-disable-next-line testing-library/no-node-access
.map( ( searchResult ) => searchResult.querySelector( 'mark' ) )
.flat()
.filter( Boolean );
Comment on lines +380 to +387
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per https://w3c.github.io/aria/#mark mark is not yet an officially supported role, but the rest of the query was convertible to screen queries, so we went for this.


// Given we're mocking out the results we should always have 4 mark elements.
expect( searchResultTextHighlightElements ).toHaveLength( 4 );
Expand Down Expand Up @@ -1092,6 +1098,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {

// Check human readable error notice is perceivable.
expect( errorNotice ).toBeVisible();
// eslint-disable-next-line testing-library/no-node-access
expect( errorNotice.parentElement ).toHaveClass(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the parent element.

'block-editor-link-control__search-error'
);
Expand Down Expand Up @@ -1324,7 +1331,8 @@ describe( 'Selecting links', () => {
} );

// Make sure focus is retained after submission.
expect( container ).toContainElement( document.activeElement );
// eslint-disable-next-line testing-library/no-node-access
expect( container.firstChild ).toHaveFocus();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit more specific than checking if any element inside the rendered tree has the focus.


expect( currentLink ).toBeVisible();
expect(
Expand Down Expand Up @@ -1594,11 +1602,13 @@ describe( 'Rich link previews', () => {
await waitFor( () => expect( linkPreview ).toHaveClass( 'is-rich' ) );

// Todo: refactor to use user-facing queries.
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the image.

const hasRichImagePreview = linkPreview.querySelector(
'.block-editor-link-control__search-item-image'
);

// Todo: refactor to use user-facing queries.
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the description.

const hasRichDescriptionPreview = linkPreview.querySelector(
'.block-editor-link-control__search-item-description'
);
Expand Down Expand Up @@ -1646,11 +1656,14 @@ describe( 'Rich link previews', () => {

await waitFor( () => expect( linkPreview ).toHaveClass( 'is-rich' ) );

// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the icon.

const iconPreview = linkPreview.querySelector(
`.block-editor-link-control__search-item-icon`
);

// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the svg.

const fallBackIcon = iconPreview.querySelector( 'svg' );
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the image.

const richIcon = iconPreview.querySelector( 'img' );

expect( fallBackIcon ).toBeVisible();
Expand Down Expand Up @@ -1680,6 +1693,7 @@ describe( 'Rich link previews', () => {
expect( linkPreview ).toHaveClass( 'is-rich' )
);

// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the preview item.

const missingDataItem = linkPreview.querySelector(
`.block-editor-link-control__search-item-${ dataItem }`
);
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/base-control/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ describe( 'BaseControl', () => {

expect( textarea ).toHaveAttribute( 'aria-details' );
expect(
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will have to be intentionally ignored, unless RTL gets better support for aria-details.

help.closest( `#${ textarea.getAttribute( 'aria-details' ) }` )
).toBeVisible();
} );
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/form-token-field/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ describe( 'FormTokenField', () => {

// This is testing implementation details, but I'm not sure there's
// a better way.
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the parent element or its parent element.

expect( input.parentElement?.parentElement ).toHaveClass(
'test-classname'
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ describe( 'withNotices operations', () => {
act( () => {
handle.current.createErrorNotice( message );
} );
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the notice wrapper.

expect( getByText( message )?.closest( '.is-error' ) ).not.toBeNull();
} );

Expand Down
1 change: 1 addition & 0 deletions packages/components/src/input-control/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe( 'InputControl', () => {
const help = screen.getByRole( 'link', { name: 'My help text' } );

expect(
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will have to be intentionally ignored, unless RTL gets better support for aria-details.

help.closest( `#${ input.getAttribute( 'aria-details' ) }` )
).toBeVisible();
} );
Expand Down
11 changes: 8 additions & 3 deletions packages/components/src/notice/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import Notice from '../index';

jest.mock( '@wordpress/a11y', () => ( { speak: jest.fn() } ) );

function getNoticeWrapper( container ) {
return container.firstChild;
}
Comment on lines +18 to +20
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a wrapper function for this doesn't only allow us to reduce code repetition and have the violation only in one place; it actually tricks the linter - there's no need for a rule ignore here.


describe( 'Notice', () => {
beforeEach( () => {
speak.mockReset();
Expand All @@ -39,15 +43,16 @@ describe( 'Notice', () => {

it( 'should not have is-dismissible class when isDismissible prop is false', () => {
const { container } = render( <Notice isDismissible={ false } /> );
const wrapper = getNoticeWrapper( container );

expect( container.firstChild ).toHaveClass( 'components-notice' );
expect( container.firstChild ).not.toHaveClass( 'is-dismissible' );
expect( wrapper ).toHaveClass( 'components-notice' );
expect( wrapper ).not.toHaveClass( 'is-dismissible' );
} );

it( 'should default to info status', () => {
const { container } = render( <Notice /> );

expect( container.firstChild ).toHaveClass( 'is-info' );
expect( getNoticeWrapper( container ) ).toHaveClass( 'is-info' );
} );

describe( 'useSpokenMessage', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/components/src/placeholder/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe( 'Placeholder', () => {

// Test for empty label. When the label is empty, the only way to
// query the div is with `querySelector`.
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This already includes an explanatory comment.

const label = placeholder.querySelector(
'.components-placeholder__label'
);
Expand All @@ -67,6 +68,7 @@ describe( 'Placeholder', () => {

// Test for non existent instructions. When the instructions is
// empty, the only way to query the div is with `querySelector`.
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This already includes an explanatory comment.

const placeholderInstructions = placeholder.querySelector(
'.components-placeholder__instructions'
);
Expand All @@ -84,6 +86,7 @@ describe( 'Placeholder', () => {

const placeholder = getPlaceholder();
const icon = within( placeholder ).getByTestId( 'icon' );
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the parent element.

expect( icon.parentNode ).toHaveClass(
'components-placeholder__label'
);
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/text-highlight/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import TextHighlight from '..';
const getMarks = ( container: Element ) =>
// Use querySelectorAll because the `mark` role is not officially supported
// yet. This should be changed to `getByRole` when it is.
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as I mentioned above for the still unsupported mark role.

Array.from( container.querySelectorAll( 'mark' ) );

const defaultText =
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/toolbar-group/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ describe( 'ToolbarGroup', () => {
const buttons = screen.getAllByRole( 'button' );

expect( buttons ).toHaveLength( 2 );
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the parent element.

expect( buttons[ 0 ].parentElement ).not.toHaveClass(
'has-left-divider'
);
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the parent element.

expect( buttons[ 1 ].parentElement ).toHaveClass(
'has-left-divider'
);
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/unit-control/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ describe( 'UnitControl', () => {
);

expect(
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently no accessible way to access the wrapper element of the unit control component.

withoutClassName.querySelector( '.components-unit-control' )
).not.toHaveClass( 'hello' );
expect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ describe( 'listener hook tests', () => {
const setAttribute = jest.fn();
const mockSelector = jest.fn();
beforeEach( () => {
// eslint-disable-next-line testing-library/no-node-access
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a false positive, so it should intentionally be ignored.

document.querySelector = mockSelector.mockReturnValue( {
setAttribute,
} );
Expand Down