Skip to content

Commit

Permalink
Add unit and vr tests to Rating (#30414)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomi-msft authored Feb 2, 2024
1 parent ad5da7b commit e45b040
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 76 deletions.
1 change: 1 addition & 0 deletions apps/vr-tests-react-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@fluentui/react-progress": "*",
"@fluentui/react-provider": "*",
"@fluentui/react-radio": "*",
"@fluentui/react-rating-preview": "*",
"@fluentui/react-search-preview": "*",
"@fluentui/react-select": "*",
"@fluentui/react-shared-contexts": "*",
Expand Down
58 changes: 58 additions & 0 deletions apps/vr-tests-react-components/src/stories/Rating.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as React from 'react';
import { Steps, StoryWright } from 'storywright';
import { storiesOf } from '@storybook/react';
import { CircleRegular, CircleFilled, SquareRegular, SquareFilled } from '@fluentui/react-icons';
import { Rating } from '@fluentui/react-rating-preview';
import { TestWrapperDecoratorFixedWidth } from '../utilities/TestWrapperDecorator';

storiesOf('Rating Converged', module)
.addDecorator(TestWrapperDecoratorFixedWidth)
.addDecorator(story => (
<StoryWright
steps={new Steps()
.snapshot('rest', { cropTo: '.testWrapper' })
.hover('input')
.snapshot('hover', { cropTo: '.testWrapper' })
.end()}
>
{story()}
</StoryWright>
))
.addStory('Neutral Rating with half star', () => <Rating defaultValue={3.5} />, {
includeHighContrast: true,
includeDarkMode: true,
})
.addStory('Brand Rating with half star', () => <Rating color="brand" defaultValue={3.5} />, {
includeHighContrast: true,
includeDarkMode: true,
})
.addStory('Marigold Rating with half star', () => <Rating color="marigold" defaultValue={3.5} />, {
includeHighContrast: true,
includeDarkMode: true,
});

storiesOf('Rating Converged', module)
.addDecorator(TestWrapperDecoratorFixedWidth)
.addDecorator(story => (
<StoryWright steps={new Steps().snapshot('rest', { cropTo: '.testWrapper' }).end()}>{story()}</StoryWright>
))
.addStory('Rating size small', () => <Rating size="small" />, {})
.addStory('Rating size medium', () => <Rating size="medium" />, {})
.addStory('Rating size large', () => <Rating size="large" />, {})
.addStory('Rating size extra-large', () => <Rating size="extra-large" />, {});

storiesOf('Rating Converged', module)
.addDecorator(TestWrapperDecoratorFixedWidth)
.addDecorator(story => (
<StoryWright steps={new Steps().snapshot('rest', { cropTo: '.testWrapper' }).end()}>{story()}</StoryWright>
))
.addStory(
'Rating with custom circle icons',
() => <Rating iconFilled={<CircleFilled />} iconOutline={<CircleRegular />} defaultValue={3.5} />,
{},
)
.addStory(
'Rating with custom square icons',
() => <Rating iconFilled={<SquareFilled />} iconOutline={<SquareRegular />} defaultValue={3.5} />,
{},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { CircleFilled, SquareFilled } from '@fluentui/react-icons';
import { RatingDisplay } from '@fluentui/react-rating-preview';
import { TestWrapperDecoratorFixedWidth } from '../utilities/TestWrapperDecorator';
import { Steps, StoryWright } from 'storywright';

storiesOf('RatingDisplay Converged', module)
.addDecorator(TestWrapperDecoratorFixedWidth)
.addDecorator(story => (
<StoryWright steps={new Steps().snapshot('rest', { cropTo: '.testWrapper' }).end()}>{story()}</StoryWright>
))
.addStory('no value', () => <RatingDisplay />)
.addStory('size small with value and count', () => <RatingDisplay size="small" value={5} count={1160} />)
.addStory('size medium with value and count', () => <RatingDisplay size="medium" value={5} count={1160} />)
.addStory('size large with value and count', () => <RatingDisplay size="large" value={5} count={1160} />)
.addStory('size extra-large with value and count', () => <RatingDisplay size="extra-large" value={5} count={1160} />)
.addStory('custom circle icons', () => <RatingDisplay icon={<CircleFilled />} value={3} />)
.addStory('custom square icons', () => <RatingDisplay icon={<SquareFilled />} value={3} />)
.addStory('rounded up', () => <RatingDisplay value={3.8} />)
.addStory('rounded down', () => <RatingDisplay value={3.7} />)
.addStory('Neutral with half value', () => <RatingDisplay value={2.5} />, {
includeHighContrast: true,
includeDarkMode: true,
})
.addStory('Brand with half value', () => <RatingDisplay value={2.5} color="brand" />, {
includeHighContrast: true,
includeDarkMode: true,
})
.addStory('Marigold with half value', () => <RatingDisplay value={2.5} color="marigold" />, {
includeHighContrast: true,
includeDarkMode: true,
})
.addStory('Compact', () => <RatingDisplay compact value={3} />, {});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "chore: add accesibility props to Rating and RatingDisplay",
"packageName": "@fluentui/react-rating-preview",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,56 @@
import * as React from 'react';
import { render } from '@testing-library/react';
// import { isConformant } from '../../testing/isConformant';
import { isConformant } from '../../testing/isConformant';
import { Rating } from './Rating';

describe('Rating', () => {
// isConformant({
// Component: Rating,
// displayName: 'Rating',
// });
it('renders a default state', () => {
const result = render(<Rating>Default RatingItem</Rating>);
expect(result.container).toMatchSnapshot();
isConformant({
Component: Rating,
displayName: 'Rating',
// TODO re-enable consistent-callback-args once https://github.com/microsoft/fluentui/pull/30376 is merged
disabledTests: ['consistent-callback-args'],
});
it('respects a default value', () => {
const { getAllByRole } = render(<Rating defaultValue={3} />);
const checkedItems = getAllByRole('radio').filter(item => (item as HTMLInputElement).checked);
expect(checkedItems[0].getAttribute('value')).toBe('3');
expect(checkedItems.length).toEqual(1);
});
it('only sets the selected rating item to checked', () => {
const { getAllByRole } = render(<Rating value={3} />);
const checkedItems = getAllByRole('radio').filter(item => (item as HTMLInputElement).checked);
expect(checkedItems[0].getAttribute('value')).toBe('3');
expect(checkedItems.length).toEqual(1);
});
it('renders the correct number of items', () => {
const { getAllByRole } = render(<Rating max={10} />);
const items = getAllByRole('radio');
expect(items.length).toEqual(10);
});
it('calle onChange when a rating is clicked', () => {
const onChange = jest.fn();
const { getAllByRole } = render(<Rating onChange={onChange} />);
const items = getAllByRole('radio');
items[0].click();
expect(onChange).toHaveBeenCalledTimes(1);
});
it('does not call onChange when a rating is clicked and the value is the same', () => {
const onChange = jest.fn();
const { getAllByRole } = render(<Rating value={3} onChange={onChange} />);
const items = getAllByRole('radio');
items[2].click();
expect(onChange).toHaveBeenCalledTimes(0);
});
it('calls onChange with the correct value when a rating is clicked', () => {
const onChange = jest.fn();
const { getAllByRole } = render(<Rating onChange={onChange} />);
const items = getAllByRole('radio');
items[3].click();
items[2].click();
items[1].click();
expect(onChange).toHaveBeenCalledTimes(3);
expect(onChange.mock.calls[0][1].value).toBe(4);
expect(onChange.mock.calls[1][1].value).toBe(3);
expect(onChange.mock.calls[2][1].value).toBe(2);
});
});

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
import * as React from 'react';
import { render } from '@testing-library/react';
//import { isConformant } from '../../testing/isConformant';
import { isConformant } from '../../testing/isConformant';
import { RatingDisplay } from './RatingDisplay';

describe('RatingDisplay', () => {
// isConformant({
// Component: RatingDisplay,
// displayName: 'RatingDisplay',
// });

// TODO add more tests here, and create visual regression tests in /apps/vr-tests

it('renders a default state', () => {
const result = render(<RatingDisplay>Default RatingDisplay</RatingDisplay>);
expect(result.container).toMatchSnapshot();
isConformant({
Component: RatingDisplay,
displayName: 'RatingDisplay',
requiredProps: { count: 1160, value: 4.5 },
});
it('renders the correct number of items', () => {
const { getByRole } = render(<RatingDisplay max={10} />);
expect(getByRole('img').children.length).toEqual(10);
});
it('renders the valueText slot when a value is provided', () => {
const { getByText } = render(<RatingDisplay value={3} />);
const valueText = getByText('3');
expect(valueText).toBeDefined();
expect(valueText.classList.contains('fui-RatingDisplay__valueText')).toBeTruthy();
});
it('does not render the valueText slot when a value is not provided', () => {
const { container } = render(<RatingDisplay />);
expect(container?.querySelector('.fui-RatingDisplay__valueText')).toBeNull();
});
it('renders the countText slot when a count is provided', () => {
const { getByText } = render(<RatingDisplay count={1160} />);
const countText = getByText('1,160');
expect(countText).toBeDefined();
expect(countText.classList.contains('fui-RatingDisplay__countText')).toBeTruthy();
});
it('does not render the countText slot when a count is not provided', () => {
const { container } = render(<RatingDisplay />);
expect(container?.querySelector('.fui-RatingDisplay__countText')).toBeNull();
});
it('renders only one item when compact is true', () => {
const { getByRole } = render(<RatingDisplay compact />);
const items = getByRole('img');
expect(items.getElementsByClassName('fui-RatingItem').length).toEqual(1);
});
});

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,112 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { isConformant } from '../../testing/isConformant';
import { RatingItem } from './RatingItem';
import { RatingItemProvider } from '../../contexts/RatingItemContext';
import { RatingItemContextValue } from './RatingItem.types';
import { StarFilled, StarRegular } from '@fluentui/react-icons';

describe('RatingItem', () => {
it('renders a default state', () => {
const result = render(<RatingItem>Default RatingItem</RatingItem>);
expect(result.container).toMatchSnapshot();
isConformant({
Component: RatingItem,
displayName: 'RatingItem',
// Need to disable this test because certain slots are not rendered without specific context values
disabledTests: ['component-has-static-classnames-object'],
});
it('does not render input elements when interactive is false', () => {
const contextValues: RatingItemContextValue = {
value: 3,
interactive: false,
iconFilled: <StarFilled />,
iconOutline: <StarRegular />,
color: 'neutral',
step: 1,
size: 'medium',
};
const { queryAllByRole } = render(
<RatingItemProvider value={contextValues}>
<RatingItem value={2} />
</RatingItemProvider>,
);
const inputs = queryAllByRole('radio');
expect(inputs.length).toEqual(0);
});
it('renders only the full value input when step is 1 and interactive is true', () => {
const contextValues: RatingItemContextValue = {
value: 3,
interactive: true,
iconFilled: <StarFilled />,
iconOutline: <StarRegular />,
color: 'neutral',
step: 1,
size: 'medium',
};
const { getByRole } = render(
<RatingItemProvider value={contextValues}>
<RatingItem value={2} />
</RatingItemProvider>,
);
const input = getByRole('radio') as HTMLInputElement;
expect(input.value).toEqual('2');
});
it('renders both the full value input and the half value input when step is 0.5 and interactive is true', () => {
const contextValues: RatingItemContextValue = {
value: 3,
interactive: true,
iconFilled: <StarFilled />,
iconOutline: <StarRegular />,
color: 'neutral',
step: 0.5,
size: 'medium',
};
const { getAllByRole } = render(
<RatingItemProvider value={contextValues}>
<RatingItem value={2} />
</RatingItemProvider>,
);
const inputs = getAllByRole('radio') as HTMLInputElement[];
expect(inputs.length).toEqual(2);
expect(inputs[0].value).toEqual('1.5');
expect(inputs[1].value).toEqual('2');
});
it('sets the half value input to checked when the value is 2.5', () => {
const contextValues: RatingItemContextValue = {
value: 2.5,
interactive: true,
iconFilled: <StarFilled />,
iconOutline: <StarRegular />,
color: 'neutral',
step: 0.5,
size: 'medium',
};
const { queryAllByRole } = render(
<RatingItemProvider value={contextValues}>
<RatingItem value={3} />
</RatingItemProvider>,
);
const inputs = queryAllByRole('radio') as HTMLInputElement[];
const checkedInputs = inputs.filter(input => input.checked);
expect(checkedInputs.length).toEqual(1);
expect(checkedInputs[0].value).toEqual(contextValues.value?.toString());
});
it('sets the full value input to checked when the value is 3', () => {
const contextValues: RatingItemContextValue = {
value: 3,
interactive: true,
iconFilled: <StarFilled />,
iconOutline: <StarRegular />,
color: 'neutral',
step: 1,
size: 'medium',
};
const { queryAllByRole } = render(
<RatingItemProvider value={contextValues}>
<RatingItem value={3} />
</RatingItemProvider>,
);
const inputs = queryAllByRole('radio') as HTMLInputElement[];
const checkedInputs = inputs.filter(input => input.checked);
expect(checkedInputs.length).toEqual(1);
expect(checkedInputs[0].value).toEqual(contextValues.value?.toString());
});
});
Loading

0 comments on commit e45b040

Please sign in to comment.