-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(react-datepicker-compat): Add cypress and unit tests for DatePi…
…cker (#27209) * adding some of the tests and setting up cypress * sync * finish implementing tests * restoring text input story * adding requested changes * adding requested changeS
- Loading branch information
1 parent
4292289
commit a97f513
Showing
21 changed files
with
472 additions
and
44 deletions.
There are no files selected for viewing
3 changes: 3 additions & 0 deletions
3
packages/react-components/react-datepicker-compat/cypress.config.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { baseConfig } from '@fluentui/scripts-cypress'; | ||
|
||
export default baseConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
...ages/react-components/react-datepicker-compat/src/components/DatePicker/DatePicker.cy.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import * as React from 'react'; | ||
import { mount as mountBase } from '@cypress/react'; | ||
|
||
import { FluentProvider } from '@fluentui/react-provider'; | ||
import { teamsLightTheme } from '@fluentui/react-theme'; | ||
|
||
import { DatePicker } from './DatePicker'; | ||
|
||
const mount = (element: JSX.Element) => { | ||
mountBase(<FluentProvider theme={teamsLightTheme}>{element}</FluentProvider>); | ||
}; | ||
|
||
const inputSelector = '[role="combobox"]'; | ||
const popoverSelector = '[role="dialog"]'; | ||
const fieldErrorMessageSelector = '[role=alert]'; | ||
|
||
describe('DatePicker', () => { | ||
it('opens a default datepicker', () => { | ||
mount(<DatePicker />); | ||
cy.get(inputSelector).click().get(popoverSelector).should('be.visible'); | ||
}); | ||
|
||
it('should not open a datepicker when disabled', () => { | ||
mount(<DatePicker disabled />); | ||
// Force is needed because otherwise Cypress throws an error | ||
cy.get(inputSelector).click({ force: true }).get(popoverSelector).should('not.exist'); | ||
}); | ||
|
||
it('should render DatePicker and calloutId must exist in the DOM when isDatePickerShown is set', () => { | ||
mount(<DatePicker />); | ||
cy.get(inputSelector).click(); | ||
|
||
cy.get('body').find('[aria-owns]').should('exist'); | ||
}); | ||
|
||
it('should clear error message when required input has date text and allowTextInput is true', () => { | ||
mount(<DatePicker isRequired allowTextInput />); | ||
|
||
// Open DatePicker and dismiss | ||
cy.get(inputSelector).click().get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('exist'); | ||
|
||
// Type a date and dismiss | ||
cy.get(inputSelector).click().click().type('Jan 1 2030').get('body').click('bottomRight'); | ||
|
||
cy.get(fieldErrorMessageSelector).should('not.exist'); | ||
}); | ||
|
||
it('clears error message when required input has date selected from calendar and allowTextInput is true', () => { | ||
mount(<DatePicker isRequired allowTextInput />); | ||
|
||
// Open picker and dismiss to show error message | ||
cy.get(inputSelector).click().get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('exist'); | ||
|
||
// Select a date from calendar, we choose 10 since the first 0-6 days in the grid are not really dates, and dismiss | ||
cy.get(inputSelector).click().get('[role="gridcell"]').its(10).click().get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('not.exist'); | ||
}); | ||
|
||
it('should not clear initial error when datepicker is opened', () => { | ||
mount(<DatePicker isRequired allowTextInput maxDate={new Date('2020-04-01')} value={new Date('2020-04-02')} />); | ||
|
||
cy.get(fieldErrorMessageSelector).should('exist'); | ||
|
||
// open and dismiss picker | ||
cy.get(inputSelector).click().get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('exist'); | ||
}); | ||
|
||
it('should reset status message after selecting a valid date', () => { | ||
mount(<DatePicker allowTextInput initialPickerDate={new Date('2021-04-15')} />); | ||
|
||
cy.get(fieldErrorMessageSelector).should('not.exist'); | ||
cy.get(inputSelector).click().click().type('test').get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('exist'); | ||
cy.get(inputSelector).click().get('[role="gridcell"]').its(10).click().get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('not.exist'); | ||
}); | ||
}); | ||
|
||
describe('When boundaries are specified', () => { | ||
const defaultDate = new Date('Dec 15 2017'); | ||
const minDate = new Date('Jan 1 2017'); | ||
const maxDate = new Date('Dec 31 2017'); | ||
const strings = { | ||
months: [ | ||
'January', | ||
'February', | ||
'March', | ||
'April', | ||
'May', | ||
'June', | ||
'July', | ||
'August', | ||
'September', | ||
'October', | ||
'November', | ||
'December', | ||
], | ||
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], | ||
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], | ||
shortDays: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], | ||
goToToday: 'Go to today', | ||
isOutOfBoundsErrorMessage: 'out of bounds', | ||
}; | ||
|
||
beforeEach(() => { | ||
mount(<DatePicker allowTextInput minDate={minDate} maxDate={maxDate} value={defaultDate} strings={strings} />); | ||
}); | ||
|
||
it('should throw validation error for date outside boundary', () => { | ||
// Before min date | ||
cy.get(inputSelector).click().click().clear().type('Jan 1 2010{enter}').get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('exist').should('have.text', 'out of bounds'); | ||
|
||
// After max date | ||
cy.get(inputSelector).click().click().clear().type('Jan 1 2020{enter}').get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('exist').should('have.text', 'out of bounds'); | ||
}); | ||
|
||
it('should not throw validation error for date inside boundary', () => { | ||
// In boundary | ||
cy.get(inputSelector).click().click().clear().type('Dec 16 2017{enter}').get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('not.exist'); | ||
|
||
// In boundary | ||
cy.get(inputSelector).click().click().clear().type('Jan 1 2017{enter}').get('body').click('bottomRight'); | ||
cy.get(fieldErrorMessageSelector).should('not.exist'); | ||
}); | ||
}); |
142 changes: 142 additions & 0 deletions
142
...es/react-components/react-datepicker-compat/src/components/DatePicker/DatePicker.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import * as React from 'react'; | ||
import { fireEvent, render, RenderResult } from '@testing-library/react'; | ||
import { DatePicker } from './DatePicker'; | ||
import { isConformant } from '../../testing/isConformant'; | ||
import { datePickerClassNames } from './useDatePickerStyles'; | ||
import { resetIdsForTests } from '@fluentui/react-utilities'; | ||
|
||
// testing-library's queryByRole function doesn't look inside portals | ||
function queryByRoleDialog(result: RenderResult) { | ||
const dialogs = result.baseElement.querySelectorAll('*[role="dialog"]'); | ||
if (!dialogs?.length) { | ||
return null; | ||
} else { | ||
expect(dialogs.length).toBe(1); | ||
return dialogs.item(0) as HTMLElement; | ||
} | ||
} | ||
|
||
const getDatepickerPopoverElement = (result: RenderResult) => { | ||
result.getByRole('combobox').click(); | ||
const dialog = queryByRoleDialog(result); | ||
expect(dialog).not.toBeNull(); | ||
return dialog!; | ||
}; | ||
|
||
describe('DatePicker', () => { | ||
beforeEach(() => { | ||
resetIdsForTests(); | ||
}); | ||
|
||
isConformant({ | ||
Component: DatePicker, | ||
displayName: 'DatePicker', | ||
disabledTests: ['consistent-callback-args'], | ||
testOptions: { | ||
'has-static-classnames': [ | ||
{ | ||
props: {}, | ||
expectedClassNames: { | ||
root: datePickerClassNames.root, | ||
inputField: datePickerClassNames.inputField, | ||
wrapper: datePickerClassNames.wrapper, | ||
popoverSurface: datePickerClassNames.popoverSurface, | ||
input: datePickerClassNames.input, | ||
calendar: datePickerClassNames.calendar, | ||
}, | ||
getPortalElement: getDatepickerPopoverElement, | ||
}, | ||
], | ||
}, | ||
}); | ||
|
||
it('can add an id to the container', () => { | ||
const result = render(<DatePicker id="test-id" />); | ||
expect(result.findByTestId('test-id')).toBeTruthy(); | ||
}); | ||
|
||
it('should not render DatePicker when isDatePickerShown is not set', () => { | ||
const result = render(<DatePicker />); | ||
expect(result).toMatchSnapshot(); | ||
}); | ||
|
||
it('renders a normal input when allowTextInput is true', () => { | ||
const result = render(<DatePicker allowTextInput />); | ||
expect(result.getByRole('combobox').getAttribute('readonly')).toBeNull(); | ||
}); | ||
|
||
it('renders a readonly input when allowTextInput is false', () => { | ||
const result = render(<DatePicker />); | ||
expect(result.getByRole('combobox').getAttribute('readonly')).not.toBeNull(); | ||
}); | ||
|
||
it('should call onSelectDate even when required input is empty when allowTextInput is true', () => { | ||
const onSelectDate = jest.fn(); | ||
const result = render(<DatePicker isRequired allowTextInput onSelectDate={onSelectDate} />); | ||
const input = result.getByRole('combobox'); | ||
|
||
fireEvent.change(input, { target: { value: 'Jan 1 2030' } }); | ||
fireEvent.blur(input); | ||
|
||
fireEvent.change(input, { target: { value: '' } }); | ||
fireEvent.blur(input); | ||
|
||
expect(onSelectDate).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
it('should call onSelectDate only once when allowTextInput is true and popup is used to select the value', () => { | ||
const onSelectDate = jest.fn(); | ||
const result = render(<DatePicker allowTextInput onSelectDate={onSelectDate} />); | ||
|
||
fireEvent.click(result.getByRole('combobox')); | ||
result.getAllByRole('gridcell')[10].click(); | ||
|
||
expect(onSelectDate).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should set "Calendar" as the Callout\'s aria-label', () => { | ||
const result = render(<DatePicker />); | ||
const input = result.getByRole('combobox'); | ||
|
||
fireEvent.click(input); | ||
fireEvent.blur(input); | ||
|
||
expect(result.getByRole('dialog').getAttribute('aria-label')).toBe('Calendar'); | ||
}); | ||
|
||
it('should reflect the correct date in the input field when selecting a value', () => { | ||
const today = new Date('January 15, 2020'); | ||
const initiallySelectedDate = new Date('January 10, 2020'); | ||
const result = render(<DatePicker allowTextInput today={today} initialPickerDate={initiallySelectedDate} />); | ||
|
||
const input = result.getByRole('combobox'); | ||
|
||
fireEvent.click(input); | ||
result.getByText('15').click(); | ||
|
||
expect(input.getAttribute('value')).toBe('Wed Jan 15 2020'); | ||
}); | ||
|
||
it('reflects the correct date in the input field when selecting a value and a different format is given', () => { | ||
const today = new Date('January 15, 2020'); | ||
const initiallySelectedDate = new Date('January 10, 2020'); | ||
const onFormatDate = (date?: Date): string => { | ||
return date ? date.getDate() + '/' + (date.getMonth() + 1) + '/' + (date.getFullYear() % 100) : ''; | ||
}; | ||
|
||
const result = render( | ||
<DatePicker | ||
allowTextInput={true} | ||
today={today} | ||
formatDate={onFormatDate} | ||
initialPickerDate={initiallySelectedDate} | ||
/>, | ||
); | ||
const input = result.getByRole('combobox'); | ||
|
||
fireEvent.click(input); | ||
result.getByText('15').click(); | ||
|
||
expect(input.getAttribute('value')).toBe('15/1/20'); | ||
}); | ||
}); |
16 changes: 0 additions & 16 deletions
16
...t-components/react-datepicker-compat/src/components/DatePicker/DatePicker.test.tsx.ignore
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.