Closed hannojg closed 1 month ago
### Fixed Issues
section aboveTests
sectionQA steps
sectiontoggleReport
and not onIconClick
).myBool && <MyComponent />
.src/languages/*
files and using the translation methodSTYLE.md
) were followedAvatar
, I verified the components using Avatar
have been tested & I retested again)/** comment above it */
this
properly so there are no scoping issues (i.e. for onClick={this.submit}
the method this.submit
should be bound to this
in the constructor)this
are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this);
if this.submit
is never passed to a component event handler like onClick
)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG
)Avatar
is modified, I verified that Avatar
is working as expected in all cases)Design
label and/or tagged @Expensify/design
so the design team can review the changes.ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test
steps.hm, lets wait one moment before we continue, want to ask Fabio one thing who wrote the useOnxy hook. Maybe we don't need the alwaysNotify
prop (conversation here)
The changes look good ! I didn't find any regressions So we are waiting for information about alwaysNotify and tests (the slack thread)
Yes, thanks for being patient with me!
I figured that the alwaysNotify
isn't needed.
There was this test that failed with the changes I made:
The thing is that the test case was a bit broken. In the test we change the selector function reference. In a react application that will only happen when the hook gets called again aka. the component re-renders (if we follow all strict rules, which we do). The test was working before this change only because the Onyx.connect callback was called another time (which will cause a rerender in useOnyx), which is what this PR fixes:
So the solution was to fix the test by simulating how in a regular react app the selector ref would change - with a rerender
@hannojg Thanks for the detailed explanation ! Fix for tests makes sense to me ! LGTM
@mountiny I think we are good to merge!
🚀Published to npm in v2.0.58
@hannojg I noticed that these changes are causing some tests in E/App to fail. Is this expected?
Summary of all failing tests
FAIL tests/unit/LocalizeTests.ts
● localize › formatList › formatList([ 'rory', 'vit' ])
expect(received).toBe(expected) // Object.is equality
Expected: "rory y vit"
Received: "rory and vit"
55 | ])('formatList(%s)', (input, {[CONST.LOCALES.DEFAULT]: expectedOutput, [CONST.LOCALES.ES]: expectedOutputES}) => {
56 | expect(Localize.formatList(input)).toBe(expectedOutput);
> 57 | return Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(Localize.formatList(input)).toBe(expectedOutputES));
| ^
58 | });
59 | });
60 | });
at toBe (tests/unit/LocalizeTests.ts:57:124)
● localize › formatList › formatList([ 'rory', 'vit', 'jules' ])
expect(received).toBe(expected) // Object.is equality
Expected: "rory, vit y jules"
Received: "rory, vit, and jules"
55 | ])('formatList(%s)', (input, {[CONST.LOCALES.DEFAULT]: expectedOutput, [CONST.LOCALES.ES]: expectedOutputES}) => {
56 | expect(Localize.formatList(input)).toBe(expectedOutput);
> 57 | return Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(Localize.formatList(input)).toBe(expectedOutputES));
| ^
58 | });
59 | });
60 | });
at toBe (tests/unit/LocalizeTests.ts:57:124)
● localize › formatList › formatList([ 'rory', 'vit', 'ionatan' ])
expect(received).toBe(expected) // Object.is equality
Expected: "rory, vit e ionatan"
Received: "rory, vit, and ionatan"
55 | ])('formatList(%s)', (input, {[CONST.LOCALES.DEFAULT]: expectedOutput, [CONST.LOCALES.ES]: expectedOutputES}) => {
56 | expect(Localize.formatList(input)).toBe(expectedOutput);
> 57 | return Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(Localize.formatList(input)).toBe(expectedOutputES));
| ^
58 | });
59 | });
60 | });
at toBe (tests/unit/LocalizeTests.ts:57:124)
FAIL tests/unit/CurrencyUtilsTest.ts (5.087 s)
● CurrencyUtils › isCurrencySymbolLTR › Returns false for preferredLocale es and currencyCode USD
expect(received).toBe(expected) // Object.is equality
Expected: false
Received: true
53 | Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, locale).then(() => {
54 | const isSymbolLeft = CurrencyUtils.isCurrencySymbolLTR(currencyCode);
> 55 | expect(isSymbolLeft).toBe(isLeft);
| ^
56 | }),
57 | );
58 | });
at toBe (tests/unit/CurrencyUtilsTest.ts:55:38)
● CurrencyUtils › convertToDisplayString › Correctly displays EUR in ES locale
expect(received).toBe(expected) // Object.is equality
Expected: "0,25 €"
Received: "€0.25"
168 | ['EUR', 250000000, '2.500.000,00\xa0€'],
169 | ])('Correctly displays %s in ES locale', (currency, amount, expectedResult) =>
> 170 | Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(CurrencyUtils.convertToDisplayString(amount, currency)).toBe(expectedResult)),
| ^
171 | );
172 | });
173 |
at toBe (tests/unit/CurrencyUtilsTest.ts:170:145)
● CurrencyUtils › convertToDisplayString › Correctly displays EUR in ES locale
expect(received).toBe(expected) // Object.is equality
Expected: "25,00 €"
Received: "€25.00"
168 | ['EUR', 250000000, '2.500.000,00\xa0€'],
169 | ])('Correctly displays %s in ES locale', (currency, amount, expectedResult) =>
> 170 | Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(CurrencyUtils.convertToDisplayString(amount, currency)).toBe(expectedResult)),
| ^
171 | );
172 | });
173 |
at toBe (tests/unit/CurrencyUtilsTest.ts:170:145)
● CurrencyUtils › convertToDisplayString › Correctly displays EUR in ES locale
expect(received).toBe(expected) // Object.is equality
Expected: "2500,00 €"
Received: "€2,500.00"
168 | ['EUR', 250000000, '2.500.000,00\xa0€'],
169 | ])('Correctly displays %s in ES locale', (currency, amount, expectedResult) =>
> 170 | Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(CurrencyUtils.convertToDisplayString(amount, currency)).toBe(expectedResult)),
| ^
171 | );
172 | });
173 |
at toBe (tests/unit/CurrencyUtilsTest.ts:170:145)
● CurrencyUtils › convertToDisplayString › Correctly displays EUR in ES locale
expect(received).toBe(expected) // Object.is equality
Expected: "2.500.000,00 €"
Received: "€2,500,000.00"
168 | ['EUR', 250000000, '2.500.000,00\xa0€'],
169 | ])('Correctly displays %s in ES locale', (currency, amount, expectedResult) =>
> 170 | Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(CurrencyUtils.convertToDisplayString(amount, currency)).toBe(expectedResult)),
| ^
171 | );
172 | });
173 |
at toBe (tests/unit/CurrencyUtilsTest.ts:170:145)
● CurrencyUtils › convertToShortDisplayString › Correctly displays EUR in ES locale
expect(received).toBe(expected) // Object.is equality
Expected: "0 €"
Received: "€0"
195 | ['EUR', 250000000, '2.500.000\xa0€'],
196 | ])('Correctly displays %s in ES locale', (currency, amount, expectedResult) =>
> 197 | Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(CurrencyUtils.convertToShortDisplayString(amount, currency)).toBe(expectedResult)),
| ^
198 | );
199 | });
200 | });
at toBe (tests/unit/CurrencyUtilsTest.ts:197:150)
● CurrencyUtils › convertToShortDisplayString › Correctly displays EUR in ES locale
expect(received).toBe(expected) // Object.is equality
Expected: "25 €"
Received: "€25"
195 | ['EUR', 250000000, '2.500.000\xa0€'],
196 | ])('Correctly displays %s in ES locale', (currency, amount, expectedResult) =>
> 197 | Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(CurrencyUtils.convertToShortDisplayString(amount, currency)).toBe(expectedResult)),
| ^
198 | );
199 | });
200 | });
at toBe (tests/unit/CurrencyUtilsTest.ts:197:150)
● CurrencyUtils › convertToShortDisplayString › Correctly displays EUR in ES locale
expect(received).toBe(expected) // Object.is equality
Expected: "2500 €"
Received: "€2,500"
195 | ['EUR', 250000000, '2.500.000\xa0€'],
196 | ])('Correctly displays %s in ES locale', (currency, amount, expectedResult) =>
> 197 | Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(CurrencyUtils.convertToShortDisplayString(amount, currency)).toBe(expectedResult)),
| ^
198 | );
199 | });
200 | });
at toBe (tests/unit/CurrencyUtilsTest.ts:197:150)
● CurrencyUtils › convertToShortDisplayString › Correctly displays EUR in ES locale
expect(received).toBe(expected) // Object.is equality
Expected: "2.500.000 €"
Received: "€2,500,000"
195 | ['EUR', 250000000, '2.500.000\xa0€'],
196 | ])('Correctly displays %s in ES locale', (currency, amount, expectedResult) =>
> 197 | Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(CurrencyUtils.convertToShortDisplayString(amount, currency)).toBe(expectedResult)),
| ^
198 | );
199 | });
200 | });
at toBe (tests/unit/CurrencyUtilsTest.ts:197:150)
Test Suites: 2 failed, 2 skipped, 86 passed, 88 of 90 total
Tests: 12 failed, 154 skipped, 928 passed, 1094 total
Snapshots: 1 passed, 1 total
Time: 22.677 s
Ran all test suites.
They are failing due to another bug in Onyx for which I have a PR to fix here:
Details
This PR fixes cases where a call to
Onyx.set
could invoke aOnyx.connect
listener twice. ForwithOnyx
connections this isn't happening, as they are already guarded by previous equal comparisons.The fix here is to add a map that will store all previous values that were send to the
Onyx.connect
callback. We can't reuse the cache here unfortunately, as it can contain values while no data has been send to the connection yet (and thus a comparison would lead to us not sending to the connection.For useOnyx I had to add a special parameter to
Onyx.connect
calledalwaysNotify
which is false by default.useOnyx
might uses a selector to transform the data. The output of that selector can change, even though the input data stay the same. Thus foruseOnyx
we always have to call the connect callback.Related Issues
https://github.com/Expensify/react-native-onyx/issues/569
Automated Tests
Manual Tests
n/a
Author Checklist
### Related Issues
section aboveTests
sectiontoggleReport
and notonIconClick
)myBool && <MyComponent />
.STYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)/** comment above it */
this
properly so there are no scoping issues (i.e. foronClick={this.submit}
the methodthis.submit
should be bound tothis
in the constructor)this
are necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);
ifthis.submit
is never passed to a component event handler likeonClick
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps.Screenshots/Videos
Note: pressing the send button might be buggy in the recordings, its a know issue first reported here and is not related to this PR.
Android: Native
https://github.com/user-attachments/assets/a3ca2efc-78a4-4c7f-8b92-4f00b6fe7fa7Android: mWeb Chrome
https://github.com/user-attachments/assets/1d83dd10-3e12-41a2-b885-ad61120f2a92iOS: Native
https://github.com/user-attachments/assets/07a76549-87ac-42b7-a65c-eb4f6148c9ceiOS: mWeb Safari
https://github.com/user-attachments/assets/3e962c75-ab38-44cb-b78b-a78adebcfca4MacOS: Chrome / Safari
https://github.com/user-attachments/assets/b031aa42-2241-4628-aa9f-d77032f10bf9MacOS: Desktop
https://github.com/user-attachments/assets/50ad6d61-170b-4b5a-af9f-414aa32de967