-
Notifications
You must be signed in to change notification settings - Fork 699
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
tests: add tests for some components #12108
Changes from 9 commits
d78d450
e60ed4a
8ada86e
fe2d045
4a9db2b
52ffdc7
b5ee478
e57933e
74b0bd1
030017c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not very sure if the way I have chosen is the most user-centric way to test this |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { render, screen, fireEvent } from '@testing-library/vue'; | ||
Check failure on line 1 in kolibri/core/assets/src/views/__tests__/FocusTrap.spec.js GitHub Actions / All file linting
|
||
import userEvent from '@testing-library/user-event'; | ||
import FocusTrap from '../FocusTrap.vue'; | ||
import FocusTrapWrapper from './FocusTrapWrapper.vue'; | ||
|
||
const renderComponent = (props = {}) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The FocusTrap tests look good. Not a blocker -- the only thing I could think that might be worth adding is a call to the public There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it! That's an important use case. Will add tests for it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added a test suite to cover this use-case, but I can't get it to work. Like to access the public method There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a case where the 'user' of the component is actually the developer, so I don't think it's necessary to do this via DOM interaction, and the reset method can be invoked programmatically - as it forms part of the public API of the component. |
||
return render(FocusTrap, { | ||
props: { | ||
disabled: false, | ||
...props, | ||
}, | ||
}); | ||
}; | ||
|
||
describe('FocusTrap', () => { | ||
it('should emit the "shouldFocusFirstEl" element when the tab key is pressed once', async () => { | ||
const { emitted } = renderComponent(); | ||
|
||
await userEvent.tab(); | ||
expect(emitted()).toHaveProperty('shouldFocusFirstEl'); | ||
expect(emitted().shouldFocusFirstEl.length).toBe(1); | ||
}); | ||
|
||
it("should trap the focus and emit 'shouldFocusFirstEl' if the last focusable element is focused and we focus the next element", async () => { | ||
const { emitted } = renderComponent(); | ||
|
||
await userEvent.tab(); | ||
await userEvent.tab(); | ||
|
||
expect(emitted()).toHaveProperty('shouldFocusFirstEl'); | ||
expect(emitted().shouldFocusFirstEl.length).toBe(2); | ||
}); | ||
|
||
it('should trap the focus and emit "shouldFocusLastEl" when the first element is focused and we focus the previous element', async () => { | ||
const { emitted } = renderComponent(); | ||
|
||
await userEvent.tab(); | ||
await userEvent.tab(); | ||
|
||
// Shift + Tab is used to focus on the initial element again | ||
await userEvent.tab({ shift: true }); | ||
|
||
expect(emitted()).toHaveProperty('shouldFocusLastEl'); | ||
expect(emitted().shouldFocusLastEl.length).toBe(1); | ||
}); | ||
|
||
it("should not trap focus when 'disabled' prop is set to true", async () => { | ||
const { emitted } = renderComponent({ disabled: true }); | ||
|
||
await userEvent.tab(); | ||
expect(emitted()).not.toHaveProperty('shouldFocusFirstEl'); | ||
|
||
await userEvent.tab(); | ||
expect(emitted()).not.toHaveProperty('shouldFocusFirstEl'); | ||
|
||
await userEvent.tab({ shift: true }); | ||
expect(emitted()).not.toHaveProperty('shouldFocusLastEl'); | ||
}); | ||
|
||
// it("should reset state when 'reset' method is called", async () => { | ||
// // FocusTrapWrapper is used to test the FocusTrap component's reset method | ||
// // It has a button which calls the reset method of the FocusTrap component | ||
// const { emitted } = render(FocusTrapWrapper); | ||
|
||
// await fireEvent.focus(screen.getByTestId('focusTrap')); | ||
// // Activate the focus trap | ||
// await userEvent.tab(); | ||
// await userEvent.tab(); | ||
// await userEvent.tab({ shift: true }); | ||
|
||
// // The focus trap should be active | ||
// expect(emitted()).toHaveProperty('shouldFocusLastEl'); | ||
// expect(emitted().shouldFocusLastEl.length).toBe(1); | ||
|
||
// // Reset the focus trap | ||
// await userEvent.click(screen.getByRole('button')); | ||
|
||
// await userEvent.tab(); | ||
// expect(emitted()).toHaveProperty('shouldFocusFirstEl'); | ||
// expect(emitted().shouldFocusFirstEl.length).toBe(2); | ||
// }); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<!-- This is a wrapper around the FocusTrap component | ||
to test it's exported public "reset" method --> | ||
<template> | ||
|
||
<FocusTrap ref="focusTrap" data-testid="focusTrap" :disabled="false"> | ||
<button @click="reset"> | ||
Reset | ||
</button> | ||
</FocusTrap> | ||
|
||
</template> | ||
|
||
<script> | ||
|
||
import FocusTrap from '../FocusTrap.vue'; | ||
|
||
export default { | ||
name: 'FocusTrapWrapper', | ||
components: { | ||
FocusTrap, | ||
}, | ||
methods: { | ||
reset() { | ||
this.$refs.focusTrap.reset(); | ||
}, | ||
}, | ||
}; | ||
|
||
</script> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { render, screen } from '@testing-library/vue'; | ||
import TimeDuration from '../TimeDuration.vue'; | ||
|
||
const renderComponent = (props = {}) => { | ||
return render(TimeDuration, { | ||
props, | ||
}); | ||
}; | ||
|
||
const MINUTE = 60; | ||
const HOUR = 60 * MINUTE; | ||
const DAY = 24 * HOUR; | ||
|
||
const testCases = [ | ||
// Under 2 minutes, should show seconds | ||
{ seconds: 0, expected: '0 seconds' }, | ||
EshaanAgg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ seconds: 1, expected: '1 second' }, | ||
{ seconds: 59, expected: '59 seconds' }, | ||
{ seconds: MINUTE, expected: '60 seconds' }, | ||
{ seconds: 2 * MINUTE - 1, expected: '119 seconds' }, | ||
|
||
// Under 1 hour, should show minutes (rounded down) | ||
{ seconds: 2 * MINUTE, expected: '2 minutes' }, | ||
{ seconds: 30 * MINUTE, expected: '30 minutes' }, | ||
{ seconds: 30 * MINUTE + 1, expected: '30 minutes' }, | ||
{ seconds: 30 * MINUTE + 59, expected: '30 minutes' }, | ||
{ seconds: 59 * MINUTE, expected: '59 minutes' }, | ||
|
||
// Under 1 day, should show hours (rounded down) | ||
{ seconds: HOUR, expected: '1 hour' }, | ||
{ seconds: 2 * HOUR, expected: '2 hours' }, | ||
{ seconds: 23 * HOUR, expected: '23 hours' }, | ||
{ seconds: 23 * HOUR + 59 * MINUTE, expected: '23 hours' }, | ||
{ seconds: 23 * HOUR + 59 * MINUTE + 59, expected: '23 hours' }, | ||
|
||
// Over 1 day, should show days (rounded down) | ||
{ seconds: DAY, expected: '1 day' }, | ||
{ seconds: 2 * DAY, expected: '2 days' }, | ||
{ seconds: 6 * DAY, expected: '6 days' }, | ||
{ seconds: 6 * DAY + 23 * HOUR + 59 * MINUTE + 59, expected: '6 days' }, | ||
]; | ||
|
||
describe('TimeDuration', () => { | ||
it.each(testCases)('should render $seconds seconds as $expected', ({ seconds, expected }) => { | ||
renderComponent({ seconds }); | ||
expect(screen.getByText(expected)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render empty string if seconds are not provided as props', () => { | ||
renderComponent(); | ||
expect(screen.getByText('—')).toBeInTheDocument(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { render, screen } from '@testing-library/vue'; | ||
import UserTypeDisplay from '../UserTypeDisplay.vue'; | ||
|
||
const sampleUserType = 'testing-user-type'; | ||
const expectedSampleUserType = 'Testing User Type'; | ||
|
||
// Helper function to render the component with the provided props | ||
const renderComponent = props => { | ||
const translatedUserKinds = { | ||
computed: { | ||
typeDisplayMap() { | ||
return { | ||
[sampleUserType]: expectedSampleUserType, | ||
}; | ||
}, | ||
}, | ||
}; | ||
|
||
return render(UserTypeDisplay, { | ||
props: { | ||
userType: sampleUserType, | ||
...props, | ||
}, | ||
mixins: [translatedUserKinds], | ||
}); | ||
}; | ||
|
||
describe('UserTypeDisplay', () => { | ||
test('smoke test (renders the translated user type correctly)', () => { | ||
renderComponent({ userType: sampleUserType }); | ||
expect(screen.getByText(expectedSampleUserType)).toBeInTheDocument(); | ||
}); | ||
|
||
test('does not render the untranslated user type', () => { | ||
renderComponent({ userType: sampleUserType }); | ||
expect(screen.queryByText(sampleUserType)).not.toBeInTheDocument(); | ||
}); | ||
|
||
test('does not render anything if the userType prop is not provided', () => { | ||
const { container } = renderComponent({ userType: undefined }); | ||
expect(container).toBeEmptyDOMElement(); | ||
}); | ||
}); |
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.
Made this change as earlier passing
0
asseconds
led to this expression being evaluated asfalse
, and thus an empty string being displayed instead of0 seconds
. Also added a test case related to the same.