Compare commits

...

3 Commits

Author SHA1 Message Date
sundasnoreen12
1005541e8d refactor: added url changes 2023-03-13 11:51:43 +05:00
sundasnoreen12
562db39c24 refactor: fixed changes for code optimization 2023-03-13 11:51:33 +05:00
sundasnoreen12
d6a38ae6d7 test: added test cases for learner view 2023-03-13 11:51:33 +05:00
5 changed files with 206 additions and 49 deletions

View File

@@ -72,6 +72,7 @@ function Search({ intl }) {
<Icon
src={SearchIcon}
onClick={() => onSubmit(searchValue)}
data-testid="search-icon"
/>
</span>
</SearchField.Advanced>

View File

@@ -8,14 +8,13 @@ ensureConfig([
], 'Posts API service');
export const getCourseConfigApiUrl = () => `${getConfig().LMS_BASE_URL}/api/discussion/v1/courses/`;
export const getDiscussionsConfigUrl = (courseId) => `${getCourseConfigApiUrl()}${courseId}/`;
/**
* Get discussions course config
* @param {string} courseId
*/
export async function getDiscussionsConfig(courseId) {
const url = `${getCourseConfigApiUrl()}${courseId}/`;
const { data } = await getAuthenticatedHttpClient().get(url);
const { data } = await getAuthenticatedHttpClient().get(getDiscussionsConfigUrl(courseId));
return data;
}
@@ -24,7 +23,7 @@ export async function getDiscussionsConfig(courseId) {
* @param {string} courseId
*/
export async function getDiscussionsSettings(courseId) {
const url = `${getCourseConfigApiUrl()}${courseId}/settings`;
const url = `${getDiscussionsConfigUrl(courseId)}settings`;
const { data } = await getAuthenticatedHttpClient().get(url);
return data;
}

View File

@@ -1,6 +1,8 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import {
fireEvent, render, screen, waitFor, within,
} from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import { act } from 'react-dom/test-utils';
import { IntlProvider } from 'react-intl';
@@ -11,11 +13,13 @@ import { initializeMockApp } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppProvider } from '@edx/frontend-platform/react';
import { PostActionsBar } from '../../components';
import { initializeStore } from '../../store';
import { executeThunk } from '../../test-utils';
import { getCourseConfigApiUrl } from '../data/api';
import { DiscussionContext } from '../common/context';
import { getDiscussionsConfigUrl } from '../data/api';
import { fetchCourseConfig } from '../data/thunks';
import { getCoursesApiUrl, getUserProfileApiUrl } from './data/api';
import { getUserProfileApiUrl, learnersApiUrl } from './data/api';
import { fetchLearners } from './data/thunks';
import LearnersView from './LearnersView';
@@ -23,27 +27,31 @@ import './data/__factories__';
let store;
let axiosMock;
const coursesApiUrl = getCoursesApiUrl();
const courseConfigApiUrl = getCourseConfigApiUrl();
const userProfileApiUrl = getUserProfileApiUrl();
const courseId = 'course-v1:edX+TestX+Test_Course';
let container;
function renderComponent() {
return render(
const wrapper = render(
<IntlProvider locale="en">
<AppProvider store={store}>
<MemoryRouter initialEntries={[`/${courseId}/`]}>
<Route path="/:courseId/">
<LearnersView />
</Route>
</MemoryRouter>
<DiscussionContext.Provider value={{
page: 'learners',
}}
>
<MemoryRouter initialEntries={[`/${courseId}/`]}>
<Route path="/:courseId/">
<PostActionsBar />
<LearnersView />
</Route>
</MemoryRouter>
</DiscussionContext.Provider>
</AppProvider>
</IntlProvider>,
);
container = wrapper.container;
}
describe('LearnersView', () => {
const learnerCount = 3;
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
@@ -53,41 +61,190 @@ describe('LearnersView', () => {
roles: [],
},
});
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
store = initializeStore();
Factory.resetAll();
const learnersData = Factory.build('learnersResult', {}, {
count: learnerCount,
pageSize: 6,
});
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.onGet(`${coursesApiUrl}${courseId}/activity_stats/`)
.reply(() => [200, learnersData]);
const learnersProfile = Factory.build('learnersProfile', {}, {
username: ['learner-1', 'learner-2', 'learner-3'],
});
axiosMock.onGet(`${userProfileApiUrl}?username=learner-1,learner-2,learner-3`)
.reply(() => [200, learnersProfile.profiles]);
await executeThunk(fetchLearners(courseId), store.dispatch, store.getState);
});
describe('Basic', () => {
test('Learners tab is disabled by default', async () => {
await act(async () => {
await renderComponent();
});
expect(screen.queryByText(/Last active/i)).toBeFalsy();
async function setUpLearnerMockResponse(
count = 3,
pageSize = 6,
page = 1,
username = ['learner-1', 'learner-2', 'learner-3'],
searchText,
) {
Factory.resetAll();
const learnersData = Factory.build('learnersResult', {}, {
count,
pageSize,
page,
});
test('Learners tab is enabled', async () => {
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`).reply(200, {
learners_tab_enabled: true,
user_is_privileged: true,
});
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/settings`).reply(200, {});
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
axiosMock.onGet(learnersApiUrl(courseId))
.reply(() => [200, learnersData]);
axiosMock.onGet(`${getUserProfileApiUrl()}?username=${username.join()}`)
.reply(() => [200, Factory.build('learnersProfile', {}, {
username,
}).profiles]);
await executeThunk(fetchLearners(courseId, { usernameSearch: searchText }), store.dispatch, store.getState);
}
async function assignPrivilages() {
axiosMock.onGet(getDiscussionsConfigUrl(courseId)).reply(200, {
learners_tab_enabled: true,
user_is_privileged: true,
});
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
}
test('Learners tab is disabled by default', async () => {
await setUpLearnerMockResponse();
await renderComponent();
expect(screen.queryByText('learner-1')).not.toBeInTheDocument();
});
test('Learners tab is enabled', async () => {
await setUpLearnerMockResponse();
await assignPrivilages();
await waitFor(() => {
renderComponent();
});
expect(screen.queryByText('learner-1')).toBeInTheDocument();
});
test('Most activity should be selected by default for the non-moderator role.', async () => {
await setUpLearnerMockResponse();
await renderComponent();
const filterBar = container.querySelector('.collapsible-trigger');
await act(async () => {
fireEvent.click(filterBar);
});
await waitFor(() => {
const mostActivity = screen.getByTestId('activity selected');
expect(mostActivity).toBeInTheDocument();
});
});
it.each([
{ searchBy: 'sort-recency', result: 0 },
{ searchBy: 'sort-activity', result: 3 },
])('successfully display learners by %s.', async ({ searchBy, result }) => {
await setUpLearnerMockResponse();
await assignPrivilages();
await renderComponent();
const filterBar = container.querySelector('.collapsible-trigger');
await act(async () => {
fireEvent.click(filterBar);
});
await waitFor(async () => {
const activity = container.querySelector(`#${searchBy}`);
await act(async () => {
await renderComponent();
fireEvent.click(activity);
});
await waitFor(() => {
const learners = container.querySelectorAll('.discussion-post');
expect(learners).toHaveLength(result);
});
});
});
it('should display a learner\'s list.', async () => {
await setUpLearnerMockResponse();
await assignPrivilages();
await waitFor(() => {
renderComponent();
});
const learners = await container.querySelectorAll('.discussion-post');
const firstLearner = learners.item(0);
const learnerAvatar = firstLearner.querySelector('[alt=learner-1]');
const learnerTitle = within(firstLearner).queryByText('learner-1');
const stats = firstLearner.querySelectorAll('.icon-size');
expect(learners).toHaveLength(3);
expect(learnerAvatar).toBeInTheDocument();
expect(learnerTitle).toBeInTheDocument();
expect(stats).toHaveLength(2);
});
it.each([
{
searchText: 'hello world',
output: 'Showing 0 results for',
learnersCount: 0,
username: [],
},
{
searchText: 'learner',
output: 'Showing 2 results for',
learnersCount: 2,
username:
['learner-1', 'learner-2'],
},
])('should have a search bar with a clear button and \'$output\' results found text.',
async ({
searchText, output, learnersCount, username,
}) => {
await setUpLearnerMockResponse();
await assignPrivilages();
await renderComponent();
const searchField = within(container).getByPlaceholderText('Search learners');
const searchButton = within(container).getByTestId('search-icon');
await fireEvent.change(searchField, { target: { value: searchText } });
await act(async () => {
fireEvent.click(searchButton);
setUpLearnerMockResponse(learnersCount, learnersCount, 1, username, searchText);
});
await waitFor(() => {
const clearButton = within(container).queryByText('Clear results');
const searchMessage = within(container).queryByText(`${output} "${searchText}"`);
const leaners = container.querySelectorAll('.discussion-post') ?? [];
expect(searchMessage).toBeInTheDocument();
expect(clearButton).toBeInTheDocument();
expect(leaners).toHaveLength(learnersCount);
});
});
test('When click on the clear button it should move to a list of all learners.', async () => {
await setUpLearnerMockResponse();
await assignPrivilages();
await renderComponent();
const searchField = within(container).getByPlaceholderText('Search learners');
const searchButton = within(container).getByTestId('search-icon');
let clearButton;
await fireEvent.change(searchField, { target: { value: 'learner' } });
await act(async () => {
fireEvent.click(searchButton);
setUpLearnerMockResponse(2, 2, 1, ['learner-1', 'learner-2'], 'learner');
});
await waitFor(() => {
clearButton = within(container).queryByText('Clear results');
});
await act(async () => fireEvent.click(clearButton));
await waitFor(() => {
setUpLearnerMockResponse();
});
const learners = container.querySelectorAll('.discussion-post');
expect(learners).toHaveLength(3);
});
});

View File

@@ -13,9 +13,9 @@ Factory.define('learner')
});
Factory.define('learnersResult')
.option('count', null, 3)
.option('page', null, 1)
.option('pageSize', null, 5)
.option('count', null)
.option('page', null)
.option('pageSize', null)
.option('courseId', null, 'course-v1:Test+TestX+Test_Course')
.option('activeFlags', null, 0)
.attr(

View File

@@ -24,7 +24,7 @@ const ActionItem = ({
<label
htmlFor={id}
className="focus border-bottom-0 d-flex align-items-center w-100 py-2 m-0 font-weight-500 filter-menu"
data-testid={value === selected ? 'selected' : null}
data-testid={value === selected ? `${value} selected` : null}
style={{ cursor: 'pointer' }}
aria-checked={value === selected}
>