fix: masquerade dropdown not showing current selection (#1434)
* feat: remove child components from state and use data instead * fix: change active selection based on user input * test: add test cases
This commit is contained in:
@@ -22,7 +22,8 @@ class MasqueradeWidget extends Component {
|
||||
this.state = {
|
||||
autoFocus: false,
|
||||
masquerade: 'Staff',
|
||||
options: [],
|
||||
active: {},
|
||||
available: [],
|
||||
shouldShowUserNameInput: false,
|
||||
masqueradeUsername: null,
|
||||
};
|
||||
@@ -58,21 +59,42 @@ class MasqueradeWidget extends Component {
|
||||
}
|
||||
|
||||
onSuccess(data) {
|
||||
const options = this.parseAvailableOptions(data);
|
||||
const { active, available } = this.parseAvailableOptions(data);
|
||||
this.setState({
|
||||
options,
|
||||
active,
|
||||
available,
|
||||
});
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
const options = this.state.available.map((group) => (
|
||||
<MasqueradeWidgetOption
|
||||
groupId={group.groupId}
|
||||
groupName={group.name}
|
||||
key={group.name}
|
||||
role={group.role}
|
||||
selected={this.state.active}
|
||||
userName={group.userName}
|
||||
userPartitionId={group.userPartitionId}
|
||||
userNameInputToggle={(...args) => this.toggle(...args)}
|
||||
onSubmit={(payload) => this.onSubmit(payload)}
|
||||
/>
|
||||
));
|
||||
return options;
|
||||
}
|
||||
|
||||
clearError() {
|
||||
this.props.onError('');
|
||||
}
|
||||
|
||||
toggle(show) {
|
||||
toggle(show, groupId, groupName, role, userName, userPartitionId) {
|
||||
this.setState(prevState => ({
|
||||
autoFocus: true,
|
||||
masquerade: 'Specific Student...',
|
||||
masquerade: groupName,
|
||||
shouldShowUserNameInput: show === undefined ? !prevState.shouldShowUserNameInput : show,
|
||||
active: {
|
||||
...prevState.active, groupId, role, userName, userPartitionId,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -80,19 +102,6 @@ class MasqueradeWidget extends Component {
|
||||
const data = postData || {};
|
||||
const active = data.active || {};
|
||||
const available = data.available || [];
|
||||
const options = available.map((group) => (
|
||||
<MasqueradeWidgetOption
|
||||
groupId={group.groupId}
|
||||
groupName={group.name}
|
||||
key={group.name}
|
||||
role={group.role}
|
||||
selected={active}
|
||||
userName={group.userName}
|
||||
userPartitionId={group.userPartitionId}
|
||||
userNameInputToggle={(...args) => this.toggle(...args)}
|
||||
onSubmit={(payload) => this.onSubmit(payload)}
|
||||
/>
|
||||
));
|
||||
if (active.userName) {
|
||||
this.setState({
|
||||
autoFocus: false,
|
||||
@@ -105,14 +114,13 @@ class MasqueradeWidget extends Component {
|
||||
} else if (active.role === 'student') {
|
||||
this.setState({ masquerade: 'Learner' });
|
||||
}
|
||||
return options;
|
||||
return { active, available };
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
autoFocus,
|
||||
masquerade,
|
||||
options,
|
||||
shouldShowUserNameInput,
|
||||
masqueradeUsername,
|
||||
} = this.state;
|
||||
@@ -126,7 +134,7 @@ class MasqueradeWidget extends Component {
|
||||
{masquerade}
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
{options}
|
||||
{this.getOptions()}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import React from 'react';
|
||||
import { getAllByRole } from '@testing-library/dom';
|
||||
import { act } from '@testing-library/react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import MasqueradeWidget from './MasqueradeWidget';
|
||||
import {
|
||||
render, screen, fireEvent, initializeTestStore, waitFor, logUnhandledRequests,
|
||||
} from '../../setupTest';
|
||||
|
||||
const originalConfig = jest.requireActual('@edx/frontend-platform').getConfig();
|
||||
jest.mock('@edx/frontend-platform', () => ({
|
||||
...jest.requireActual('@edx/frontend-platform'),
|
||||
getConfig: jest.fn(),
|
||||
}));
|
||||
getConfig.mockImplementation(() => originalConfig);
|
||||
|
||||
describe('Masquerade Widget Dropdown', () => {
|
||||
let mockData;
|
||||
let courseware;
|
||||
let mockResponse;
|
||||
let axiosMock;
|
||||
let masqueradeUrl;
|
||||
const masqueradeOptions = [
|
||||
{
|
||||
name: 'Staff',
|
||||
role: 'staff',
|
||||
},
|
||||
{
|
||||
name: 'Specific Student...',
|
||||
role: 'student',
|
||||
user_name: '',
|
||||
},
|
||||
{
|
||||
group_id: 1,
|
||||
name: 'Audit',
|
||||
role: 'student',
|
||||
user_partition_id: 50,
|
||||
},
|
||||
];
|
||||
|
||||
beforeAll(async () => {
|
||||
const store = await initializeTestStore();
|
||||
courseware = store.getState().courseware;
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
masqueradeUrl = `${getConfig().LMS_BASE_URL}/courses/${courseware.courseId}/masquerade`;
|
||||
mockData = {
|
||||
courseId: courseware.courseId,
|
||||
onError: () => {},
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockResponse = {
|
||||
success: true,
|
||||
active: {
|
||||
course_key: courseware.courseId,
|
||||
group_id: null,
|
||||
role: 'staff',
|
||||
user_name: null,
|
||||
user_partition_id: null,
|
||||
group_name: null,
|
||||
},
|
||||
available: masqueradeOptions,
|
||||
};
|
||||
axiosMock.reset();
|
||||
axiosMock.onGet(masqueradeUrl).reply(200, mockResponse);
|
||||
logUnhandledRequests(axiosMock);
|
||||
});
|
||||
|
||||
it('renders masquerade name correctly', async () => {
|
||||
render(<MasqueradeWidget {...mockData} />);
|
||||
await waitFor(() => expect(axiosMock.history.get).toHaveLength(1));
|
||||
expect(screen.getByRole('button')).toHaveTextContent('Staff');
|
||||
});
|
||||
|
||||
masqueradeOptions.forEach((option) => {
|
||||
it(`marks role ${option.role} as active`, async () => {
|
||||
const active = {
|
||||
course_key: courseware.courseId,
|
||||
group_id: option.group_id ?? null,
|
||||
role: option.role,
|
||||
user_name: option.user_name ?? null,
|
||||
user_partition_id: option.user_partition_id ?? null,
|
||||
group_name: null,
|
||||
};
|
||||
|
||||
mockResponse = {
|
||||
success: true,
|
||||
active,
|
||||
available: masqueradeOptions,
|
||||
};
|
||||
|
||||
axiosMock.reset();
|
||||
axiosMock.onGet(masqueradeUrl).reply(200, mockResponse);
|
||||
|
||||
const { container } = render(<MasqueradeWidget {...mockData} />);
|
||||
const dropdownToggle = container.querySelector('.dropdown-toggle');
|
||||
await act(async () => {
|
||||
await fireEvent.click(dropdownToggle);
|
||||
});
|
||||
const dropdownMenu = container.querySelector('.dropdown-menu');
|
||||
getAllByRole(dropdownMenu, 'button', { hidden: true }).forEach(button => {
|
||||
if (button.textContent === option.name) {
|
||||
expect(button).toHaveClass('active');
|
||||
} else {
|
||||
expect(button).not.toHaveClass('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('handles the clicks with toggle', async () => {
|
||||
const { container } = render(<MasqueradeWidget {...mockData} />);
|
||||
await waitFor(() => expect(axiosMock.history.get).toHaveLength(1));
|
||||
|
||||
const dropdownToggle = container.querySelector('.dropdown-toggle');
|
||||
await act(async () => {
|
||||
await fireEvent.click(dropdownToggle);
|
||||
});
|
||||
const dropdownMenu = container.querySelector('.dropdown-menu');
|
||||
const studentOption = getAllByRole(dropdownMenu, 'button', { hidden: true }).filter(
|
||||
button => (button.textContent === 'Specific Student...'),
|
||||
)[0];
|
||||
await act(async () => {
|
||||
await fireEvent.click(studentOption);
|
||||
});
|
||||
getAllByRole(dropdownMenu, 'button', { hidden: true }).forEach(button => {
|
||||
if (button.textContent === 'Specific Student...') {
|
||||
expect(button).toHaveClass('active');
|
||||
} else {
|
||||
expect(button).not.toHaveClass('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,6 +16,7 @@ class MasqueradeWidgetOption extends Component {
|
||||
event.target.parentNode.parentNode.click();
|
||||
const {
|
||||
groupId,
|
||||
groupName,
|
||||
role,
|
||||
userName,
|
||||
userPartitionId,
|
||||
@@ -23,7 +24,7 @@ class MasqueradeWidgetOption extends Component {
|
||||
} = this.props;
|
||||
const payload = {};
|
||||
if (userName || userName === '') {
|
||||
userNameInputToggle(true);
|
||||
userNameInputToggle(true, groupId, groupName, role, userName, userPartitionId);
|
||||
return false;
|
||||
}
|
||||
if (role) {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import React from 'react';
|
||||
import { getAllByRole } from '@testing-library/dom';
|
||||
import { act } from '@testing-library/react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import MasqueradeWidgetOption from './MasqueradeWidgetOption';
|
||||
import {
|
||||
render, fireEvent, initializeTestStore,
|
||||
} from '../../setupTest';
|
||||
|
||||
const originalConfig = jest.requireActual('@edx/frontend-platform').getConfig();
|
||||
jest.mock('@edx/frontend-platform', () => ({
|
||||
...jest.requireActual('@edx/frontend-platform'),
|
||||
getConfig: jest.fn(),
|
||||
}));
|
||||
getConfig.mockImplementation(() => originalConfig);
|
||||
|
||||
describe('Masquerade Widget Dropdown', () => {
|
||||
let courseware;
|
||||
let mockDataStaff;
|
||||
let mockDataStudent;
|
||||
let active;
|
||||
|
||||
beforeAll(async () => {
|
||||
const store = await initializeTestStore();
|
||||
courseware = store.getState().courseware;
|
||||
active = {
|
||||
courseKey: courseware.courseId,
|
||||
groupId: null,
|
||||
role: 'staff',
|
||||
userName: null,
|
||||
userPartitionId: null,
|
||||
groupName: null,
|
||||
};
|
||||
mockDataStaff = {
|
||||
groupId: null,
|
||||
groupName: 'Staff',
|
||||
key: 'Staff',
|
||||
role: 'staff',
|
||||
selected: active,
|
||||
userName: null,
|
||||
userPartitionId: null,
|
||||
userNameInputToggle: () => {},
|
||||
onSubmit: () => {},
|
||||
};
|
||||
mockDataStudent = {
|
||||
groupId: null,
|
||||
groupName: 'Specific Student...',
|
||||
key: 'Specific Student...',
|
||||
role: 'student',
|
||||
selected: active,
|
||||
userName: '',
|
||||
userPartitionId: null,
|
||||
userNameInputToggle: () => {},
|
||||
onSubmit: () => {},
|
||||
};
|
||||
Object.defineProperty(global, 'location', {
|
||||
configurable: true,
|
||||
value: { reload: jest.fn() },
|
||||
});
|
||||
});
|
||||
|
||||
it('renders masquerade active option correctly', async () => {
|
||||
const { container } = render(<MasqueradeWidgetOption {...mockDataStaff} />);
|
||||
const button = getAllByRole(container, 'button', { hidden: true })[0];
|
||||
expect(button).toHaveTextContent('Staff');
|
||||
expect(button).toHaveClass('active');
|
||||
});
|
||||
|
||||
it('renders masquerade inactive option correctly', async () => {
|
||||
const { container } = render(<MasqueradeWidgetOption {...mockDataStudent} />);
|
||||
const button = getAllByRole(container, 'button', { hidden: true })[0];
|
||||
expect(button).toHaveTextContent('Specific Student...');
|
||||
expect(button).not.toHaveClass('active');
|
||||
});
|
||||
|
||||
it('handles the clicks regular option', () => {
|
||||
const onSubmit = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
const { container } = render(<MasqueradeWidgetOption {...mockDataStaff} onSubmit={onSubmit} />);
|
||||
const button = getAllByRole(container, 'button', { hidden: true })[0];
|
||||
act(() => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
expect(onSubmit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles the clicks student option', () => {
|
||||
const userNameInputToggle = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
const { container } = render(
|
||||
<MasqueradeWidgetOption {...mockDataStudent} userNameInputToggle={userNameInputToggle} />,
|
||||
);
|
||||
const button = getAllByRole(container, 'button', { hidden: true })[0];
|
||||
act(() => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
expect(userNameInputToggle).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user