-
Notifications
You must be signed in to change notification settings - Fork 15
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
Refactor Link to add more cheap unit tests #4553
base: master
Are you sure you want to change the base?
Refactor Link to add more cheap unit tests #4553
Conversation
|
nativeHTMLAttributes: Omit<Props, keyof BaseProps> | ||
} | ||
|
||
/* eslint-disable complexity */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ES lint wants code to have no more 10 branches. For this kind of function this does not make sense, code is not complex (this error existed before refactoring too)
} | ||
|
||
/* eslint-disable complexity */ | ||
export const calculateViewModel = (props: Props): ViewModel => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pure function return all info needed to render Link without rendering Link.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OleksandrNechai Do you think that calculateViewModel
might do too many things at once? I tried to split the returned pieces of "model" into groups, but have not succeeded, though.
In terms of code structure the function seems to be a good approach, but I am a bit worried that it does so many unrelated calculations, as in case of Link component the model itself has so many heterogeneous properties, so it might affect the readability a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it totally does a lot of things. And yet I find this function simple because when you look at it, each section of code calculates one prop and they are mostly not intertwined. I tried to decompose this function in smaller ones, and result was not great. Extra functions would take 5 params and return one output and hide 2 lines of code. This make code more complicated, as I did not manage to extract "deep" function: https://medium.com/@nathan.fooo/4-notes-modules-should-be-deep-ba5671c4288c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OleksandrNechai Agree!
Although the function still looks shallow as on the picture from https://medium.com/@nathan.fooo/4-notes-modules-should-be-deep-ba5671c4288c, I have not strong preference 👍
packages/base/Link/src/Link/test.tsx
Outdated
@@ -99,3 +100,222 @@ describe('Link', () => { | |||
expect(getByTestId('foo')).not.toHaveAttribute('href') | |||
}) | |||
}) | |||
|
|||
describe('calculateViewModel', () => { | |||
it('should apply default values when no props are provided', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not start test description with should, because it doesn’t bring value.
packages/base/Link/src/Link/test.tsx
Outdated
expect(result.ariaDisabled).toBe(true) | ||
}) | ||
|
||
it('should apply visited class when visited is true', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have to wrap the use case with describe section to accentuate a condition when that test case must happen.
packages/base/Link/src/Link/test.tsx
Outdated
}) | ||
|
||
describe('when href, target, rel and onClick are provided', () => { | ||
it('applies correct href, target, rel, and onClick', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OleksandrNechai I wonder what does it mean "correct" href
, target
, etc.? Describing it might take more time than writing tests, but then maybe there is no value in this test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job!
Very nice way to simplify component testing by extracting logic to helper function 👍
const result = calculateViewModel(props) | ||
|
||
expect(result.href).toBe('https://example.com') | ||
expect(result.target).toBe('_blank') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe let's use a variable for those values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
4d500ab
to
a224ead
Compare
a224ead
to
b32a83c
Compare
[FX-NNNN]
Description
This if a follow up of f8e5c8f and #4547. Where I did not test code due to inability to add cheap test.
Extract all view logic into separate pure function. This allows adding many tests that run in few ms, raising confidence in our code without making tests suits running forever.
How to test
Locally
yarn test:unit:watch
Development checks
picasso-tailwind-merge
requires major update (check itsREADME.md
)props
in component with documentationexamples
for componentBreaking change
PR commands
List of available commands:
@toptal-bot run package:alpha-release
- Release alpha version@toptal-anvil ping reviewers
- Ping FX team for reviewPR Review Guidelines
When to approve? ✅
You are OK with merging this PR and
nit:
to your comment. (ex.nit: I'd rename this variable from makeCircle to getCircle
)When to request changes? ❌
You are not OK with merging this PR because
When to comment (neither ✅ nor ❌)
You want your comments to be addressed before merging this PR in cases like:
How to handle the comments?