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

test: more meaningful AnimatedNumber component test #8309

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

rahimrahman
Copy link
Contributor

@rahimrahman rahimrahman commented Nov 1, 2024

Summary

This was presented during Developer Meeting October 30th. The goal was to ask developers to re-consider the use of toMatchSnapshot which doesn't really give enough information what the component AnimatedNumber is doing and why it's rendering the way it did.

In order to get coverage % when running this test, you'd have to temporarily update the jest.config.js file:

-    coveragePathIgnorePatterns: ['/node_modules/', '/components/', '/screens/'],
+    coveragePathIgnorePatterns: ['/node_modules/'],
npm run test app/components/animated_number/index.test.tsx -- --collectCoverageFrom=app/components/animated_number/index.tsx --coverage --watch
 PASS  app/components/animated_number/index.test.tsx
  AnimatedNumber, using toMatchSnapshot
    ○ skipped should render correctly
    ○ skipped should rerender the correct number
  AnimatedNumber
    ✓ should render the non-animated number (107 ms)
    ✓ should removed the non-animation number after getting the correct height (7 ms)
    ✓ should switch to the animated number view (3 ms)
    ✓ KNOWN UI BUG: should show that there will be an issue if the text height changes, due to the non-animated number view has been removed (2 ms)
    should show the correct number of animated views based on the digits
      ✓ should display 1 view(s) for 1 (2 ms)
      ✓ should display 2 view(s) for 23 (2 ms)
      ✓ should display 3 view(s) for 579 (3 ms)
      ✓ should display 4 view(s) for -123 (3 ms)
      ✓ should display 4 view(s) for 6789 (3 ms)
      ✓ should display 5 view(s) for 23456 (4 ms)
    should show the correct number
      ✓ should display the number 123 (4 ms)
      ✓ should display the number 9982 (4 ms)
      ✓ should display the number 328 (3 ms)
      ✓ should display the number 1111 (3 ms)
      ✓ should display the number 2222 (4 ms)
      ✓ should display the number 3333 (3 ms)
    should rerender the correct number that it animates to
      ✓ should display the number 146 (5 ms)
      ✓ should display the number 144 (3 ms)
      ✓ should display the number 1 (2 ms)
      ✓ should display the number 1000000 (6 ms)
      ✓ should display the number -145 (4 ms)


=============================== Coverage summary ===============================
Statements   : 100% ( 37/37 )
Branches     : 100% ( 16/16 )
Functions    : 100% ( 13/13 )
Lines        : 100% ( 35/35 )
================================================================================

Ticket Link

Checklist

  • Added or updated unit tests (required for all new features)
  • Has UI changes
  • Includes text changes and localization file updates
  • Have tested against the 5 core themes to ensure consistency between them.
  • Have run E2E tests by adding label E2E iOS tests for PR.

Device Information

This PR was tested on:

Screenshots

Release Note


fireEvent(text, 'onLayout', {nativeEvent: {layout: {height: NUMBER_HEIGHT}}});

try {
fireEvent(text, 'onLayout', {nativeEvent: {layout: {height: NUMBER_HEIGHT}}});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The onLayout on non-animation-number view is not being triggered since it's no longer there. Even though we're using the same NUMBER_HEIGHT, the point is, it will not update to any number.

Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised that the RN version of Testing Library doesn't automatically fire onLayout events

let translateY = -1;

// every digit will have a row of 10 numbers, so the translateY should be the height of the number * the number * -1 (since the animation is going up)
await waitFor(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think I need a waitFor() anymore since I'm using a Promise.all() afterwards to make sure all the numbers are correct.

Copy link
Contributor

@streamer45 streamer45 left a comment

Choose a reason for hiding this comment

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

Great work on this @rahimrahman 💯

Left a couple of comments, but nothing blocking.

Comment on lines +10 to +11
// jest.mock('../../../node_modules/react-native/Libraries/Text/Text', () => {
// const Text = jest.requireActual('../../../node_modules/react-native/Libraries/Text/Text');
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this meant to stay commented out? If so, could we leave a comment explaining its purpose?

expect(animatedView).toBeTruthy();
});

describe.each([1, 23, 579, -123, 6789, 23456])('should show the correct number of animated views based on the digits', (animateToNumber: number) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is more of a general question, but ideally, I'd like to find consistency in how we identify test cases, and in this specific instance, it seems like there's some pattern here (testing numbers with increasing digits + negative case).

I wonder whether some fuzz testing could be valuable in such cases rather than arbitrarily choosing some values. I don't have an answer here; I'm just curious to hear more thoughts.

Comment on lines +169 to +170
} catch (e) {
expect(e).toBeTruthy();
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason for not using the .rejects.toThrow('error'); pattern in here?

@hmhealey hmhealey added the 2: Dev Review Requires review by a core commiter label Nov 4, 2024
Copy link
Member

@hmhealey hmhealey left a comment

Choose a reason for hiding this comment

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

I have a few questions and one request for a helper method, but I definitely like the way this looks compared to the older version even for a more complicated component like this one

describe('AnimatedNumber', () => {
// running on jest, since Animated is a native module, Animated.timing.start needs to be mocked in order to update to the final Animated.Value.
// Ex: 1 => 2, the Animated.Value should be -20 (from -10) after the animation is done
jest.spyOn(Animated, 'timing').mockImplementation((a, b) => ({
Copy link
Member

Choose a reason for hiding this comment

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

Are we likely to need this when testing other components? I wonder if we might want to mock this globally be adding it to test/setup.ts

it('should render the non-animated number', () => {
const {getByTestId} = render(<AnimatedNumber animateToNumber={123}/>);

const text = getByTestId('no-animation-number');
Copy link
Member

Choose a reason for hiding this comment

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

On web, I've been recommending people to get elements with methods like screen.getByLabelText instead of getByTestId since it uses the accessible text. Is there an equivalent on mobile?

From what I understand, our accessibility support on mobile is pretty bad so I wouldn't be surprised if there's an option but not one that we can use

});

describe.each([1, 23, 579, -123, 6789, 23456])('should show the correct number of animated views based on the digits', (animateToNumber: number) => {
it(`should display ${animateToNumber.toString().length} view(s) for ${animateToNumber}`, async () => {
Copy link
Member

Choose a reason for hiding this comment

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

We might want to replace any occurrences of X.toString().length with a helper method like countDigits(X) since it took me a minute to remember what that did. Giving it a name would help make it clearer what we're trying to do with it.

On my first read of this, I thought this meant that it made animateToNumber views, and I was going to comment on how that would cause problems when there 😅

fireEvent(text, 'onLayout', {nativeEvent: {layout: {height: NUMBER_HEIGHT}}});

try {
fireEvent(text, 'onLayout', {nativeEvent: {layout: {height: NUMBER_HEIGHT}}});
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised that the RN version of Testing Library doesn't automatically fire onLayout events

@larkox
Copy link
Contributor

larkox commented Nov 5, 2024

@rahimrahman Maybe I am missing something, but aren't we missing the snapshots on the PR? I am surprised the CI did not yell about that. Maybe something we should look into.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2: Dev Review Requires review by a core commiter release-note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants