test: Deprecate react-unit-test-utils 5/9 (#490)
* test: deprecate react-unit-test-utils 5/9 * fix: lint issues * test: change fireEvent for userEvent --------- Co-authored-by: diana-villalvazo-wgu <diana.villalvazo@wgu.edu>
This commit is contained in:
@@ -1,108 +1,189 @@
|
||||
/* eslint-disable import/no-named-as-default */
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { render, screen, initializeMocks } from 'testUtilsExtra';
|
||||
import { DataTable } from '@openedx/paragon';
|
||||
|
||||
import selectors from 'data/selectors';
|
||||
import { bulkManagementColumns } from 'data/constants/app';
|
||||
|
||||
import { HistoryTable, mapHistoryRows, mapStateToProps } from './HistoryTable';
|
||||
import ResultsSummary from './ResultsSummary';
|
||||
import { HistoryTable, mapStateToProps } from './HistoryTable';
|
||||
|
||||
jest.mock('@openedx/paragon', () => ({ DataTable: () => 'DataTable' }));
|
||||
jest.unmock('@openedx/paragon');
|
||||
jest.unmock('react');
|
||||
jest.unmock('@edx/frontend-platform/i18n');
|
||||
initializeMocks();
|
||||
|
||||
jest.mock('@edx/frontend-platform/i18n', () => ({
|
||||
defineMessages: m => m,
|
||||
FormattedMessage: () => 'FormattedMessage',
|
||||
jest.mock('@openedx/paragon', () => ({
|
||||
DataTable: jest.fn(() => <div data-testid="data-table">DataTable</div>),
|
||||
}));
|
||||
jest.mock('./ResultsSummary', () => jest.fn(() => <div data-testid="results-summary">ResultsSummary</div>));
|
||||
jest.mock('data/selectors', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
grades: {
|
||||
bulkManagementHistoryEntries: jest.fn(state => ({ historyEntries: state })),
|
||||
bulkManagementHistoryEntries: jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
jest.mock('./ResultsSummary', () => 'ResultsSummary');
|
||||
|
||||
describe('HistoryTable', () => {
|
||||
describe('component', () => {
|
||||
const entry1 = {
|
||||
originalFilename: 'blue.png',
|
||||
user: 'Eifel',
|
||||
timeUploaded: '65',
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const mockBulkManagementHistory = [
|
||||
{
|
||||
originalFilename: 'test-file-1.csv',
|
||||
user: 'test-user-1',
|
||||
timeUploaded: '2025-01-01T10:00:00Z',
|
||||
resultsSummary: {
|
||||
rowId: 12,
|
||||
courseId: 'Da Bu Dee',
|
||||
text: 'Da ba daa',
|
||||
rowId: 1,
|
||||
text: 'Download results 1',
|
||||
},
|
||||
};
|
||||
const entry2 = {
|
||||
originalFilename: 'allStar.jpg',
|
||||
user: 'Smashmouth',
|
||||
timeUploaded: '2000s?',
|
||||
},
|
||||
{
|
||||
originalFilename: 'test-file-2.csv',
|
||||
user: 'test-user-2',
|
||||
timeUploaded: '2025-01-02T10:00:00Z',
|
||||
resultsSummary: {
|
||||
courseId: 'rockstar',
|
||||
rowId: 2,
|
||||
text: 'all that glitters is gold',
|
||||
text: 'Download results 2',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('mapHistoryRows', () => {
|
||||
const mockRow = {
|
||||
resultsSummary: {
|
||||
rowId: 1,
|
||||
text: 'Download results',
|
||||
},
|
||||
originalFilename: 'test-file.csv',
|
||||
user: 'test-user',
|
||||
timeUploaded: '2025-01-01T10:00:00Z',
|
||||
};
|
||||
const props = {
|
||||
bulkManagementHistory: [entry1, entry2],
|
||||
};
|
||||
let el;
|
||||
describe('snapshot', () => {
|
||||
beforeEach(() => {
|
||||
el = shallow(<HistoryTable {...props} />);
|
||||
});
|
||||
test('snapshot - loads formatted table', () => {
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
});
|
||||
describe('history table', () => {
|
||||
let table;
|
||||
beforeEach(() => {
|
||||
table = el.instance.findByType(DataTable);
|
||||
});
|
||||
describe('data (from bulkManagementHistory.map(this.formatHistoryRow)', () => {
|
||||
const fieldAssertions = [
|
||||
'maps resultsSummay to ResultsSummary',
|
||||
'wraps filename and user',
|
||||
'forwards the rest',
|
||||
];
|
||||
test(`snapshot: ${fieldAssertions.join(', ')}`, () => {
|
||||
expect(table[0].props.data).toMatchSnapshot();
|
||||
});
|
||||
test(fieldAssertions.join(', '), () => {
|
||||
const rows = table[0].props.data;
|
||||
expect(rows[0].resultsSummary).toEqual(<ResultsSummary {...entry1.resultsSummary} />);
|
||||
expect(rows[0].user).toEqual(<span className="wrap-text-in-cell">{entry1.user}</span>);
|
||||
expect(
|
||||
rows[0].filename,
|
||||
).toEqual(<span className="wrap-text-in-cell">{entry1.originalFilename}</span>);
|
||||
expect(rows[1].resultsSummary).toEqual(<ResultsSummary {...entry2.resultsSummary} />);
|
||||
expect(rows[1].user).toEqual(<span className="wrap-text-in-cell">{entry2.user}</span>);
|
||||
expect(
|
||||
rows[1].filename,
|
||||
).toEqual(<span className="wrap-text-in-cell">{entry2.originalFilename}</span>);
|
||||
});
|
||||
});
|
||||
test('columns from bulkManagementColumns', () => {
|
||||
expect(table[0].props.columns).toEqual(bulkManagementColumns);
|
||||
});
|
||||
});
|
||||
|
||||
it('transforms row data correctly', () => {
|
||||
const result = mapHistoryRows(mockRow);
|
||||
|
||||
expect(result).toHaveProperty('resultsSummary');
|
||||
expect(result).toHaveProperty('filename');
|
||||
expect(result).toHaveProperty('user');
|
||||
expect(result).toHaveProperty('timeUploaded');
|
||||
expect(result.timeUploaded).toBe('2025-01-01T10:00:00Z');
|
||||
});
|
||||
|
||||
it('wraps filename in span with correct class', () => {
|
||||
const result = mapHistoryRows(mockRow);
|
||||
render(<div>{result.filename}</div>);
|
||||
|
||||
const filenameSpan = screen.getByText('test-file.csv');
|
||||
expect(filenameSpan).toBeInTheDocument();
|
||||
expect(filenameSpan).toHaveClass('wrap-text-in-cell');
|
||||
});
|
||||
|
||||
it('wraps user in span with correct class', () => {
|
||||
const result = mapHistoryRows(mockRow);
|
||||
render(<div>{result.user}</div>);
|
||||
|
||||
const userSpan = screen.getByText('test-user');
|
||||
expect(userSpan).toBeInTheDocument();
|
||||
expect(userSpan).toHaveClass('wrap-text-in-cell');
|
||||
});
|
||||
|
||||
it('renders ResultsSummary component with correct props', () => {
|
||||
const result = mapHistoryRows(mockRow);
|
||||
render(<div>{result.resultsSummary}</div>);
|
||||
|
||||
expect(ResultsSummary).toHaveBeenCalledWith(mockRow.resultsSummary, {});
|
||||
expect(screen.getByTestId('results-summary')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('component', () => {
|
||||
it('renders DataTable with empty data when no history provided', () => {
|
||||
render(<HistoryTable />);
|
||||
|
||||
expect(DataTable).toHaveBeenCalledWith(
|
||||
{
|
||||
data: [],
|
||||
hasFixedColumnWidths: true,
|
||||
columns: bulkManagementColumns,
|
||||
className: 'table-striped',
|
||||
itemCount: 0,
|
||||
},
|
||||
{},
|
||||
);
|
||||
expect(screen.getByTestId('data-table')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders DataTable with mapped history data', () => {
|
||||
render(
|
||||
<HistoryTable bulkManagementHistory={mockBulkManagementHistory} />,
|
||||
);
|
||||
|
||||
expect(DataTable).toHaveBeenCalledWith(
|
||||
{
|
||||
data: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
filename: expect.any(Object),
|
||||
user: expect.any(Object),
|
||||
resultsSummary: expect.any(Object),
|
||||
timeUploaded: '2025-01-01T10:00:00Z',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
filename: expect.any(Object),
|
||||
user: expect.any(Object),
|
||||
resultsSummary: expect.any(Object),
|
||||
timeUploaded: '2025-01-02T10:00:00Z',
|
||||
}),
|
||||
]),
|
||||
hasFixedColumnWidths: true,
|
||||
columns: bulkManagementColumns,
|
||||
className: 'table-striped',
|
||||
itemCount: 2,
|
||||
},
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
it('passes correct props to DataTable', () => {
|
||||
render(
|
||||
<HistoryTable bulkManagementHistory={mockBulkManagementHistory} />,
|
||||
);
|
||||
|
||||
const dataTableCall = DataTable.mock.calls[0][0];
|
||||
expect(dataTableCall.hasFixedColumnWidths).toBe(true);
|
||||
expect(dataTableCall.columns).toBe(bulkManagementColumns);
|
||||
expect(dataTableCall.className).toBe('table-striped');
|
||||
expect(dataTableCall.itemCount).toBe(mockBulkManagementHistory.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapStateToProps', () => {
|
||||
const testState = { a: 'simple', test: 'state' };
|
||||
let mapped;
|
||||
const mockState = { test: 'state' };
|
||||
const mockHistoryEntries = [
|
||||
{ originalFilename: 'file1.csv', user: 'user1' },
|
||||
{ originalFilename: 'file2.csv', user: 'user2' },
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
mapped = mapStateToProps(testState);
|
||||
selectors.grades.bulkManagementHistoryEntries.mockReturnValue(
|
||||
mockHistoryEntries,
|
||||
);
|
||||
});
|
||||
test('bulkManagementHistory from grades.bulkManagementHistoryEntries', () => {
|
||||
|
||||
it('maps bulkManagementHistory from selector', () => {
|
||||
const result = mapStateToProps(mockState);
|
||||
|
||||
expect(
|
||||
mapped.bulkManagementHistory,
|
||||
).toEqual(selectors.grades.bulkManagementHistoryEntries(testState));
|
||||
selectors.grades.bulkManagementHistoryEntries,
|
||||
).toHaveBeenCalledWith(mockState);
|
||||
expect(result.bulkManagementHistory).toBe(mockHistoryEntries);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`HistoryTable component snapshot history table data (from bulkManagementHistory.map(this.formatHistoryRow) snapshot: maps resultsSummay to ResultsSummary, wraps filename and user, forwards the rest 1`] = `
|
||||
[
|
||||
{
|
||||
"filename": <span
|
||||
className="wrap-text-in-cell"
|
||||
>
|
||||
blue.png
|
||||
</span>,
|
||||
"resultsSummary": <ResultsSummary
|
||||
courseId="Da Bu Dee"
|
||||
rowId={12}
|
||||
text="Da ba daa"
|
||||
/>,
|
||||
"timeUploaded": "65",
|
||||
"user": <span
|
||||
className="wrap-text-in-cell"
|
||||
>
|
||||
Eifel
|
||||
</span>,
|
||||
},
|
||||
{
|
||||
"filename": <span
|
||||
className="wrap-text-in-cell"
|
||||
>
|
||||
allStar.jpg
|
||||
</span>,
|
||||
"resultsSummary": <ResultsSummary
|
||||
courseId="rockstar"
|
||||
rowId={2}
|
||||
text="all that glitters is gold"
|
||||
/>,
|
||||
"timeUploaded": "2000s?",
|
||||
"user": <span
|
||||
className="wrap-text-in-cell"
|
||||
>
|
||||
Smashmouth
|
||||
</span>,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`HistoryTable component snapshot snapshot - loads formatted table 1`] = `
|
||||
<DataTable
|
||||
className="table-striped"
|
||||
columns={
|
||||
[
|
||||
{
|
||||
"Header": "Gradebook",
|
||||
"accessor": "filename",
|
||||
"columnSortable": false,
|
||||
"width": "col-5",
|
||||
},
|
||||
{
|
||||
"Header": "Download Summary",
|
||||
"accessor": "resultsSummary",
|
||||
"columnSortable": false,
|
||||
"width": "col",
|
||||
},
|
||||
{
|
||||
"Header": "Who",
|
||||
"accessor": "user",
|
||||
"columnSortable": false,
|
||||
"width": "col-1",
|
||||
},
|
||||
{
|
||||
"Header": "When",
|
||||
"accessor": "timeUploaded",
|
||||
"columnSortable": false,
|
||||
"width": "col",
|
||||
},
|
||||
]
|
||||
}
|
||||
data={
|
||||
[
|
||||
{
|
||||
"filename": <span
|
||||
className="wrap-text-in-cell"
|
||||
>
|
||||
blue.png
|
||||
</span>,
|
||||
"resultsSummary": <ResultsSummary
|
||||
courseId="Da Bu Dee"
|
||||
rowId={12}
|
||||
text="Da ba daa"
|
||||
/>,
|
||||
"timeUploaded": "65",
|
||||
"user": <span
|
||||
className="wrap-text-in-cell"
|
||||
>
|
||||
Eifel
|
||||
</span>,
|
||||
},
|
||||
{
|
||||
"filename": <span
|
||||
className="wrap-text-in-cell"
|
||||
>
|
||||
allStar.jpg
|
||||
</span>,
|
||||
"resultsSummary": <ResultsSummary
|
||||
courseId="rockstar"
|
||||
rowId={2}
|
||||
text="all that glitters is gold"
|
||||
/>,
|
||||
"timeUploaded": "2000s?",
|
||||
"user": <span
|
||||
className="wrap-text-in-cell"
|
||||
>
|
||||
Smashmouth
|
||||
</span>,
|
||||
},
|
||||
]
|
||||
}
|
||||
hasFixedColumnWidths={true}
|
||||
itemCount={2}
|
||||
/>
|
||||
`;
|
||||
@@ -1,72 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`StudentGroupsFilter component render snapshot 1`] = `
|
||||
<Fragment>
|
||||
<SelectGroup
|
||||
id="Tracks"
|
||||
label="Tracks"
|
||||
onChange={[MockFunction]}
|
||||
options={
|
||||
[
|
||||
<option
|
||||
value="Track-All"
|
||||
>
|
||||
Track-All
|
||||
</option>,
|
||||
<option
|
||||
value="v1"
|
||||
>
|
||||
n1
|
||||
</option>,
|
||||
<option
|
||||
value="v2"
|
||||
>
|
||||
n2
|
||||
</option>,
|
||||
<option
|
||||
value="v3"
|
||||
>
|
||||
n3
|
||||
</option>,
|
||||
<option
|
||||
value="v4"
|
||||
>
|
||||
n4
|
||||
</option>,
|
||||
]
|
||||
}
|
||||
value="test-track"
|
||||
/>
|
||||
<SelectGroup
|
||||
disabled={false}
|
||||
id="Cohorts"
|
||||
label="Cohorts"
|
||||
onChange={[MockFunction]}
|
||||
options={
|
||||
[
|
||||
<option
|
||||
value="Cohort-All"
|
||||
>
|
||||
Cohort-All
|
||||
</option>,
|
||||
<option
|
||||
value="v1"
|
||||
>
|
||||
n1
|
||||
</option>,
|
||||
<option
|
||||
value="v2"
|
||||
>
|
||||
n2
|
||||
</option>,
|
||||
<option
|
||||
value="v3"
|
||||
>
|
||||
n3
|
||||
</option>,
|
||||
]
|
||||
}
|
||||
value="test-cohort"
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
||||
@@ -1,84 +1,167 @@
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { render, screen, initializeMocks } from 'testUtilsExtra';
|
||||
|
||||
import SelectGroup from '../SelectGroup';
|
||||
import { StudentGroupsFilter } from './index';
|
||||
import useStudentGroupsFilterData from './hooks';
|
||||
import StudentGroupsFilter from '.';
|
||||
|
||||
jest.mock('../SelectGroup', () => 'SelectGroup');
|
||||
jest.mock('./hooks', () => ({ __esModule: true, default: jest.fn() }));
|
||||
jest.unmock('@openedx/paragon');
|
||||
jest.unmock('react');
|
||||
jest.unmock('@edx/frontend-platform/i18n');
|
||||
jest.mock('../SelectGroup', () => jest.fn(() => <div data-testid="select-group">SelectGroup</div>));
|
||||
jest.mock('./hooks', () => jest.fn());
|
||||
|
||||
const props = {
|
||||
cohorts: {
|
||||
value: 'test-cohort',
|
||||
initializeMocks();
|
||||
|
||||
describe('StudentGroupsFilter', () => {
|
||||
const mockUpdateQueryParams = jest.fn();
|
||||
|
||||
const mockTracksData = {
|
||||
value: 'test-track-value',
|
||||
entries: [
|
||||
{ value: 'v1', name: 'n1' },
|
||||
{ value: 'v2', name: 'n2' },
|
||||
{ value: 'v3', name: 'n3' },
|
||||
{ value: 'track1', name: 'Track 1' },
|
||||
{ value: 'track2', name: 'Track 2' },
|
||||
],
|
||||
handleChange: jest.fn(),
|
||||
};
|
||||
|
||||
const mockCohortsData = {
|
||||
value: 'test-cohort-value',
|
||||
entries: [
|
||||
{ value: 'cohort1', name: 'Cohort 1' },
|
||||
{ value: 'cohort2', name: 'Cohort 2' },
|
||||
],
|
||||
handleChange: jest.fn(),
|
||||
isDisabled: false,
|
||||
},
|
||||
tracks: {
|
||||
value: 'test-track',
|
||||
entries: [
|
||||
{ value: 'v1', name: 'n1' },
|
||||
{ value: 'v2', name: 'n2' },
|
||||
{ value: 'v3', name: 'n3' },
|
||||
{ value: 'v4', name: 'n4' },
|
||||
],
|
||||
handleChange: jest.fn(),
|
||||
},
|
||||
};
|
||||
useStudentGroupsFilterData.mockReturnValue(props);
|
||||
const updateQueryParams = jest.fn();
|
||||
};
|
||||
|
||||
let el;
|
||||
describe('StudentGroupsFilter component', () => {
|
||||
beforeAll(() => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
el = shallow(<StudentGroupsFilter updateQueryParams={updateQueryParams} />);
|
||||
});
|
||||
describe('behavior', () => {
|
||||
it('initializes hooks', () => {
|
||||
expect(useStudentGroupsFilterData).toHaveBeenCalledWith({ updateQueryParams });
|
||||
expect(useIntl).toHaveBeenCalledWith();
|
||||
useStudentGroupsFilterData.mockReturnValue({
|
||||
tracks: mockTracksData,
|
||||
cohorts: mockCohortsData,
|
||||
});
|
||||
});
|
||||
describe('render', () => {
|
||||
test('snapshot', () => {
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
|
||||
it('calls useStudentGroupsFilterData hook with updateQueryParams', () => {
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
expect(useStudentGroupsFilterData).toHaveBeenCalledWith({
|
||||
updateQueryParams: mockUpdateQueryParams,
|
||||
});
|
||||
test('track options', () => {
|
||||
const {
|
||||
options,
|
||||
onChange,
|
||||
value,
|
||||
} = el.instance.findByType(SelectGroup)[0].props;
|
||||
expect(value).toEqual(props.tracks.value);
|
||||
expect(onChange).toEqual(props.tracks.handleChange);
|
||||
expect(options.length).toEqual(5);
|
||||
const testEntry = props.tracks.entries[0];
|
||||
const optionProps = options[1].props;
|
||||
expect(optionProps.value).toEqual(testEntry.value);
|
||||
expect(optionProps.children).toEqual(testEntry.name);
|
||||
});
|
||||
|
||||
it('renders two SelectGroup components', () => {
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
expect(SelectGroup).toHaveBeenCalledTimes(2);
|
||||
expect(screen.getAllByTestId('select-group')).toHaveLength(2);
|
||||
});
|
||||
|
||||
describe('tracks SelectGroup', () => {
|
||||
it('renders tracks SelectGroup with correct props', () => {
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
const tracksCall = SelectGroup.mock.calls[0][0];
|
||||
expect(tracksCall.id).toBe('Tracks');
|
||||
expect(tracksCall.value).toBe(mockTracksData.value);
|
||||
expect(tracksCall.onChange).toBe(mockTracksData.handleChange);
|
||||
});
|
||||
test('cohort options', () => {
|
||||
const {
|
||||
options,
|
||||
onChange,
|
||||
disabled,
|
||||
value,
|
||||
} = el.instance.findByType(SelectGroup)[1].props;
|
||||
expect(value).toEqual(props.cohorts.value);
|
||||
expect(disabled).toEqual(false);
|
||||
expect(onChange).toEqual(props.cohorts.handleChange);
|
||||
expect(options.length).toEqual(4);
|
||||
const testEntry = props.cohorts.entries[0];
|
||||
const optionProps = options[1].props;
|
||||
expect(optionProps.value).toEqual(testEntry.value);
|
||||
expect(optionProps.children).toEqual(testEntry.name);
|
||||
|
||||
it('includes trackAll option in tracks SelectGroup', () => {
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
const tracksCall = SelectGroup.mock.calls[0][0];
|
||||
const { options } = tracksCall;
|
||||
|
||||
expect(options).toHaveLength(3);
|
||||
expect(options[0].props.value).toBeDefined();
|
||||
expect(options[0].props.children).toBeDefined();
|
||||
});
|
||||
|
||||
it('includes track entries in tracks SelectGroup options', () => {
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
const tracksCall = SelectGroup.mock.calls[0][0];
|
||||
const { options } = tracksCall;
|
||||
|
||||
expect(options[1].props.value).toBe('track1');
|
||||
expect(options[1].props.children).toBe('Track 1');
|
||||
expect(options[2].props.value).toBe('track2');
|
||||
expect(options[2].props.children).toBe('Track 2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('cohorts SelectGroup', () => {
|
||||
it('renders cohorts SelectGroup with correct props', () => {
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
const cohortsCall = SelectGroup.mock.calls[1][0];
|
||||
expect(cohortsCall.id).toBe('Cohorts');
|
||||
expect(cohortsCall.value).toBe(mockCohortsData.value);
|
||||
expect(cohortsCall.onChange).toBe(mockCohortsData.handleChange);
|
||||
expect(cohortsCall.disabled).toBe(mockCohortsData.isDisabled);
|
||||
});
|
||||
|
||||
it('includes cohortAll option in cohorts SelectGroup', () => {
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
const cohortsCall = SelectGroup.mock.calls[1][0];
|
||||
const { options } = cohortsCall;
|
||||
|
||||
expect(options).toHaveLength(3);
|
||||
expect(options[0].props.value).toBeDefined();
|
||||
expect(options[0].props.children).toBeDefined();
|
||||
});
|
||||
|
||||
it('includes cohort entries in cohorts SelectGroup options', () => {
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
const cohortsCall = SelectGroup.mock.calls[1][0];
|
||||
const { options } = cohortsCall;
|
||||
|
||||
expect(options[1].props.value).toBe('cohort1');
|
||||
expect(options[1].props.children).toBe('Cohort 1');
|
||||
expect(options[2].props.value).toBe('cohort2');
|
||||
expect(options[2].props.children).toBe('Cohort 2');
|
||||
});
|
||||
|
||||
it('passes disabled state to cohorts SelectGroup', () => {
|
||||
useStudentGroupsFilterData.mockReturnValue({
|
||||
tracks: mockTracksData,
|
||||
cohorts: { ...mockCohortsData, isDisabled: true },
|
||||
});
|
||||
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
const cohortsCall = SelectGroup.mock.calls[1][0];
|
||||
expect(cohortsCall.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with empty entries', () => {
|
||||
it('handles empty tracks entries', () => {
|
||||
useStudentGroupsFilterData.mockReturnValue({
|
||||
tracks: { ...mockTracksData, entries: [] },
|
||||
cohorts: mockCohortsData,
|
||||
});
|
||||
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
const tracksCall = SelectGroup.mock.calls[0][0];
|
||||
expect(tracksCall.options).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('handles empty cohorts entries', () => {
|
||||
useStudentGroupsFilterData.mockReturnValue({
|
||||
tracks: mockTracksData,
|
||||
cohorts: { ...mockCohortsData, entries: [] },
|
||||
});
|
||||
|
||||
render(<StudentGroupsFilter updateQueryParams={mockUpdateQueryParams} />);
|
||||
|
||||
const cohortsCall = SelectGroup.mock.calls[1][0];
|
||||
expect(cohortsCall.options).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`GradebookHeader component render default view shapshot 1`] = `
|
||||
<div
|
||||
className="gradebook-header"
|
||||
>
|
||||
<a
|
||||
className="mb-3"
|
||||
href="test-dashboard-url"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
<<
|
||||
</span>
|
||||
Back to Dashboard
|
||||
</a>
|
||||
<h1>
|
||||
Gradebook
|
||||
</h1>
|
||||
<div
|
||||
className="subtitle-row d-flex justify-content-between align-items-center"
|
||||
>
|
||||
<h2
|
||||
className="text-break"
|
||||
>
|
||||
test-course-id
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`GradebookHeader component render frozen grades snapshot: show frozen warning 1`] = `
|
||||
<div
|
||||
className="gradebook-header"
|
||||
>
|
||||
<a
|
||||
className="mb-3"
|
||||
href="test-dashboard-url"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
<<
|
||||
</span>
|
||||
Back to Dashboard
|
||||
</a>
|
||||
<h1>
|
||||
Gradebook
|
||||
</h1>
|
||||
<div
|
||||
className="subtitle-row d-flex justify-content-between align-items-center"
|
||||
>
|
||||
<h2
|
||||
className="text-break"
|
||||
>
|
||||
test-course-id
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
className="alert alert-warning"
|
||||
role="alert"
|
||||
>
|
||||
The grades for this course are now frozen. Editing of grades is no longer allowed.
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`GradebookHeader component render show bulk management snapshot: show toggle view message button with handleToggleViewClick method 1`] = `
|
||||
<div
|
||||
className="gradebook-header"
|
||||
>
|
||||
<a
|
||||
className="mb-3"
|
||||
href="test-dashboard-url"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
<<
|
||||
</span>
|
||||
Back to Dashboard
|
||||
</a>
|
||||
<h1>
|
||||
Gradebook
|
||||
</h1>
|
||||
<div
|
||||
className="subtitle-row d-flex justify-content-between align-items-center"
|
||||
>
|
||||
<h2
|
||||
className="text-break"
|
||||
>
|
||||
test-course-id
|
||||
</h2>
|
||||
<Button
|
||||
onClick={[MockFunction hooks.handleToggleViewClick]}
|
||||
variant="tertiary"
|
||||
>
|
||||
toggle-view-message
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`GradebookHeader component render user cannot view gradebook snapshot: show unauthorized warning 1`] = `
|
||||
<div
|
||||
className="gradebook-header"
|
||||
>
|
||||
<a
|
||||
className="mb-3"
|
||||
href="test-dashboard-url"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
<<
|
||||
</span>
|
||||
Back to Dashboard
|
||||
</a>
|
||||
<h1>
|
||||
Gradebook
|
||||
</h1>
|
||||
<div
|
||||
className="subtitle-row d-flex justify-content-between align-items-center"
|
||||
>
|
||||
<h2
|
||||
className="text-break"
|
||||
>
|
||||
test-course-id
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
className="alert alert-warning"
|
||||
role="alert"
|
||||
>
|
||||
You are not authorized to view the gradebook for this course.
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,77 +1,303 @@
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Button } from '@openedx/paragon';
|
||||
import { render, screen, initializeMocks } from 'testUtilsExtra';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { formatMessage } from 'testUtils';
|
||||
import { instructorDashboardUrl } from 'data/services/lms/urls';
|
||||
|
||||
import { GradebookHeader } from './index';
|
||||
import useGradebookHeaderData from './hooks';
|
||||
import GradebookHeader from '.';
|
||||
import messages from './messages';
|
||||
|
||||
jest.mock('./hooks', () => ({ __esModule: true, default: jest.fn() }));
|
||||
jest.unmock('@openedx/paragon');
|
||||
jest.unmock('react');
|
||||
jest.unmock('@edx/frontend-platform/i18n');
|
||||
jest.mock('data/services/lms/urls', () => ({
|
||||
instructorDashboardUrl: jest.fn(),
|
||||
}));
|
||||
jest.mock('./hooks', () => jest.fn());
|
||||
|
||||
instructorDashboardUrl.mockReturnValue('test-dashboard-url');
|
||||
initializeMocks();
|
||||
|
||||
const hookProps = {
|
||||
areGradesFrozen: false,
|
||||
canUserViewGradebook: true,
|
||||
courseId: 'test-course-id',
|
||||
handleToggleViewClick: jest.fn().mockName('hooks.handleToggleViewClick'),
|
||||
showBulkManagement: false,
|
||||
toggleViewMessage: { defaultMessage: 'toggle-view-message' },
|
||||
};
|
||||
useGradebookHeaderData.mockReturnValue(hookProps);
|
||||
describe('GradebookHeader', () => {
|
||||
const mockHandleToggleViewClick = jest.fn();
|
||||
|
||||
let el;
|
||||
describe('GradebookHeader component', () => {
|
||||
beforeAll(() => {
|
||||
el = shallow(<GradebookHeader />);
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
instructorDashboardUrl.mockReturnValue('https://example.com/dashboard');
|
||||
});
|
||||
describe('behavior', () => {
|
||||
it('initializes hooks', () => {
|
||||
expect(useGradebookHeaderData).toHaveBeenCalledWith();
|
||||
expect(useIntl).toHaveBeenCalledWith();
|
||||
|
||||
describe('basic rendering', () => {
|
||||
beforeEach(() => {
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: false,
|
||||
canUserViewGradebook: true,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: false,
|
||||
toggleViewMessage: messages.toActivityLog,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the main header container', () => {
|
||||
render(<GradebookHeader />);
|
||||
const header = screen.getByText('Gradebook').closest('.gradebook-header');
|
||||
expect(header).toHaveClass('gradebook-header');
|
||||
});
|
||||
|
||||
it('renders back to dashboard link', () => {
|
||||
render(<GradebookHeader />);
|
||||
const dashboardLink = screen.getByRole('link');
|
||||
expect(dashboardLink).toHaveAttribute(
|
||||
'href',
|
||||
'https://example.com/dashboard',
|
||||
);
|
||||
expect(dashboardLink).toHaveClass('mb-3');
|
||||
expect(dashboardLink).toHaveTextContent('Back to Dashboard');
|
||||
});
|
||||
|
||||
it('renders gradebook title', () => {
|
||||
render(<GradebookHeader />);
|
||||
const title = screen.getByRole('heading', { level: 1 });
|
||||
expect(title).toHaveTextContent('Gradebook');
|
||||
});
|
||||
|
||||
it('renders course ID subtitle', () => {
|
||||
render(<GradebookHeader />);
|
||||
const subtitle = screen.getByRole('heading', { level: 2 });
|
||||
expect(subtitle).toHaveTextContent('course-v1:TestU+CS101+2024');
|
||||
expect(subtitle).toHaveClass('text-break');
|
||||
});
|
||||
|
||||
it('renders subtitle row with correct classes', () => {
|
||||
render(<GradebookHeader />);
|
||||
const subtitleRow = screen.getByRole('heading', {
|
||||
level: 2,
|
||||
}).parentElement;
|
||||
expect(subtitleRow).toHaveClass(
|
||||
'subtitle-row',
|
||||
'd-flex',
|
||||
'justify-content-between',
|
||||
'align-items-center',
|
||||
);
|
||||
});
|
||||
|
||||
it('calls instructorDashboardUrl to get dashboard URL', () => {
|
||||
render(<GradebookHeader />);
|
||||
expect(instructorDashboardUrl).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls useGradebookHeaderData hook', () => {
|
||||
render(<GradebookHeader />);
|
||||
expect(useGradebookHeaderData).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('render', () => {
|
||||
describe('default view', () => {
|
||||
test('shapshot', () => {
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
|
||||
describe('bulk management toggle button', () => {
|
||||
describe('when showBulkManagement is true', () => {
|
||||
beforeEach(() => {
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: false,
|
||||
canUserViewGradebook: true,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: true,
|
||||
toggleViewMessage: messages.toActivityLog,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders toggle view button', () => {
|
||||
render(<GradebookHeader />);
|
||||
expect(screen.getByRole('button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays correct button text from toggleViewMessage', () => {
|
||||
render(<GradebookHeader />);
|
||||
const toggleButton = screen.getByRole('button');
|
||||
expect(toggleButton).toHaveTextContent('View Bulk Management History');
|
||||
});
|
||||
|
||||
it('calls handleToggleViewClick when button is clicked', async () => {
|
||||
render(<GradebookHeader />);
|
||||
const user = userEvent.setup();
|
||||
const toggleButton = screen.getByRole('button');
|
||||
|
||||
await user.click(toggleButton);
|
||||
expect(mockHandleToggleViewClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('displays correct message from toggleViewMessage', () => {
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: false,
|
||||
canUserViewGradebook: true,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: true,
|
||||
toggleViewMessage: messages.toGradesView,
|
||||
});
|
||||
|
||||
render(<GradebookHeader />);
|
||||
const toggleButton = screen.getByRole('button');
|
||||
expect(toggleButton).toHaveTextContent('Return to Gradebook');
|
||||
});
|
||||
});
|
||||
describe('show bulk management', () => {
|
||||
|
||||
describe('when showBulkManagement is false', () => {
|
||||
beforeEach(() => {
|
||||
useGradebookHeaderData.mockReturnValueOnce({ ...hookProps, showBulkManagement: true });
|
||||
el = shallow(<GradebookHeader />);
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: false,
|
||||
canUserViewGradebook: true,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: false,
|
||||
toggleViewMessage: messages.toActivityLog,
|
||||
});
|
||||
});
|
||||
test('snapshot: show toggle view message button with handleToggleViewClick method', () => {
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
const { onClick } = el.instance.findByType(Button)[0].props;
|
||||
expect(onClick).toEqual(hookProps.handleToggleViewClick);
|
||||
expect(el.instance.findByType(Button)[0].children[0].el).toEqual(formatMessage(hookProps.toggleViewMessage));
|
||||
|
||||
it('does not render toggle view button', () => {
|
||||
render(<GradebookHeader />);
|
||||
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
describe('frozen grades', () => {
|
||||
});
|
||||
|
||||
describe('frozen grades warning', () => {
|
||||
describe('when areGradesFrozen is true', () => {
|
||||
beforeEach(() => {
|
||||
useGradebookHeaderData.mockReturnValueOnce({ ...hookProps, areGradesFrozen: true });
|
||||
el = shallow(<GradebookHeader />);
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: true,
|
||||
canUserViewGradebook: true,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: false,
|
||||
toggleViewMessage: messages.toActivityLog,
|
||||
});
|
||||
});
|
||||
test('snapshot: show frozen warning', () => {
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
|
||||
it('renders frozen warning alert', () => {
|
||||
render(<GradebookHeader />);
|
||||
const alert = screen.getByRole('alert');
|
||||
expect(alert).toHaveClass('alert', 'alert-warning');
|
||||
expect(alert).toHaveTextContent(
|
||||
'The grades for this course are now frozen. Editing of grades is no longer allowed.',
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('user cannot view gradebook', () => {
|
||||
|
||||
describe('when areGradesFrozen is false', () => {
|
||||
beforeEach(() => {
|
||||
useGradebookHeaderData.mockReturnValueOnce({ ...hookProps, canUserViewGradebook: false });
|
||||
el = shallow(<GradebookHeader />);
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: false,
|
||||
canUserViewGradebook: true,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: false,
|
||||
toggleViewMessage: messages.toActivityLog,
|
||||
});
|
||||
});
|
||||
test('snapshot: show unauthorized warning', () => {
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
|
||||
it('does not render frozen warning alert', () => {
|
||||
render(<GradebookHeader />);
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'The grades for this course are now frozen. Editing of grades is no longer allowed.',
|
||||
),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('unauthorized warning', () => {
|
||||
describe('when canUserViewGradebook is false', () => {
|
||||
beforeEach(() => {
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: false,
|
||||
canUserViewGradebook: false,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: false,
|
||||
toggleViewMessage: messages.toActivityLog,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders unauthorized warning alert', () => {
|
||||
render(<GradebookHeader />);
|
||||
const alert = screen.getByRole('alert');
|
||||
expect(alert).toHaveClass('alert', 'alert-warning');
|
||||
expect(alert).toHaveTextContent(
|
||||
'You are not authorized to view the gradebook for this course.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when canUserViewGradebook is true', () => {
|
||||
beforeEach(() => {
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: false,
|
||||
canUserViewGradebook: true,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: false,
|
||||
toggleViewMessage: messages.toActivityLog,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render unauthorized warning alert', () => {
|
||||
render(<GradebookHeader />);
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'You are not authorized to view the gradebook for this course.',
|
||||
),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiple warnings', () => {
|
||||
it('renders both frozen and unauthorized warnings when both conditions are true', () => {
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: true,
|
||||
canUserViewGradebook: false,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: false,
|
||||
toggleViewMessage: messages.toActivityLog,
|
||||
});
|
||||
|
||||
render(<GradebookHeader />);
|
||||
const alerts = screen.getAllByRole('alert');
|
||||
expect(alerts).toHaveLength(2);
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
'The grades for this course are now frozen. Editing of grades is no longer allowed.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
'You are not authorized to view the gradebook for this course.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('complete integration', () => {
|
||||
it('renders all elements when showBulkManagement is true', () => {
|
||||
useGradebookHeaderData.mockReturnValue({
|
||||
areGradesFrozen: false,
|
||||
canUserViewGradebook: true,
|
||||
courseId: 'course-v1:TestU+CS101+2024',
|
||||
handleToggleViewClick: mockHandleToggleViewClick,
|
||||
showBulkManagement: true,
|
||||
toggleViewMessage: messages.toActivityLog,
|
||||
});
|
||||
|
||||
render(<GradebookHeader />);
|
||||
|
||||
expect(screen.getByRole('link')).toBeInTheDocument();
|
||||
expect(screen.getByRole('heading', { level: 1 })).toBeInTheDocument();
|
||||
expect(screen.getByRole('heading', { level: 2 })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button')).toBeInTheDocument();
|
||||
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`BulkManagementControls render snapshot - show - network and import buttons 1`] = `
|
||||
<div
|
||||
className="d-flex"
|
||||
>
|
||||
<NetworkButton
|
||||
label={
|
||||
{
|
||||
"defaultMessage": "Download Grades",
|
||||
"description": "A labeled button that allows an admin user to download course grades all at once (in bulk).",
|
||||
"id": "gradebook.GradesView.BulkManagementControls.bulkManagementLabel",
|
||||
}
|
||||
}
|
||||
onClick={[MockFunction]}
|
||||
/>
|
||||
<ImportGradesButton />
|
||||
</div>
|
||||
`;
|
||||
@@ -1,32 +1,163 @@
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { render, screen, initializeMocks } from 'testUtilsExtra';
|
||||
|
||||
import NetworkButton from 'components/NetworkButton';
|
||||
import ImportGradesButton from '../ImportGradesButton';
|
||||
|
||||
import { BulkManagementControls } from './index';
|
||||
import useBulkManagementControlsData from './hooks';
|
||||
import BulkManagementControls from '.';
|
||||
|
||||
jest.mock('../ImportGradesButton', () => 'ImportGradesButton');
|
||||
jest.mock('components/NetworkButton', () => 'NetworkButton');
|
||||
import messages from './messages';
|
||||
|
||||
jest.unmock('@openedx/paragon');
|
||||
jest.unmock('react');
|
||||
jest.unmock('@edx/frontend-platform/i18n');
|
||||
jest.mock('components/NetworkButton', () => jest.fn(() => <div data-testid="network-button">NetworkButton</div>));
|
||||
jest.mock('../ImportGradesButton', () => jest.fn(() => (
|
||||
<div data-testid="import-grades-button">ImportGradesButton</div>
|
||||
)));
|
||||
jest.mock('./hooks', () => jest.fn());
|
||||
|
||||
const hookProps = {
|
||||
show: true,
|
||||
handleClickExportGrades: jest.fn(),
|
||||
};
|
||||
useBulkManagementControlsData.mockReturnValue(hookProps);
|
||||
initializeMocks();
|
||||
|
||||
describe('BulkManagementControls', () => {
|
||||
describe('behavior', () => {
|
||||
shallow(<BulkManagementControls />);
|
||||
expect(useBulkManagementControlsData).toHaveBeenCalledWith();
|
||||
const mockHandleClickExportGrades = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
describe('render', () => {
|
||||
test('snapshot - show - network and import buttons', () => {
|
||||
expect(shallow(<BulkManagementControls />).snapshot).toMatchSnapshot();
|
||||
|
||||
describe('when show is false', () => {
|
||||
beforeEach(() => {
|
||||
useBulkManagementControlsData.mockReturnValue({
|
||||
show: false,
|
||||
handleClickExportGrades: mockHandleClickExportGrades,
|
||||
});
|
||||
});
|
||||
test('snapshot - empty if show is not truthy', () => {
|
||||
useBulkManagementControlsData.mockReturnValueOnce({ ...hookProps, show: false });
|
||||
expect(shallow(<BulkManagementControls />).isEmptyRender()).toEqual(true);
|
||||
|
||||
it('renders nothing when show is false', () => {
|
||||
render(<BulkManagementControls />);
|
||||
expect(screen.queryByTestId('network-button')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId('import-grades-button'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render NetworkButton when show is false', () => {
|
||||
render(<BulkManagementControls />);
|
||||
expect(NetworkButton).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not render ImportGradesButton when show is false', () => {
|
||||
render(<BulkManagementControls />);
|
||||
expect(ImportGradesButton).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when show is true', () => {
|
||||
beforeEach(() => {
|
||||
useBulkManagementControlsData.mockReturnValue({
|
||||
show: true,
|
||||
handleClickExportGrades: mockHandleClickExportGrades,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the container div with correct class when show is true', () => {
|
||||
render(<BulkManagementControls />);
|
||||
const containerDiv = screen.getByTestId('network-button').parentElement;
|
||||
expect(containerDiv).toHaveClass('d-flex');
|
||||
});
|
||||
|
||||
it('renders NetworkButton with correct props', () => {
|
||||
render(<BulkManagementControls />);
|
||||
|
||||
expect(NetworkButton).toHaveBeenCalledWith(
|
||||
{
|
||||
label: messages.downloadGradesBtn,
|
||||
onClick: mockHandleClickExportGrades,
|
||||
},
|
||||
{},
|
||||
);
|
||||
expect(screen.getByTestId('network-button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders ImportGradesButton', () => {
|
||||
render(<BulkManagementControls />);
|
||||
|
||||
expect(ImportGradesButton).toHaveBeenCalledWith({}, {});
|
||||
expect(screen.getByTestId('import-grades-button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls handleClickExportGrades when NetworkButton is clicked', () => {
|
||||
render(<BulkManagementControls />);
|
||||
|
||||
const networkButtonCall = NetworkButton.mock.calls[0][0];
|
||||
const { onClick } = networkButtonCall;
|
||||
|
||||
onClick();
|
||||
expect(mockHandleClickExportGrades).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('passes correct label to NetworkButton', () => {
|
||||
render(<BulkManagementControls />);
|
||||
|
||||
const networkButtonCall = NetworkButton.mock.calls[0][0];
|
||||
expect(networkButtonCall.label).toBe(messages.downloadGradesBtn);
|
||||
});
|
||||
|
||||
it('renders both buttons in the correct order', () => {
|
||||
render(<BulkManagementControls />);
|
||||
|
||||
expect(NetworkButton).toHaveBeenCalled();
|
||||
expect(ImportGradesButton).toHaveBeenCalled();
|
||||
|
||||
const networkButton = screen.getByTestId('network-button');
|
||||
const importButton = screen.getByTestId('import-grades-button');
|
||||
|
||||
expect(networkButton).toBeInTheDocument();
|
||||
expect(importButton).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('hook integration', () => {
|
||||
it('calls useBulkManagementControlsData hook', () => {
|
||||
useBulkManagementControlsData.mockReturnValue({
|
||||
show: true,
|
||||
handleClickExportGrades: mockHandleClickExportGrades,
|
||||
});
|
||||
|
||||
render(<BulkManagementControls />);
|
||||
expect(useBulkManagementControlsData).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('uses the show value from hook to determine rendering', () => {
|
||||
useBulkManagementControlsData.mockReturnValue({
|
||||
show: false,
|
||||
handleClickExportGrades: mockHandleClickExportGrades,
|
||||
});
|
||||
|
||||
render(<BulkManagementControls />);
|
||||
expect(screen.queryByTestId('network-button')).not.toBeInTheDocument();
|
||||
|
||||
useBulkManagementControlsData.mockReturnValue({
|
||||
show: true,
|
||||
handleClickExportGrades: mockHandleClickExportGrades,
|
||||
});
|
||||
|
||||
render(<BulkManagementControls />);
|
||||
expect(screen.getByTestId('network-button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('passes handleClickExportGrades from hook to NetworkButton', () => {
|
||||
const customHandler = jest.fn();
|
||||
useBulkManagementControlsData.mockReturnValue({
|
||||
show: true,
|
||||
handleClickExportGrades: customHandler,
|
||||
});
|
||||
|
||||
render(<BulkManagementControls />);
|
||||
|
||||
const networkButtonCall = NetworkButton.mock.calls[0][0];
|
||||
expect(networkButtonCall.onClick).toBe(customHandler);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,104 @@
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { render, screen, initializeMocks } from 'testUtilsExtra';
|
||||
|
||||
import HistoryHeader from './HistoryHeader';
|
||||
|
||||
jest.unmock('@openedx/paragon');
|
||||
jest.unmock('react');
|
||||
jest.unmock('@edx/frontend-platform/i18n');
|
||||
|
||||
initializeMocks();
|
||||
|
||||
describe('HistoryHeader', () => {
|
||||
const props = {
|
||||
id: 'water',
|
||||
label: 'Brita',
|
||||
value: 'hydration',
|
||||
const defaultProps = {
|
||||
id: 'test-id',
|
||||
label: 'Test Label',
|
||||
value: 'Test Value',
|
||||
};
|
||||
describe('Component', () => {
|
||||
test('snapshot', () => {
|
||||
expect(shallow(<HistoryHeader {...props} />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders header with label and value', () => {
|
||||
render(<HistoryHeader {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Test Label:')).toBeInTheDocument();
|
||||
expect(screen.getByText('Test Value')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders header element with correct classes', () => {
|
||||
render(<HistoryHeader {...defaultProps} />);
|
||||
|
||||
const headerElement = screen.getByText('Test Label:');
|
||||
expect(headerElement).toHaveClass('grade-history-header');
|
||||
expect(headerElement).toHaveClass('grade-history-test-id');
|
||||
});
|
||||
|
||||
it('renders with string value', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
value: 'String Value',
|
||||
};
|
||||
|
||||
render(<HistoryHeader {...props} />);
|
||||
expect(screen.getByText('String Value')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with number value', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
value: 85,
|
||||
};
|
||||
|
||||
render(<HistoryHeader {...props} />);
|
||||
expect(screen.getByText('85')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with null value (default prop)', () => {
|
||||
const props = {
|
||||
id: 'test-id',
|
||||
label: 'Test Label',
|
||||
};
|
||||
|
||||
render(<HistoryHeader {...props} />);
|
||||
expect(screen.getByText('Test Label:')).toBeInTheDocument();
|
||||
|
||||
const valueDiv = screen.getByText('Test Label:').nextSibling;
|
||||
expect(valueDiv).toBeInTheDocument();
|
||||
expect(valueDiv).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('renders with React node as label', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
label: <strong>Bold Label</strong>,
|
||||
};
|
||||
|
||||
render(<HistoryHeader {...props} />);
|
||||
const strongElement = screen.getByText('Bold Label');
|
||||
expect(strongElement.tagName).toBe('STRONG');
|
||||
});
|
||||
|
||||
it('generates correct class name based on id', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
id: 'assignment-name',
|
||||
};
|
||||
|
||||
render(<HistoryHeader {...props} />);
|
||||
const headerElement = screen.getByText('Test Label:');
|
||||
expect(headerElement).toHaveClass('grade-history-assignment-name');
|
||||
});
|
||||
|
||||
it('renders container structure correctly', () => {
|
||||
render(<HistoryHeader {...defaultProps} />);
|
||||
|
||||
const headerElement = screen.getByText('Test Label:');
|
||||
const valueElement = screen.getByText('Test Value');
|
||||
|
||||
expect(headerElement).toBeInTheDocument();
|
||||
expect(valueElement).toBeInTheDocument();
|
||||
|
||||
expect(headerElement).toHaveClass(
|
||||
'grade-history-header',
|
||||
'grade-history-test-id',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`HistoryHeader Component snapshot 1`] = `
|
||||
<div>
|
||||
<div
|
||||
className="grade-history-header grade-history-water"
|
||||
>
|
||||
Brita
|
||||
:
|
||||
</div>
|
||||
<div>
|
||||
hydration
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
Reference in New Issue
Block a user