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

ColorPicker: Add component test for Copy button with clipboard mocking #69223

Open
wants to merge 6 commits into
base: trunk
Choose a base branch
from

Conversation

im3dabasia
Copy link
Contributor

@im3dabasia im3dabasia commented Feb 17, 2025

Work in Progress

  • Work on adding/updating the test to include all three format selectors: HSL, HEX, and RGB.

Follow-up PR for: #49698 (comment)

I'd like to add another test for the Copy button, but I wasn't quite sure how to properly mock it. Rather than delay this PR, I'll put this on a list to come back to later.

What?

Adds a component test for the Copy button in the ColorPicker component, ensuring clipboard functionality is properly mocked and tested.

Why?

The Copy button functionality wasn't previously tested. This PR introduces a test to verify that the correct color value is copied to the clipboard when the button is clicked.

How?

  1. Mocked navigator.clipboard.readText using jest.spyOn.
  2. Simulates user interactions with userEvent to select a format and trigger the copy action.
  3. Verifies that the copied text matches the expected color value.

Testing Instructions

  1. Run the test by the following command:
npm run test:unit packages/components/src/color-picker/test/index.tsx
  1. Ensure all the tests pass

Screenshot

Screenshot 2025-02-18 at 2 10 29 PM

@im3dabasia im3dabasia marked this pull request as ready for review February 18, 2025 08:47
Copy link

github-actions bot commented Feb 18, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: im3dabasia <[email protected]>
Co-authored-by: Mamaduka <[email protected]>
Co-authored-by: ciampo <[email protected]>
Co-authored-by: mirka <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@im3dabasia
Copy link
Contributor Author

im3dabasia commented Feb 18, 2025

Hi @Mamaduka,

I have added a test for the copy functionality in the ColorPicker component. When you have a moment, please review my PR.

As a follow-up, I can create a comprehensive list of components where similar functionality exists, so we can add tests for them as well. I believe this is a commonly used feature, and adding tests for it would be beneficial.

Let me know what you think.

cc: @chad1008

Copy link
Member

@Mamaduka Mamaduka left a comment

Choose a reason for hiding this comment

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

Thanks, @im3dabasia!

I'm not sure if the current shape of the test is proper. It's mocking the readText to return the color value and then asserts that it equals the same color value.

It seems that we're not actually testing anything.

@im3dabasia
Copy link
Contributor Author

Thanks, @im3dabasia!

I'm not sure if the current shape of the test is proper. It's mocking the readText to return the color value and then asserts that it equals the same color value.

It seems that we're not actually testing anything.

Hi @Mamaduka,

Thank you for your feedback! While it may seem like we aren’t testing much, we are indeed verifying the copy functionality in a way that aligns with how the clipboard works in the testing environment.

Since navigator.clipboard is not available by default in a test environment (as it requires a secure context), I had to mock the readText API. After researching similar challenges that other developers faced, I found useful references, including [Stack Overflow link1], [Stack Overflow link2], and [a blog post].

Quoting the documentation:
"Note that the Clipboard API is usually not available outside of a secure context.
To enable testing of workflows involving the clipboard, userEvent.setup() replaces window.navigator.clipboard with a stub."

Related reference

I initially tried a different approach, assuming that since userEvent.setup({writeToClipboard:true}) mocks the clipboard API, we could directly store the copied value from the internal mock and compare it. However, when I attempted this, the stored value ended up being undefined, meaning this method didn’t work as expected.

const color = '#000';
const user = userEvent.setup( { writeToClipboard: true } );

render( <ColorPicker color={ color } enableAlpha={ false } /> );

const copyButton = screen.getByRole( 'button', { name: 'Copy' } );
expect( copyButton ).toBeVisible();

await user.click( copyButton );

const pastedText = await user.paste();

expect( pastedText ).toBe( color );

Would love to hear your thoughts on this! Let me know if you have any suggestions or if there's a better way to handle this.

@Mamaduka
Copy link
Member

Maybe @WordPress/gutenberg-components have a suggestion here.

@ciampo
Copy link
Contributor

ciampo commented Feb 19, 2025

After a quick look, I also agree with @Mamaduka that we're not really testing the copy functionality. If we do provide a mock, I feel like it should be a more feature-full mock, able to "copy" and "store" the copied text, and return it when the clipboard is "read"— ie. without any values being hardcoded.

I also wanted to point out that the copy button uses the useCopyToClipboard hook from @wordpress/compose, which internally uses the clipboard library. In other words, we may not need to mock navigator (in case clipboard implements the functionality in a different way).

@im3dabasia
Copy link
Contributor Author

im3dabasia commented Feb 19, 2025

I also wanted to point out that the copy button uses the useCopyToClipboard hook from @wordpress/compose, which internally uses the clipboard library. In other words, we may not need to mock navigator (in case clipboard implements the functionality in a different way).

Thank you, @ciampo! After looking into it further, I discovered that useCopyToClipboard relies on the clipboard package, which internally handles clipboard operations using document.execCommand('copy'). I’m now able to successfully copy data to the clipboard as expected, and it seems that an actual copy is being made rather than just mocking and returning a dummy value.

Thank you @ciampo and @Mamaduka for your wonderful insights.

EDIT:

To correctly simulate the entire copy workflow, we need to mock both document.execCommand('copy') and the internal logic of the hook—which involves creating a temporary <textarea>, assigning the value to it, selecting the text, copying it, and then removing the temporary <textarea> from the DOM.

I will soon have the code polished and send it for review.

@mirka
Copy link
Member

mirka commented Feb 20, 2025

To correctly simulate the entire copy workflow, we need to mock both document.execCommand('copy') and the internal logic of the hook—which involves creating a temporary <textarea>, assigning the value to it, selecting the text, copying it, and then removing the temporary <textarea> from the DOM.

Since it seems that this is a lot of work to mock, and ColorPicker is just a consumer of these upstream clipboard utilities, another strategy that comes to mind here is to simply assert that useCopyToClipboard is being called correctly.

jest.mock( '@wordpress/compose', () => ( {
	...jest.requireActual( '@wordpress/compose' ),
	useCopyToClipboard: jest.fn(),
} ) );
// there might be a cleaner way to do this 😅
const value = useCopyToClipboard.mock.calls[ 0 ][ 0 ]();
expect( value ).toBe( '#000' );

Something like that? And then maybe we can do the heavy duty testing for useCopyToClipboard itself.

Just a thought.

@im3dabasia
Copy link
Contributor Author

Something like that? And then maybe we can do the heavy duty testing for useCopyToClipboard itself.

Just a thought.

Hey @mirka, @ciampo ,

I appreciate your feedback! I will explore this idea as well. I've pushed a couple of commits where I simulated an actual copy. Could you please check them when you have a moment?

@t-hamano t-hamano added [Type] Code Quality Issues or PRs that relate to code quality [Package] Components /packages/components labels Feb 20, 2025
@ciampo
Copy link
Contributor

ciampo commented Feb 26, 2025

I agree with @mirka — for the purpose of ColorPicker, we don't want to mock the internals of useCopyToClipboard (including document.createElement). Lena's suggested plan sounds like the better compromise to me.

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.

5 participants