Skip to content

Commit

Permalink
refactor: convert masquerade UI widgets to Function Components + Type…
Browse files Browse the repository at this point in the history
…Script (#1513)

* refactor: convert masquerade UI widgets to TypeScript

* test: improve test coverage

* chore: upgrade @testing-library/user-event to v14

* test: improve test coverage

* test: improve test coverage
  • Loading branch information
bradenmacdonald authored Dec 4, 2024
1 parent f5b6243 commit 4a925f9
Show file tree
Hide file tree
Showing 22 changed files with 562 additions and 519 deletions.
13 changes: 5 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "12.1.5",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "13.5.0",
"@testing-library/user-event": "14.5.2",
"axios-mock-adapter": "2.1.0",
"bundlewatch": "^0.4.0",
"eslint-import-resolver-webpack": "^0.13.9",
Expand Down
11 changes: 7 additions & 4 deletions src/course-home/dates-tab/DatesTab.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,20 @@ describe('DatesTab', () => {
});

it('shows extra info', async () => {
const user = userEvent.setup();
const { items } = await getDay('Sat, Aug 17, 2030');
expect(items).toHaveLength(3);

const tipIcon = within(items[2]).getByTestId('dates-extra-info');
const tipText = "ORA Dates are set by the instructor, and can't be changed";

expect(screen.queryByText(tipText)).toBeNull(); // tooltip does not start in DOM
userEvent.hover(tipIcon);
const tooltip = screen.getByText(tipText); // now it's there
userEvent.unhover(tipIcon);
await waitForElementToBeRemoved(tooltip); // and it's gone again
await user.hover(tipIcon);
screen.getByText(tipText); // now it's there
await user.unhover(tipIcon);
await waitFor(() => {
expect(screen.queryByText(tipText)).toBeNull(); // and it's gone again
});
});
});

Expand Down
10 changes: 6 additions & 4 deletions src/course-home/outline-tab/OutlineTab.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ describe('Outline Tab', () => {
});

it('handles expand/collapse all button click', async () => {
const user = userEvent.setup();
await fetchAndRender();
// Button renders as "Expand All"
const expandButton = screen.getByRole('button', { name: 'Expand all' });
Expand All @@ -153,11 +154,11 @@ describe('Outline Tab', () => {
expect(collapsedSectionNode).toHaveAttribute('aria-expanded', 'false');

// Click to expand section
userEvent.click(expandButton);
await user.click(expandButton);
await waitFor(() => expect(collapsedSectionNode).toHaveAttribute('aria-expanded', 'true'));

// Click to collapse section
userEvent.click(expandButton);
await user.click(expandButton);
await waitFor(() => expect(collapsedSectionNode).toHaveAttribute('aria-expanded', 'false'));
});

Expand Down Expand Up @@ -275,16 +276,17 @@ describe('Outline Tab', () => {
});

it('renders show more/less button and handles click', async () => {
const user = userEvent.setup();
expect(screen.getByTestId('alert-container-welcome')).toBeInTheDocument();
let showMoreButton = screen.getByRole('button', { name: 'Show More' });
expect(showMoreButton).toBeInTheDocument();

userEvent.click(showMoreButton);
await user.click(showMoreButton);
let showLessButton = screen.getByRole('button', { name: 'Show Less' });
expect(showLessButton).toBeInTheDocument();
expect(screen.getByTestId('long-welcome-message-iframe')).toBeInTheDocument();

userEvent.click(showLessButton);
await user.click(showLessButton);
showLessButton = screen.queryByRole('button', { name: 'Show Less' });
expect(showLessButton).not.toBeInTheDocument();
showMoreButton = screen.getByRole('button', { name: 'Show More' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ describe('<CourseOutlineTray />', () => {
});

it('collapses sidebar correctly when toggle button is clicked', async () => {
const user = userEvent.setup();
const mockToggleSidebar = jest.fn();
await initTestStore();
renderWithProvider({ toggleSidebar: mockToggleSidebar });
Expand All @@ -94,45 +95,48 @@ describe('<CourseOutlineTray />', () => {
expect(sidebarBackBtn).toBeInTheDocument();
expect(collapseBtn).toBeInTheDocument();

userEvent.click(collapseBtn);
await user.click(collapseBtn);
expect(mockToggleSidebar).toHaveBeenCalledWith(null);
});

it('toggles openSequenceId correctly when a sequence is clicked', async () => {
const user = userEvent.setup();
await initTestStore();
renderWithProvider();
const sequenceButton = screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` });
expect(sequenceButton).toBeInTheDocument();
userEvent.click(sequenceButton);
await user.click(sequenceButton);
expect(screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` })).toHaveAttribute('aria-expanded', 'true');
userEvent.click(sequenceButton);
await user.click(sequenceButton);
expect(screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` })).toHaveAttribute('aria-expanded', 'false');
});

it('updates setOpenSequenceId correctly when toggling sequences', async () => {
const user = userEvent.setup();
await initTestStore();
renderWithProvider();
const sequenceButton = screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` });
expect(sequenceButton).toBeInTheDocument();
userEvent.click(sequenceButton);
await user.click(sequenceButton);
expect(sequenceButton).toHaveAttribute('aria-expanded', 'true');
userEvent.click(sequenceButton);
await user.click(sequenceButton);
expect(sequenceButton).toHaveAttribute('aria-expanded', 'false');
});

it('navigates to section or sequence level correctly on click by back/section button', async () => {
const user = userEvent.setup();
await initTestStore();
renderWithProvider();

const sidebarBackBtn = screen.queryByRole('button', { name: section.title });
expect(sidebarBackBtn).toBeInTheDocument();
expect(screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` })).toBeInTheDocument();

userEvent.click(sidebarBackBtn);
await user.click(sidebarBackBtn);
expect(sidebarBackBtn).not.toBeInTheDocument();
expect(screen.queryByText(messages.courseOutlineTitle.defaultMessage)).toBeInTheDocument();

userEvent.click(screen.getByRole('button', { name: `${section.title} , ${courseOutlineMessages.incompleteSection.defaultMessage}` }));
await user.click(screen.getByRole('button', { name: `${section.title} , ${courseOutlineMessages.incompleteSection.defaultMessage}` }));
expect(screen.queryByRole('button', { name: section.title })).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe('<CourseOutlineTrigger />', () => {
}

it('renders correctly for desktop when sidebar is enabled', async () => {
const user = userEvent.setup();
const mockToggleSidebar = jest.fn();
await initTestStore({ enableNavigationSidebar: { enable_navigation_sidebar: true } });
renderWithProvider({ toggleSidebar: mockToggleSidebar }, { isMobileView: false });
Expand All @@ -52,13 +53,14 @@ describe('<CourseOutlineTrigger />', () => {
});
expect(toggleButton).toBeInTheDocument();

userEvent.click(toggleButton);
await user.click(toggleButton);

expect(mockToggleSidebar).toHaveBeenCalled();
expect(mockToggleSidebar).toHaveBeenCalledWith(outlineSidebarId);
});

it('renders correctly for mobile when sidebar is enabled', async () => {
const user = userEvent.setup();
const mockToggleSidebar = jest.fn();
await initTestStore({ enableNavigationSidebar: { enable_navigation_sidebar: true } });
renderWithProvider({
Expand All @@ -71,13 +73,14 @@ describe('<CourseOutlineTrigger />', () => {
});
expect(toggleButton).toBeInTheDocument();

userEvent.click(toggleButton);
await user.click(toggleButton);

expect(mockToggleSidebar).toHaveBeenCalled();
expect(mockToggleSidebar).toHaveBeenCalledWith(outlineSidebarId);
});

it('changes current sidebar value on click', async () => {
const user = userEvent.setup();
const mockToggleSidebar = jest.fn();
await initTestStore({ enableNavigationSidebar: { enable_navigation_sidebar: true } });
renderWithProvider({
Expand All @@ -91,7 +94,7 @@ describe('<CourseOutlineTrigger />', () => {
});
expect(toggleButton).toBeInTheDocument();

userEvent.click(toggleButton);
await user.click(toggleButton);

expect(mockToggleSidebar).toHaveBeenCalledTimes(1);
expect(mockToggleSidebar).toHaveBeenCalledWith(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('<SidebarSection />', () => {
});

it('renders correctly when section is incomplete', async () => {
const user = userEvent.setup();
await initTestStore();
const { getByText, container } = render(<RootWrapper />);

Expand All @@ -44,12 +45,13 @@ describe('<SidebarSection />', () => {
expect(container.querySelector('.text-success')).not.toBeInTheDocument();

const button = getByText(section.title);
userEvent.click(button);
await user.click(button);
expect(mockHandleSelectSection).toHaveBeenCalledTimes(1);
expect(mockHandleSelectSection).toHaveBeenCalledWith(section.id);
});

it('renders correctly when section is complete', async () => {
const user = userEvent.setup();
await initTestStore();
const { getByText, getByTestId } = render(
<RootWrapper section={{ ...section, completionStat: { completed: 4, total: 4 }, complete: true }} />,
Expand All @@ -60,7 +62,7 @@ describe('<SidebarSection />', () => {
expect(getByTestId('check-circle-icon')).toBeInTheDocument();

const button = getByText(section.title);
userEvent.click(button);
await user.click(button);
expect(mockHandleSelectSection).toHaveBeenCalledTimes(1);
expect(mockHandleSelectSection).toHaveBeenCalledWith(section.id);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('<SidebarSequence />', () => {
});

it('renders correctly when sequence is not collapsed and complete', async () => {
const user = userEvent.setup();
await initTestStore();
renderWithProvider({
defaultOpen: true,
Expand All @@ -67,6 +68,6 @@ describe('<SidebarSequence />', () => {
expect(screen.getByText(sequence.title)).toBeInTheDocument();
expect(screen.getByText(sequenceDescription)).toBeInTheDocument();
expect(screen.getByText(`, ${courseOutlineMessages.completedAssignment.defaultMessage}`)).toBeInTheDocument();
userEvent.click(screen.getByText(sequence.title));
await user.click(screen.getByText(sequence.title));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describe('<SidebarUnit />', () => {
});

it('sends log event correctly when unit is clicked', async () => {
const user = userEvent.setup();
await initTestStore();
renderWithProvider({ unit: { ...unit } });
const logData = {
Expand All @@ -99,7 +100,7 @@ describe('<SidebarUnit />', () => {
widget_placement: 'left',
};

userEvent.click(screen.getByText(unit.title));
await user.click(screen.getByText(unit.title));

expect(sendTrackEvent).toHaveBeenCalledWith('edx.ui.lms.sequence.tab_selected', logData);
expect(sendTrackingLogEvent).toHaveBeenCalledWith('edx.ui.lms.sequence.tab_selected', logData);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Input } from '@openedx/paragon';

import { MasqueradeStatus, Payload } from './data/api';
import messages from './messages';

interface Props extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onSubmit' | 'onError'> {
onError: (error: string) => void;
onSubmit: (payload: Payload) => Promise<MasqueradeStatus>;
}

export const MasqueradeUserNameInput: React.FC<Props> = ({ onSubmit, onError, ...otherProps }) => {
const intl = useIntl();

const handleSubmit = React.useCallback((userIdentifier: string) => {
const payload: Payload = {
role: 'student',
user_name: userIdentifier, // user name or email
};
onSubmit(payload).then((data) => {
if (data && data.success) {
global.location.reload();
} else {
const error = (data && data.error) || '';
onError(error);
}
}).catch(() => {
const message = intl.formatMessage(messages.genericError);
onError(message);
});
return true;
}, [onError]);

const handleKeyPress = React.useCallback((event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
return handleSubmit(event.currentTarget.value);
}
return true;
}, [handleSubmit]);

return (
<Input
aria-labelledby="masquerade-search-label"
label={intl.formatMessage(messages.userNameLabel)}
onKeyPress={handleKeyPress}
type="text"
{...otherProps}
/>
);
};
Loading

0 comments on commit 4a925f9

Please sign in to comment.