Skip to content
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

test(ui-voip): Normalize @rocket.chat/ui-voip unit tests #33739

Merged
merged 3 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/ui-voip/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"devDependencies": {
"@babel/core": "~7.25.8",
"@faker-js/faker": "~8.0.2",
"@react-spectrum/test-utils": "~1.0.0-alpha.2",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.59.3",
Expand All @@ -49,6 +50,7 @@
"@types/jest": "~29.5.12",
"@types/jest-axe": "~3.5.9",
"@types/react": "~17.0.80",
"@types/react-dom": "~17.0.25",
"eslint": "~8.45.0",
"eslint-plugin-react": "~7.32.2",
"eslint-plugin-react-hooks": "~4.6.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,63 @@ import { faker } from '@faker-js/faker';
import { mockAppRoot } from '@rocket.chat/mock-providers';
import { composeStories } from '@storybook/react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { axe } from 'jest-axe';

import VoipContactId from './VoipContactId';
import * as stories from './VoipContactId.stories';

const testCases = Object.values(composeStories(stories)).map((story) => [story.storyName || 'Story', story]);

describe('VoipContactId', () => {
beforeAll(() => {
Object.assign(navigator, {
clipboard: {
writeText: jest.fn(),
},
});
beforeAll(() => {
Object.assign(navigator, {
clipboard: {
writeText: jest.fn(),
},
});
});

test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
const tree = render(<Story />, { wrapper: mockAppRoot().build(), legacyRoot: true });
expect(tree.baseElement).toMatchSnapshot();
});
test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
const tree = render(<Story />, { wrapper: mockAppRoot().build(), legacyRoot: true });
expect(tree.baseElement).toMatchSnapshot();
});

test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
const { container } = render(<Story />, { wrapper: mockAppRoot().build(), legacyRoot: true });
test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
const { container } = render(<Story />, { wrapper: mockAppRoot().build(), legacyRoot: true });

const results = await axe(container);
expect(results).toHaveNoViolations();
});

const results = await axe(container);
expect(results).toHaveNoViolations();
it('should display avatar and name when username is available', () => {
render(<VoipContactId name='John Doe' username='john.doe' />, {
wrapper: mockAppRoot().build(),
legacyRoot: true,
});

it('should display avatar and name when username is available', () => {
render(<VoipContactId name='John Doe' username='john.doe' />, {
wrapper: mockAppRoot().build(),
legacyRoot: true,
});
expect(screen.getByRole('img', { hidden: true })).toHaveAttribute('title', 'john.doe');
expect(screen.getByText('John Doe')).toBeInTheDocument();
});

expect(screen.getByRole('img', { hidden: true })).toHaveAttribute('title', 'john.doe');
expect(screen.getByText('John Doe')).toBeInTheDocument();
it('should display transferedBy information when available', () => {
render(<VoipContactId name='John Doe' username='john.doe' transferedBy='Jane Doe' />, {
wrapper: mockAppRoot().build(),
legacyRoot: true,
});

it('should display transferedBy information when available', () => {
render(<VoipContactId name='John Doe' username='john.doe' transferedBy='Jane Doe' />, {
wrapper: mockAppRoot().build(),
legacyRoot: true,
});
expect(screen.getByText('From: Jane Doe')).toBeInTheDocument();
});

expect(screen.getByText('From: Jane Doe')).toBeInTheDocument();
it('should display copy button when username isnt available', async () => {
const phone = faker.phone.number();
render(<VoipContactId name={phone} />, {
wrapper: mockAppRoot().build(),
legacyRoot: true,
});

it('should display copy button when username isnt available', async () => {
const phone = faker.phone.number();
render(<VoipContactId name={phone} />, {
wrapper: mockAppRoot().build(),
legacyRoot: true,
});
const copyButton = screen.getByRole('button', { name: 'Copy_phone_number' });
expect(copyButton).toBeInTheDocument();

const copyButton = screen.getByRole('button', { name: 'Copy_phone_number' });
expect(copyButton).toBeInTheDocument();

copyButton.click();
await waitFor(() => expect(navigator.clipboard.writeText).toHaveBeenCalledWith(phone));
});
await userEvent.click(copyButton);
await waitFor(() => expect(navigator.clipboard.writeText).toHaveBeenCalledWith(phone));
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VoipContactId renders Loading without crashing 1`] = `
exports[`renders Loading without crashing 1`] = `
<body>
<div>
<div
Expand All @@ -21,7 +21,7 @@ exports[`VoipContactId renders Loading without crashing 1`] = `
</body>
`;

exports[`VoipContactId renders WithPhoneNumber without crashing 1`] = `
exports[`renders WithPhoneNumber without crashing 1`] = `
<body>
<div>
<div
Expand Down Expand Up @@ -59,7 +59,7 @@ exports[`VoipContactId renders WithPhoneNumber without crashing 1`] = `
</body>
`;

exports[`VoipContactId renders WithTransfer without crashing 1`] = `
exports[`renders WithTransfer without crashing 1`] = `
<body>
<div>
<div
Expand Down Expand Up @@ -106,7 +106,7 @@ exports[`VoipContactId renders WithTransfer without crashing 1`] = `
</body>
`;

exports[`VoipContactId renders WithUsername without crashing 1`] = `
exports[`renders WithUsername without crashing 1`] = `
<body>
<div>
<div
Expand Down
104 changes: 54 additions & 50 deletions packages/ui-voip/src/components/VoipDialPad/VoipDialPad.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,79 @@
/* eslint-disable no-await-in-loop */
import { installPointerEvent, triggerLongPress } from '@react-spectrum/test-utils';
import { mockAppRoot } from '@rocket.chat/mock-providers';
import { fireEvent, render, screen } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import DialPad from './VoipDialPad';

describe('VoipDialPad', () => {
beforeEach(() => {
jest.useFakeTimers();
});
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });

afterEach(() => {
jest.clearAllTimers();
});
installPointerEvent();

it('should not be editable by default', () => {
render(<DialPad value='' onChange={jest.fn()} />, { wrapper: mockAppRoot().build(), legacyRoot: true });
beforeEach(() => {
jest.useFakeTimers();
});

expect(screen.getByLabelText('Phone_number')).toHaveAttribute('readOnly');
});
afterEach(() => {
jest.clearAllTimers();
});

it('should enable input when editable', () => {
render(<DialPad editable value='' onChange={jest.fn()} />, { wrapper: mockAppRoot().build(), legacyRoot: true });
it('should not be editable by default', async () => {
aleksandernsilva marked this conversation as resolved.
Show resolved Hide resolved
render(<DialPad value='' onChange={jest.fn()} />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.getByLabelText('Phone_number')).not.toHaveAttribute('readOnly');
});
expect(screen.getByLabelText('Phone_number')).toHaveAttribute('readOnly');
});

it('should disable backspace button when input is empty', () => {
render(<DialPad editable value='' onChange={jest.fn()} />, { wrapper: mockAppRoot().build(), legacyRoot: true });
it('should enable input when editable', async () => {
render(<DialPad editable value='' onChange={jest.fn()} />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.getByTestId('dial-paid-input-backspace')).toBeDisabled();
});
expect(screen.getByLabelText('Phone_number')).not.toHaveAttribute('readOnly');
});

it('should enable backspace button when input has value', () => {
render(<DialPad editable value='123' onChange={jest.fn()} />, { wrapper: mockAppRoot().build(), legacyRoot: true });
it('should disable backspace button when input is empty', async () => {
render(<DialPad editable value='' onChange={jest.fn()} />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.getByTestId('dial-paid-input-backspace')).toBeEnabled();
});
expect(screen.getByTestId('dial-paid-input-backspace')).toBeDisabled();
});

it('should remove last character when backspace is clicked', () => {
const fn = jest.fn();
render(<DialPad editable value='123' onChange={fn} />, { wrapper: mockAppRoot().build(), legacyRoot: true });
it('should enable backspace button when input has value', async () => {
render(<DialPad editable value='123' onChange={jest.fn()} />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.getByLabelText('Phone_number')).toHaveValue('123');
expect(screen.getByTestId('dial-paid-input-backspace')).toBeEnabled();
});

screen.getByTestId('dial-paid-input-backspace').click();
it('should remove last character when backspace is clicked', async () => {
const fn = jest.fn();
render(<DialPad editable value='123' onChange={fn} />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(fn).toHaveBeenCalledWith('12');
});
expect(screen.getByLabelText('Phone_number')).toHaveValue('123');

it('should call onChange when number is clicked', () => {
const fn = jest.fn();
render(<DialPad editable value='123' onChange={fn} />, { wrapper: mockAppRoot().build(), legacyRoot: true });
await user.click(screen.getByTestId('dial-paid-input-backspace'));

expect(fn).toHaveBeenCalledWith('12');
});

it('should call onChange when number is clicked', async () => {
const fn = jest.fn();
render(<DialPad editable value='123' onChange={fn} />, { wrapper: mockAppRoot().build(), legacyRoot: true });

for (const digit of ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']) {
await user.click(screen.getByTestId(`dial-pad-button-${digit}`));
expect(fn).toHaveBeenCalledWith(`123${digit}`, digit);
}
});

['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'].forEach((digit) => {
screen.getByTestId(`dial-pad-button-${digit}`).click();
expect(fn).toHaveBeenCalledWith(`123${digit}`, digit);
});
});
it('should call onChange with + when 0 pressed and held', async () => {
const fn = jest.fn();
render(<DialPad editable longPress value='123' onChange={fn} />, { wrapper: mockAppRoot().build(), legacyRoot: true });

xit('should call onChange with + when 0 pressed and held', () => {
const fn = jest.fn();
render(<DialPad editable longPress value='123' onChange={fn} />, { wrapper: mockAppRoot().build(), legacyRoot: true });
const button = screen.getByTestId('dial-pad-button-0');

const button = screen.getByTestId('dial-pad-button-0');
await user.click(button);

button.click();
expect(fn).toHaveBeenCalledWith('1230', '0');
expect(fn).toHaveBeenCalledWith('1230', '0');

fireEvent.pointerDown(button);
jest.runOnlyPendingTimers();
fireEvent.pointerUp(button);
await triggerLongPress({ element: button, advanceTimer: (time = 800) => jest.advanceTimersByTime(time) });

expect(fn).toHaveBeenCalledWith('123+', '+');
});
expect(fn).toHaveBeenCalledWith('123+', '+');
});
17 changes: 8 additions & 9 deletions packages/ui-voip/src/components/VoipPopup/VoipPopup.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,49 +18,48 @@ jest.mock('../../hooks/useVoipDialer', () => ({
}));

const mockedUseVoipSession = jest.mocked(useVoipSession);
const appRoot = mockAppRoot();

it('should properly render incoming popup', async () => {
mockedUseVoipSession.mockImplementationOnce(() => createMockVoipSession({ type: 'INCOMING' }));
render(<VoipPopup />, { wrapper: appRoot.build(), legacyRoot: true });
render(<VoipPopup />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.getByTestId('vc-popup-incoming')).toBeInTheDocument();
});

it('should properly render ongoing popup', async () => {
mockedUseVoipSession.mockImplementationOnce(() => createMockVoipSession({ type: 'ONGOING' }));

render(<VoipPopup />, { wrapper: appRoot.build(), legacyRoot: true });
render(<VoipPopup />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.getByTestId('vc-popup-ongoing')).toBeInTheDocument();
});

it('should properly render outgoing popup', async () => {
mockedUseVoipSession.mockImplementationOnce(() => createMockVoipSession({ type: 'OUTGOING' }));

render(<VoipPopup />, { wrapper: appRoot.build(), legacyRoot: true });
render(<VoipPopup />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.getByTestId('vc-popup-outgoing')).toBeInTheDocument();
});

it('should properly render error popup', async () => {
mockedUseVoipSession.mockImplementationOnce(() => createMockVoipSession({ type: 'ERROR' }));

render(<VoipPopup />, { wrapper: appRoot.build(), legacyRoot: true });
render(<VoipPopup />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.getByTestId('vc-popup-error')).toBeInTheDocument();
});

it('should properly render dialer popup', async () => {
render(<VoipPopup />, { wrapper: appRoot.build(), legacyRoot: true });
render(<VoipPopup />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.getByTestId('vc-popup-dialer')).toBeInTheDocument();
});

it('should prioritize session over dialer', async () => {
mockedUseVoipSession.mockImplementationOnce(() => createMockVoipSession({ type: 'INCOMING' }));

render(<VoipPopup />, { wrapper: appRoot.build(), legacyRoot: true });
render(<VoipPopup />, { wrapper: mockAppRoot().build(), legacyRoot: true });

expect(screen.queryByTestId('vc-popup-dialer')).not.toBeInTheDocument();
expect(screen.getByTestId('vc-popup-incoming')).toBeInTheDocument();
Expand All @@ -69,12 +68,12 @@ it('should prioritize session over dialer', async () => {
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 />, { wrapper: appRoot.build(), legacyRoot: true });
const tree = render(<Story />, { wrapper: mockAppRoot().build(), legacyRoot: true });
expect(replaceReactAriaIds(tree.baseElement)).toMatchSnapshot();
});

test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
const { container } = render(<Story />, { wrapper: appRoot.build(), legacyRoot: true });
const { container } = render(<Story />, { wrapper: mockAppRoot().build(), legacyRoot: true });

const results = await axe(container);
expect(results).toHaveNoViolations();
Expand Down
Loading
Loading