Skip to content
Snippets Groups Projects
Unverified Commit c97b46e8 authored by Douglas Fabris's avatar Douglas Fabris Committed by GitHub
Browse files

chore: Adds snapshot tests in `ui-video-conf` package (#32780)


* chore: add snapshot tests

* fix: review

* Use `@rocket.chat/jest-presets`

---------

Co-authored-by: default avatarTasso <tasso.evangelista@rocket.chat>
parent 7515c171
No related branches found
No related tags found
No related merge requests found
Showing
with 166 additions and 31 deletions
...@@ -16,7 +16,9 @@ ...@@ -16,7 +16,9 @@
"@swc/core": "~1.5.24", "@swc/core": "~1.5.24",
"@swc/jest": "~0.2.36", "@swc/jest": "~0.2.36",
"@testing-library/jest-dom": "~6.4.5", "@testing-library/jest-dom": "~6.4.5",
"@types/jest-axe": "~3.5.9",
"identity-obj-proxy": "~3.0.0", "identity-obj-proxy": "~3.0.0",
"jest-axe": "~9.0.0",
"jest-environment-jsdom": "~29.7.0", "jest-environment-jsdom": "~29.7.0",
"uuid": "~9.0.1" "uuid": "~9.0.1"
}, },
......
import { TextEncoder, TextDecoder } from 'node:util'; import { TextEncoder, TextDecoder } from 'node:util';
import { toHaveNoViolations } from 'jest-axe';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
expect.extend(toHaveNoViolations);
const urlByBlob = new WeakMap<Blob, string>(); const urlByBlob = new WeakMap<Blob, string>();
const blobByUrl = new Map<string, Blob>(); const blobByUrl = new Map<string, Blob>();
......
/** @type {import('@storybook/react/types').StorybookConfig} */ /** @type {import('@storybook/react/types').StorybookConfig} */
module.exports = { module.exports = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-essentials'], addons: ['@storybook/addon-essentials', '@storybook/addon-a11y'],
framework: '@storybook/react', framework: '@storybook/react',
features: { features: {
postcss: false, postcss: false,
......
import '../../../apps/meteor/app/theme/client/main.css'; import '../../../apps/meteor/app/theme/client/main.css';
import 'highlight.js/styles/github.css'; import 'highlight.js/styles/github.css';
import '@rocket.chat/icons/dist/rocketchat.css';
export const parameters = { export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' }, actions: { argTypesRegex: '^on[A-Z].*' },
......
import client from '@rocket.chat/jest-presets/client';
import type { Config } from 'jest';
export default {
preset: client.preset,
setupFilesAfterEnv: [...client.setupFilesAfterEnv],
testMatch: ['<rootDir>/src/**/*.spec.{ts,tsx}'],
} satisfies Config;
...@@ -9,9 +9,11 @@ ...@@ -9,9 +9,11 @@
"@rocket.chat/fuselage": "^0.57.0", "@rocket.chat/fuselage": "^0.57.0",
"@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "^0.36.0", "@rocket.chat/icons": "^0.36.0",
"@rocket.chat/jest-presets": "workspace:~",
"@rocket.chat/styled": "~0.31.25", "@rocket.chat/styled": "~0.31.25",
"@rocket.chat/ui-avatar": "workspace:^", "@rocket.chat/ui-avatar": "workspace:^",
"@rocket.chat/ui-contexts": "workspace:^", "@rocket.chat/ui-contexts": "workspace:^",
"@storybook/addon-a11y": "^6.5.16",
"@storybook/addon-actions": "~6.5.16", "@storybook/addon-actions": "~6.5.16",
"@storybook/addon-docs": "~6.5.16", "@storybook/addon-docs": "~6.5.16",
"@storybook/addon-essentials": "~6.5.16", "@storybook/addon-essentials": "~6.5.16",
...@@ -19,13 +21,16 @@ ...@@ -19,13 +21,16 @@
"@storybook/manager-webpack4": "~6.5.16", "@storybook/manager-webpack4": "~6.5.16",
"@storybook/react": "~6.5.16", "@storybook/react": "~6.5.16",
"@storybook/testing-library": "~0.0.13", "@storybook/testing-library": "~0.0.13",
"@storybook/testing-react": "~1.3.0",
"@types/babel__core": "~7.20.3", "@types/babel__core": "~7.20.3",
"@types/jest": "~29.5.12", "@types/jest": "~29.5.12",
"@types/jest-axe": "~3.5.9",
"eslint": "~8.45.0", "eslint": "~8.45.0",
"eslint-plugin-react": "~7.32.2", "eslint-plugin-react": "~7.32.2",
"eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-react-hooks": "~4.6.0",
"eslint-plugin-storybook": "~0.6.15", "eslint-plugin-storybook": "~0.6.15",
"jest": "~29.7.0", "jest": "~29.7.0",
"jest-axe": "~9.0.0",
"react-docgen-typescript-plugin": "~1.0.5", "react-docgen-typescript-plugin": "~1.0.5",
"ts-jest": "~29.1.1", "ts-jest": "~29.1.1",
"typescript": "~5.3.3" "typescript": "~5.3.3"
...@@ -45,9 +50,9 @@ ...@@ -45,9 +50,9 @@
"eslint": "eslint --ext .js,.jsx,.ts,.tsx .", "eslint": "eslint --ext .js,.jsx,.ts,.tsx .",
"eslint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", "eslint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix",
"test": "jest", "test": "jest",
"build": "tsc -p tsconfig.json", "build": "tsc -p tsconfig.build.json",
"storybook": "start-storybook -p 6006", "storybook": "start-storybook -p 6006",
"dev": "tsc -p tsconfig.json --watch --preserveWatchOutput" "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput"
}, },
"main": "./dist/index.js", "main": "./dist/index.js",
"typings": "./dist/index.d.ts", "typings": "./dist/index.d.ts",
......
import { composeStories } from '@storybook/testing-react';
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import * as stories from './VideoConfButton.stories';
const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
const tree = render(<Story />);
expect(tree.baseElement).toMatchSnapshot();
});
test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
const { container } = render(<Story />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import VideoConfButton from './VideoConfButton';
export default {
title: 'Components/VideoConfButton',
component: VideoConfButton,
} satisfies ComponentMeta<typeof VideoConfButton>;
export const Default: ComponentStory<typeof VideoConfButton> = () => <VideoConfButton>Button</VideoConfButton>;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders Default without crashing 1`] = `
<body>
<div>
<button
class="rcx-box rcx-box--full rcx-button rcx-css-1qcz93u"
type="button"
>
<span
class="rcx-button--content"
>
Button
</span>
</button>
</div>
</body>
`;
export { default } from './VideoConfButton';
import { composeStories } from '@storybook/testing-react';
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import * as stories from './VideoConfController.stories';
const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
const tree = render(<Story />);
expect(tree.baseElement).toMatchSnapshot();
});
test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
const { container } = render(<Story />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import VideoConfController from './VideoConfController';
export default {
title: 'Components/VideoConfController',
component: VideoConfController,
} satisfies ComponentMeta<typeof VideoConfController>;
export const Default: ComponentStory<typeof VideoConfController> = (args) => <VideoConfController {...args} />;
Default.args = {
'icon': 'info',
'aria-label': 'info',
};
import { IconButton } from '@rocket.chat/fuselage'; import { IconButton } from '@rocket.chat/fuselage';
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import type { Keys as IconName } from '@rocket.chat/icons'; import type { Keys as IconName } from '@rocket.chat/icons';
import type { ReactElement, ButtonHTMLAttributes } from 'react'; import type { ReactElement, ButtonHTMLAttributes } from 'react';
...@@ -11,21 +10,16 @@ type VideoConfControllerProps = { ...@@ -11,21 +10,16 @@ type VideoConfControllerProps = {
small?: boolean; small?: boolean;
} & Omit<ButtonHTMLAttributes<HTMLElement>, 'ref' | 'is' | 'className' | 'size' | 'elevation'>; } & Omit<ButtonHTMLAttributes<HTMLElement>, 'ref' | 'is' | 'className' | 'size' | 'elevation'>;
const VideoConfController = ({ icon, active, secondary, disabled, small = true, ...props }: VideoConfControllerProps): ReactElement => { const VideoConfController = ({ icon, active, secondary, disabled, small = true, ...props }: VideoConfControllerProps): ReactElement => (
const id = useUniqueId(); <IconButton
aria-live='assertive'
return ( small={small}
<IconButton icon={icon}
aria-live='assertive' info={active}
small={small} disabled={disabled}
icon={icon} secondary={secondary || active || disabled}
id={id} {...props}
info={active} />
disabled={disabled} );
secondary={secondary || active || disabled}
{...props}
/>
);
};
export default VideoConfController; export default VideoConfController;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders Default without crashing 1`] = `
<body>
<div>
<button
aria-label="info"
aria-live="assertive"
class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button"
type="button"
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-info rcx-icon rcx-css-4pvxx3"
>
</i>
</button>
</div>
</body>
`;
export { default } from './VideoConfController';
import { composeStories } from '@storybook/testing-react';
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import * as stories from './VideoConfMessage.stories';
const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
const tree = render(<Story />);
expect(tree.baseElement).toMatchSnapshot();
});
test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
const { container } = render(<Story />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
...@@ -2,7 +2,6 @@ import { MessageDivider, Message, Avatar, Box } from '@rocket.chat/fuselage'; ...@@ -2,7 +2,6 @@ import { MessageDivider, Message, Avatar, Box } from '@rocket.chat/fuselage';
import type { ComponentMeta, ComponentStory } from '@storybook/react'; import type { ComponentMeta, ComponentStory } from '@storybook/react';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import '@rocket.chat/icons/dist/rocketchat.css';
import { VideoConfMessage, VideoConfMessageIcon, VideoConfMessageRow, VideoConfMessageText } from '.'; import { VideoConfMessage, VideoConfMessageIcon, VideoConfMessageRow, VideoConfMessageText } from '.';
import VideoConfMessageAction from './VideoConfMessageAction'; import VideoConfMessageAction from './VideoConfMessageAction';
import VideoConfMessageActions from './VideoConfMessageActions'; import VideoConfMessageActions from './VideoConfMessageActions';
...@@ -22,7 +21,7 @@ export default { ...@@ -22,7 +21,7 @@ export default {
<MessageDivider>May, 24, 2020</MessageDivider> <MessageDivider>May, 24, 2020</MessageDivider>
<Message className='customclass'> <Message className='customclass'>
<Message.LeftContainer> <Message.LeftContainer>
<Avatar url={avatarUrl} size='x36' /> <Avatar alt='' url={avatarUrl} size='x36' />
</Message.LeftContainer> </Message.LeftContainer>
<Message.Container> <Message.Container>
<Message.Header> <Message.Header>
...@@ -41,7 +40,7 @@ export default { ...@@ -41,7 +40,7 @@ export default {
</Box> </Box>
), ),
], ],
} as ComponentMeta<typeof VideoConfMessage>; } satisfies ComponentMeta<typeof VideoConfMessage>;
const avatarUrl = const avatarUrl =
'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAoACgDASIAAhEBAxEB/8QAGwAAAgIDAQAAAAAAAAAAAAAAAAcEBgIDBQj/xAAuEAACAQQAAwcEAQUAAAAAAAABAgMABAUREiExBhMUIkFRYQcWcYGhFTJSgpH/xAAYAQADAQEAAAAAAAAAAAAAAAACAwQBAP/EAB4RAAIBBQEBAQAAAAAAAAAAAAABAgMREiExE0HR/9oADAMBAAIRAxEAPwBuXuIkhBuMe5ib/AHQP49q4L3mLitryTLTSpOiHQI5k/HzXa/qbFOEudVTu1dumWvcTaNCZYZ7vU6g6LxqjOU/24dfs1Ouh9FnkMpd3Reeyx83hAxZZEhkdV9/MBrX71WGPvJcqrJBGveKATtuXXqNU0pu02bTHXD/AGvJAluyxxRd6F4x00o+NdKoVrjbzJdvVe1t5cVLc2ck8qjnohgpPtz2v7G6JtPQ2VJwjlcw+37mchpnK6GtIuv5NFWeTsLNPvxWTvpfjvOEfwKKzEVkSct2vscS/BIzSN0YRkeX81UpPqO8masJETu7OOccY4dswYFQeftv096XV5knuJGdm2T1+agvMXj8jEaHX905QihabvcbuS7X566mLWLwSY8PuRnk/u4eZ0deTl71Ef6hY+0yM88TzeNZY4luYwpVYyduOfrvhPTnr0pXSX9y5mCsyJMdyxxvwq599em+taItqCSNc90ChvZRUruUcT0JiO18Elpk7t8v41LWzacxkBSuvjQ/FFJayjDWrCTepAQ2vUH0oo/Jk3ovpwJJeVCP5CN+lFFaaMqy+nAyuChvrTI2kN9JAsi2ZOy4IBHMnkSCP+iqBexSWdxLazoUljJVlPUH2oorkV10pRc7b1zXb/hZOzuJvM86QWEXeELxOzHSIPcmiiiunVlF2RNTpRkrs//Z'; 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAoACgDASIAAhEBAxEB/8QAGwAAAgIDAQAAAAAAAAAAAAAAAAcEBgIDBQj/xAAuEAACAQQAAwcEAQUAAAAAAAABAgMABAUREiExBhMUIkFRYQcWcYGhFTJSgpH/xAAYAQADAQEAAAAAAAAAAAAAAAACAwQBAP/EAB4RAAIBBQEBAQAAAAAAAAAAAAABAgMREiExE0HR/9oADAMBAAIRAxEAPwBuXuIkhBuMe5ib/AHQP49q4L3mLitryTLTSpOiHQI5k/HzXa/qbFOEudVTu1dumWvcTaNCZYZ7vU6g6LxqjOU/24dfs1Ouh9FnkMpd3Reeyx83hAxZZEhkdV9/MBrX71WGPvJcqrJBGveKATtuXXqNU0pu02bTHXD/AGvJAluyxxRd6F4x00o+NdKoVrjbzJdvVe1t5cVLc2ck8qjnohgpPtz2v7G6JtPQ2VJwjlcw+37mchpnK6GtIuv5NFWeTsLNPvxWTvpfjvOEfwKKzEVkSct2vscS/BIzSN0YRkeX81UpPqO8masJETu7OOccY4dswYFQeftv096XV5knuJGdm2T1+agvMXj8jEaHX905QihabvcbuS7X566mLWLwSY8PuRnk/u4eZ0deTl71Ef6hY+0yM88TzeNZY4luYwpVYyduOfrvhPTnr0pXSX9y5mCsyJMdyxxvwq599em+taItqCSNc90ChvZRUruUcT0JiO18Elpk7t8v41LWzacxkBSuvjQ/FFJayjDWrCTepAQ2vUH0oo/Jk3ovpwJJeVCP5CN+lFFaaMqy+nAyuChvrTI2kN9JAsi2ZOy4IBHMnkSCP+iqBexSWdxLazoUljJVlPUH2oorkV10pRc7b1zXb/hZOzuJvM86QWEXeELxOzHSIPcmiiiunVlF2RNTpRkrs//Z';
...@@ -54,7 +53,7 @@ export const CallingDM: ComponentStory<typeof VideoConfMessage> = () => ( ...@@ -54,7 +53,7 @@ export const CallingDM: ComponentStory<typeof VideoConfMessage> = () => (
<VideoConfMessageText>Calling...</VideoConfMessageText> <VideoConfMessageText>Calling...</VideoConfMessageText>
</VideoConfMessageContent> </VideoConfMessageContent>
<VideoConfMessageActions> <VideoConfMessageActions>
<VideoConfMessageAction icon='info' /> <VideoConfMessageAction aria-label='info' icon='info' />
</VideoConfMessageActions> </VideoConfMessageActions>
</VideoConfMessageRow> </VideoConfMessageRow>
<VideoConfMessageFooter> <VideoConfMessageFooter>
...@@ -72,7 +71,7 @@ export const CallEndedDM: ComponentStory<typeof VideoConfMessage> = () => ( ...@@ -72,7 +71,7 @@ export const CallEndedDM: ComponentStory<typeof VideoConfMessage> = () => (
<VideoConfMessageText>Call ended</VideoConfMessageText> <VideoConfMessageText>Call ended</VideoConfMessageText>
</VideoConfMessageContent> </VideoConfMessageContent>
<VideoConfMessageActions> <VideoConfMessageActions>
<VideoConfMessageAction icon='info' /> <VideoConfMessageAction aria-label='info' icon='info' />
</VideoConfMessageActions> </VideoConfMessageActions>
</VideoConfMessageRow> </VideoConfMessageRow>
<VideoConfMessageFooter> <VideoConfMessageFooter>
...@@ -90,7 +89,7 @@ export const CallOngoing: ComponentStory<typeof VideoConfMessage> = () => ( ...@@ -90,7 +89,7 @@ export const CallOngoing: ComponentStory<typeof VideoConfMessage> = () => (
<VideoConfMessageText>Call ongoing</VideoConfMessageText> <VideoConfMessageText>Call ongoing</VideoConfMessageText>
</VideoConfMessageContent> </VideoConfMessageContent>
<VideoConfMessageActions> <VideoConfMessageActions>
<VideoConfMessageAction icon='info' /> <VideoConfMessageAction aria-label='info' icon='info' />
</VideoConfMessageActions> </VideoConfMessageActions>
</VideoConfMessageRow> </VideoConfMessageRow>
<VideoConfMessageFooter> <VideoConfMessageFooter>
...@@ -109,7 +108,7 @@ export const CallEnded: ComponentStory<typeof VideoConfMessage> = () => ( ...@@ -109,7 +108,7 @@ export const CallEnded: ComponentStory<typeof VideoConfMessage> = () => (
<VideoConfMessageText>Call ended</VideoConfMessageText> <VideoConfMessageText>Call ended</VideoConfMessageText>
</VideoConfMessageContent> </VideoConfMessageContent>
<VideoConfMessageActions> <VideoConfMessageActions>
<VideoConfMessageAction icon='info' /> <VideoConfMessageAction aria-label='info' icon='info' />
</VideoConfMessageActions> </VideoConfMessageActions>
</VideoConfMessageRow> </VideoConfMessageRow>
<VideoConfMessageFooter> <VideoConfMessageFooter>
......
import { Box } from '@rocket.chat/fuselage'; import { Box } from '@rocket.chat/fuselage';
import type { ReactElement } from 'react'; import type { AllHTMLAttributes, ReactElement } from 'react';
const VideoConfMessage = ({ ...props }): ReactElement => ( type VideoConfMessageProps = Omit<AllHTMLAttributes<HTMLDivElement>, 'is'>;
const VideoConfMessage = (props: VideoConfMessageProps): ReactElement => (
<Box <Box
mbs={4} mbs={4}
color='default' color='default'
...@@ -10,7 +11,7 @@ const VideoConfMessage = ({ ...props }): ReactElement => ( ...@@ -10,7 +11,7 @@ const VideoConfMessage = ({ ...props }): ReactElement => (
borderWidth={1} borderWidth={1}
borderColor='extra-light' borderColor='extra-light'
borderRadius='x4' borderRadius='x4'
className='rcx-videoconf-message-block' rcx-videoconf-message-block
{...props} {...props}
/> />
); );
......
...@@ -2,6 +2,6 @@ import { IconButton } from '@rocket.chat/fuselage'; ...@@ -2,6 +2,6 @@ import { IconButton } from '@rocket.chat/fuselage';
import type { ComponentProps, ReactElement } from 'react'; import type { ComponentProps, ReactElement } from 'react';
const VideoConfMessageAction = ({ icon = 'info', ...props }: ComponentProps<typeof IconButton>): ReactElement => ( const VideoConfMessageAction = ({ icon = 'info', ...props }: ComponentProps<typeof IconButton>): ReactElement => (
<IconButton icon={icon} small {...props} /> <IconButton {...props} icon={icon} small />
); );
export default VideoConfMessageAction; export default VideoConfMessageAction;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment