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

FontSizePicker: Rewrite unit tests to use userEvent and be more comprehensive #45298

Merged
merged 12 commits into from
Nov 1, 2022

Conversation

noisysocks
Copy link
Member

What?

Rewrites the FontSizePicker unit tests to use userEvent where possible and be more comprehensive.

Why?

I want to make @ciampo happy 🙂 see #44891 (review).

How?

I looked at the FontSizePicker component storybook, listed all of the functionality that I could think of, and then wrote new tests from scratch that covered everything that I listed.

Testing Instructions

npm run test, obviously, but the more important thing is to review the code and check that I haven't missed anything.

Screenshots or screencast

@noisysocks noisysocks added [Type] Code Quality Issues or PRs that relate to code quality [Package] Components /packages/components labels Oct 26, 2022
@ciampo ciampo requested review from mirka and chad1008 October 26, 2022 08:34
Copy link
Member

@ramonjd ramonjd left a comment

Choose a reason for hiding this comment

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

Thanks for improving the unit tests. Good coverage 👍

I left some questions about testing the header hint and labels.

it( 'should fallback to font size `slug` for header hint label if `name` is undefined', () => {
const fontSizes = [
{
slug: 'gigantosaurus',
Copy link
Member

Choose a reason for hiding this comment

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

Do you have something against gigantosauruses? 🦖

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right, I'll put her back.

value={ value }
/>
);
// TODO: There's no accessible way to select the label.
Copy link
Member

Choose a reason for hiding this comment

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

There's been a label since #45180 (slightly modified in #45298) as far as I'm aware.

Copy link
Member Author

Choose a reason for hiding this comment

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

How did I miss that! I'll query for the existence of an element with that label 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

I was also going to point out that in case you need to do exact test matching across multiple DOM elements, you can pass a custom matcher to screen.getByText:

function textContentMatcher( textMatch: string | RegExp ) {
	const hasText =
		typeof textMatch === 'string'
			? ( node: Element | null ) => node?.textContent === textMatch
			: ( node: Element | null ) =>
					textMatch.test( node?.textContent ?? '' );

	const matcherFn: MatcherFunction = ( _content, node ) => {
		if ( ! hasText( node ) ) {
			return false;
		}

		const childrenDontHaveText = Array.from( node?.children || [] ).every(
			( child ) => ! hasText( child )
		);

		return childrenDontHaveText;
	};

	return matcherFn;
}

// later, in a test:
const label = screen.getByText(
	textContentMatcher( expectedHint )
);

const label = document.querySelector(
'.components-base-control__label'
);
expect( label ).toHaveTextContent( expectedHint );
Copy link
Member

@ramonjd ramonjd Oct 28, 2022

Choose a reason for hiding this comment

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

The expectedHint in this test case doesn't look right to me.

Screen Shot 2022-10-28 at 12 20 17 pm

Could it be that the test is passing because toHaveTextContent does partial matches?

If we're only interested in testing whether the accessible label and text hint are set correctly, then another option might be to just pass a single fontSize for the label you want to test:

		test.each( [
			{
				value: undefined,
				expectedHint: 'Size Default',
				fontSizePresets: [ fontSizes[ 0 ] ],
			},
			{
				value: '8px',
				expectedHint: 'Size Tiny(px)',
				fontSizePresets: [ fontSizes[ 0 ] ],
			},
			{
				value: '3px',
				expectedHint: 'Size (Custom)',
				fontSizePresets: [ fontSizes[ 0 ] ],
			},
		] )(
			'displays $expectedHint as header hint when value is $value',
			( { value, expectedHint, fontSizePresets } ) => {
				render(
					<FontSizePicker
						__nextHasNoMarginBottom
						fontSizes={ fontSizePresets }
						value={ value }
					/>
				);
				const label = screen.getByLabelText( expectedHint );
				expect( label ).toBeInTheDocument();
				expect( label ).toHaveTextContent(
					// Removes whitespaces to match text interspersed in HTML elements.
					expectedHint.replace( /\s*/g, '' )
				);
			}
		);

This avoids have to fire an event to click on preset buttons and what not.

Copy link
Member Author

Choose a reason for hiding this comment

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

By fixing your other comment I think I inadvertently fixed this, good catch 😀

@noisysocks
Copy link
Member Author

Thanks @ramonjd! Updated this per your feedback and added a changelog entry.

@ciampo
Copy link
Contributor

ciampo commented Oct 28, 2022

About to post my review as, sorry for the delay!

Copy link
Contributor

@ciampo ciampo left a comment

Choose a reason for hiding this comment

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

Thank you @noisysocks for working on this!!

Apart from the inline comments, it looks like we're not adding tests for:

  • "complex" CSS values (e.g clamp(1.75rem, 3vw, 2.25rem))
  • font sizes with different units (i.e some in px, some in rem, ...)

Those are probably some of the harder to test edge cases, and therefore it would be nice to have unit tests for those too!

I want to make @ciampo happy

I'm flattered 😊

packages/components/src/font-size-picker/test/index.tsx Outdated Show resolved Hide resolved
value={ value }
/>
);
// TODO: There's no accessible way to select the label.
Copy link
Contributor

Choose a reason for hiding this comment

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

I was also going to point out that in case you need to do exact test matching across multiple DOM elements, you can pass a custom matcher to screen.getByText:

function textContentMatcher( textMatch: string | RegExp ) {
	const hasText =
		typeof textMatch === 'string'
			? ( node: Element | null ) => node?.textContent === textMatch
			: ( node: Element | null ) =>
					textMatch.test( node?.textContent ?? '' );

	const matcherFn: MatcherFunction = ( _content, node ) => {
		if ( ! hasText( node ) ) {
			return false;
		}

		const childrenDontHaveText = Array.from( node?.children || [] ).every(
			( child ) => ! hasText( child )
		);

		return childrenDontHaveText;
	};

	return matcherFn;
}

// later, in a test:
const label = screen.getByText(
	textContentMatcher( expectedHint )
);

);
expect( screen.getByLabelText( 'Custom' ) ).toBeInTheDocument();
// TODO: onChange() shouldn't be called.
//expect( onChange ).not.toHaveBeenCalled();
Copy link
Contributor

Choose a reason for hiding this comment

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

Good find! Adding a comment here so that we don't miss this as a follow-up

Copy link
Member Author

Choose a reason for hiding this comment

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

It's one of the things we fixed in #44891 :)

packages/components/src/font-size-picker/test/index.tsx Outdated Show resolved Hide resolved
packages/components/src/font-size-picker/test/index.tsx Outdated Show resolved Hide resolved
@noisysocks
Copy link
Member Author

Addressed some of the feedback. I'll add more tests for non-px values tomorrow.

@noisysocks
Copy link
Member Author

Okey dokey, I've added tests for non-px and clamp() values and added assertions for the number of times onChange is called. Should be good to go 👍

@noisysocks
Copy link
Member Author

Merging this so I can unblock #44891 and #44966. Thanks for the feedback @ciampo!

@noisysocks noisysocks merged commit fe31535 into trunk Nov 1, 2022
@noisysocks noisysocks deleted the update/font-size-picker-unit-tests branch November 1, 2022 06:20
@github-actions github-actions bot added this to the Gutenberg 14.5 milestone Nov 1, 2022
@ciampo
Copy link
Contributor

ciampo commented Nov 1, 2022

Thank YOU for always taking the time to make significant improvements to the components you work on!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Components /packages/components [Type] Code Quality Issues or PRs that relate to code quality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants