-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: create library (v2) form (#1116)
- Loading branch information
Showing
27 changed files
with
570 additions
and
185 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import React from 'react'; | ||
import { render, screen } from '@testing-library/react'; | ||
import { IntlProvider } from '@edx/frontend-platform/i18n'; | ||
|
||
import AlertError from '.'; | ||
|
||
const RootWrapper = ({ error }: { error: unknown }) => ( | ||
<IntlProvider locale="en"> | ||
<AlertError error={error} /> | ||
</IntlProvider> | ||
); | ||
|
||
describe('<AlertMessage />', () => { | ||
test('render using a string', () => { | ||
const error = 'This is a string error message'; | ||
const { getByText } = render(<RootWrapper error={error} />); | ||
expect(getByText('This is a string error message')).toBeInTheDocument(); | ||
}); | ||
|
||
test('render using an error', () => { | ||
const error = new Error('This is an error message'); | ||
const { getByText } = render(<RootWrapper error={error} />); | ||
expect(getByText('This is an error message')).toBeInTheDocument(); | ||
}); | ||
|
||
test('render using an error with response', () => { | ||
const error = { | ||
message: 'This is an error message', | ||
response: { | ||
data: { | ||
message: 'This is a response body', | ||
}, | ||
}, | ||
}; | ||
const { getByText } = render(<RootWrapper error={error} />); | ||
screen.logTestingPlaygroundURL(); | ||
expect(getByText(/this is an error message/i)).toBeInTheDocument(); | ||
expect(getByText(/\{"message":"this is a response body"\}/i)).toBeInTheDocument(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react'; | ||
import { | ||
Alert, | ||
} from '@openedx/paragon'; | ||
|
||
const AlertError: React.FC<{ error: unknown }> = ({ error }) => ( | ||
<Alert variant="danger" className="mt-3"> | ||
{error instanceof Object && 'message' in error ? error.message : String(error)} | ||
<br /> | ||
{error instanceof Object && (error as any).response?.data && JSON.stringify((error as any).response?.data)} | ||
</Alert> | ||
); | ||
|
||
export default AlertError; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
131 changes: 131 additions & 0 deletions
131
src/library-authoring/create-library/CreateLibrary.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import React from 'react'; | ||
import MockAdapter from 'axios-mock-adapter'; | ||
import { initializeMockApp } from '@edx/frontend-platform'; | ||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; | ||
import { IntlProvider } from '@edx/frontend-platform/i18n'; | ||
import { AppProvider } from '@edx/frontend-platform/react'; | ||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | ||
import { fireEvent, render, waitFor } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
import initializeStore from '../../store'; | ||
import CreateLibrary from './CreateLibrary'; | ||
import { getContentLibraryV2CreateApiUrl } from './data/api'; | ||
|
||
let store; | ||
const mockNavigate = jest.fn(); | ||
let axiosMock: MockAdapter; | ||
|
||
jest.mock('react-router-dom', () => ({ | ||
...jest.requireActual('react-router-dom'), | ||
useNavigate: () => mockNavigate, | ||
})); | ||
|
||
jest.mock('../../generic/data/apiHooks', () => ({ | ||
...jest.requireActual('../../generic/data/apiHooks'), | ||
useOrganizationListData: () => ({ | ||
data: ['org1', 'org2', 'org3', 'org4', 'org5'], | ||
isLoading: false, | ||
}), | ||
})); | ||
|
||
const queryClient = new QueryClient({ | ||
defaultOptions: { | ||
queries: { | ||
retry: false, | ||
}, | ||
}, | ||
}); | ||
|
||
const RootWrapper = () => ( | ||
<AppProvider store={store}> | ||
<IntlProvider locale="en" messages={{}}> | ||
<QueryClientProvider client={queryClient}> | ||
<CreateLibrary /> | ||
</QueryClientProvider> | ||
</IntlProvider> | ||
</AppProvider> | ||
); | ||
|
||
describe('<CreateLibrary />', () => { | ||
beforeEach(() => { | ||
initializeMockApp({ | ||
authenticatedUser: { | ||
userId: 3, | ||
username: 'abc123', | ||
administrator: true, | ||
roles: [], | ||
}, | ||
}); | ||
store = initializeStore(); | ||
|
||
axiosMock = new MockAdapter(getAuthenticatedHttpClient()); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
axiosMock.restore(); | ||
queryClient.clear(); | ||
}); | ||
|
||
test('call api data with correct data', async () => { | ||
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(200, { | ||
id: 'library-id', | ||
}); | ||
|
||
const { getByRole } = render(<RootWrapper />); | ||
|
||
const titleInput = getByRole('textbox', { name: /library name/i }); | ||
userEvent.click(titleInput); | ||
userEvent.type(titleInput, 'Test Library Name'); | ||
|
||
const orgInput = getByRole('combobox', { name: /organization/i }); | ||
userEvent.click(orgInput); | ||
userEvent.type(orgInput, 'org1'); | ||
userEvent.tab(); | ||
|
||
const slugInput = getByRole('textbox', { name: /library id/i }); | ||
userEvent.click(slugInput); | ||
userEvent.type(slugInput, 'test_library_slug'); | ||
|
||
fireEvent.click(getByRole('button', { name: /create/i })); | ||
await waitFor(() => { | ||
expect(axiosMock.history.post.length).toBe(1); | ||
expect(axiosMock.history.post[0].data).toBe( | ||
'{"description":"","title":"Test Library Name","org":"org1","slug":"test_library_slug"}', | ||
); | ||
expect(mockNavigate).toHaveBeenCalledWith('/library/library-id'); | ||
}); | ||
}); | ||
|
||
test('show api error', async () => { | ||
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(400, { | ||
field: 'Error message', | ||
}); | ||
const { getByRole, getByTestId } = render(<RootWrapper />); | ||
|
||
const titleInput = getByRole('textbox', { name: /library name/i }); | ||
userEvent.click(titleInput); | ||
userEvent.type(titleInput, 'Test Library Name'); | ||
|
||
const orgInput = getByTestId('autosuggest-textbox-input'); | ||
userEvent.click(orgInput); | ||
userEvent.type(orgInput, 'org1'); | ||
userEvent.tab(); | ||
|
||
const slugInput = getByRole('textbox', { name: /library id/i }); | ||
userEvent.click(slugInput); | ||
userEvent.type(slugInput, 'test_library_slug'); | ||
|
||
fireEvent.click(getByRole('button', { name: /create/i })); | ||
await waitFor(() => { | ||
expect(axiosMock.history.post.length).toBe(1); | ||
expect(axiosMock.history.post[0].data).toBe( | ||
'{"description":"","title":"Test Library Name","org":"org1","slug":"test_library_slug"}', | ||
); | ||
expect(mockNavigate).not.toHaveBeenCalled(); | ||
expect(getByRole('alert')).toHaveTextContent('Request failed with status code 400'); | ||
expect(getByRole('alert')).toHaveTextContent('{"field":"Error message"}'); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.