Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 51 additions & 54 deletions src/actions/media-file-type-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,12 @@ import {
createAction,
stopLoading,
startLoading,
showMessage,
showSuccessMessage,
authErrorHandler,
fetchResponseHandler,
fetchErrorHandler,
escapeFilterValue
} from "openstack-uicore-foundation/lib/utils/actions";
import URI from "urijs";
import history from "../history";
import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions";
import { getAccessTokenSafely } from "../utils/methods";
import {
DEBOUNCE_WAIT,
Expand Down Expand Up @@ -87,9 +84,9 @@ export const getMediaFileTypes =
createAction(REQUEST_MEDIA_FILE_TYPES),
createAction(RECEIVE_MEDIA_FILE_TYPES),
`${window.API_BASE_URL}/api/v1/summit-media-file-types`,
authErrorHandler,
{ order, orderDir, term }
)(params)(dispatch).then(() => {
snackbarErrorHandler,
{ order, orderDir, term, perPage }
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand All @@ -109,9 +106,9 @@ export const getAllMediaFileTypes = () => async (dispatch) => {
createAction(REQUEST_ALL_MEDIA_FILE_TYPES),
createAction(RECEIVE_ALL_MEDIA_FILE_TYPES),
`${window.API_BASE_URL}/api/v1/summit-media-file-types`,
authErrorHandler,
snackbarErrorHandler,
{}
)(params)(dispatch).then(() => {
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand All @@ -129,8 +126,8 @@ export const getMediaFileType = (mediaFileTypeId) => async (dispatch) => {
null,
createAction(RECEIVE_MEDIA_FILE_TYPE),
`${window.API_BASE_URL}/api/v1/summit-media-file-types/${mediaFileTypeId}`,
authErrorHandler
)(params)(dispatch).then(() => {
snackbarErrorHandler
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand All @@ -139,52 +136,52 @@ export const resetMediaFileTypeForm = () => (dispatch) => {
dispatch(createAction(RESET_MEDIA_FILE_TYPE_FORM)({}));
};

export const saveMediaFileType =
(entity, noAlert = false) =>
async (dispatch) => {
const accessToken = await getAccessTokenSafely();
export const saveMediaFileType = (entity) => async (dispatch) => {
const accessToken = await getAccessTokenSafely();

dispatch(startLoading());
dispatch(startLoading());

const normalizedEntity = normalizeEntity(entity);
const params = { access_token: accessToken };

if (entity.id) {
putRequest(
createAction(UPDATE_MEDIA_FILE_TYPE),
createAction(MEDIA_FILE_TYPE_UPDATED),
`${window.API_BASE_URL}/api/v1/summit-media-file-types/${entity.id}`,
normalizedEntity,
authErrorHandler,
entity
)(params)(dispatch).then(() => {
if (!noAlert)
dispatch(showSuccessMessage(T.translate("media_file_type.saved")));
else dispatch(stopLoading());
});
} else {
const success_message = {
title: T.translate("general.done"),
html: T.translate("media_file_type.created"),
type: "success"
};

postRequest(
createAction(UPDATE_MEDIA_FILE_TYPE),
createAction(MEDIA_FILE_TYPE_ADDED),
`${window.API_BASE_URL}/api/v1/summit-media-file-types`,
normalizedEntity,
authErrorHandler,
entity
)(params)(dispatch).then((payload) => {
const normalizedEntity = normalizeEntity(entity);
const params = { access_token: accessToken };

if (entity.id) {
return putRequest(
createAction(UPDATE_MEDIA_FILE_TYPE),
createAction(MEDIA_FILE_TYPE_UPDATED),
`${window.API_BASE_URL}/api/v1/summit-media-file-types/${entity.id}`,
normalizedEntity,
snackbarErrorHandler,
entity
)(params)(dispatch)
.then(() => {
dispatch(
showMessage(success_message, () => {
history.push(`/app/media-file-types/${payload.response.id}`);
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("media_file_type.saved")
})
);
});
}
};
})
.finally(() => dispatch(stopLoading()));
}

return postRequest(
createAction(UPDATE_MEDIA_FILE_TYPE),
createAction(MEDIA_FILE_TYPE_ADDED),
`${window.API_BASE_URL}/api/v1/summit-media-file-types`,
normalizedEntity,
snackbarErrorHandler,
entity
)(params)(dispatch)
.then(() => {
dispatch(
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("media_file_type.created")
})
);
})
.finally(() => dispatch(stopLoading()));
};

export const deleteMediaFileType = (mediaFileTypeId) => async (dispatch) => {
const accessToken = await getAccessTokenSafely();
Expand All @@ -198,8 +195,8 @@ export const deleteMediaFileType = (mediaFileTypeId) => async (dispatch) => {
createAction(MEDIA_FILE_TYPE_DELETED)({ mediaFileTypeId }),
`${window.API_BASE_URL}/api/v1/summit-media-file-types/${mediaFileTypeId}`,
null,
authErrorHandler
)(params)(dispatch).then(() => {
snackbarErrorHandler
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3354,7 +3354,7 @@
"description": "Description",
"allowed_extensions_input": "Allowed Extensions (comma separated: PDF,DOC,etc)",
"allowed_extensions": "Allowed Extensions",
"delete_warning": "Are you sure you want to delete media file type ",
"delete_warning": "Are you sure you want to delete media file type {name}?",
"saved": "Media File Type saved successfully.",
"created": "Media File Type created successfully.",
"placeholders": {
Expand Down
222 changes: 222 additions & 0 deletions src/pages/media_file_types/__tests__/media-file-type-list-page.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import React from "react";
import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithRedux } from "../../../utils/test-utils";
import MediaFileTypeListPage from "../media-file-type-list-page";
import * as mediaFileTypeActions from "../../../actions/media-file-type-actions";
import {
DEFAULT_CURRENT_PAGE,
DEFAULT_PER_PAGE
} from "../../../utils/constants";

jest.mock("i18n-react/dist/i18n-react", () => ({
__esModule: true,
default: { translate: (key) => key }
}));

jest.mock("openstack-uicore-foundation/lib/components/mui/table", () => ({
__esModule: true,
default: ({ data, onEdit, onDelete }) => (
<div data-testid="mui-table">
{data.map((row) => (
<div key={row.id} data-testid={`row-${row.id}`}>
<span>{row.name}</span>
<button type="button" onClick={() => onEdit(row)}>
edit-row
</button>
<button type="button" onClick={() => onDelete(row.id)}>
delete-row
</button>
</div>
))}
</div>
)
}));

jest.mock("openstack-uicore-foundation/lib/components/mui/search-input", () => {
const React = require("react");
return {
__esModule: true,
default: ({ onSearch, term }) => {
const [value, setValue] = React.useState(term || "");
return (
<input
data-testid="search-input"
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") onSearch(value);
}}
/>
);
}
};
});

jest.mock("../components/media-file-type-dialog", () => ({
__esModule: true,
default: ({ onClose, onSave }) => (
<div role="dialog" data-testid="media-file-type-dialog">
<button type="button" onClick={onClose}>
dialog-close
</button>
<button type="button" onClick={() => onSave({ id: 0, name: "New Type" })}>
dialog-save
</button>
</div>
)
}));

jest.mock("../../../actions/media-file-type-actions", () => ({
...jest.requireActual("../../../actions/media-file-type-actions"),
getMediaFileTypes: jest.fn(() => () => Promise.resolve()),
getMediaFileType: jest.fn(() => () => Promise.resolve()),
saveMediaFileType: jest.fn(() => () => Promise.resolve()),
deleteMediaFileType: jest.fn(() => () => Promise.resolve()),
resetMediaFileTypeForm: jest.fn(() => () => {})
}));

const buildInitialState = (listOverrides = {}) => ({
mediaFileTypeListState: {
media_file_types: [],
term: "",
order: "id",
orderDir: 1,
currentPage: 1,
lastPage: 1,
perPage: 10,
totalMediaFileTypes: 0,
...listOverrides
},
mediaFileTypeState: {
entity: { id: 0, name: "", description: "", allowed_extensions: "" },
errors: {}
}
});

describe("MediaFileTypeListPage", () => {
beforeEach(() => {
jest.clearAllMocks();
});

test("should call getMediaFileTypes on mount", async () => {
renderWithRedux(<MediaFileTypeListPage />, {
initialState: buildInitialState()
});

await waitFor(() => {
expect(mediaFileTypeActions.getMediaFileTypes).toHaveBeenCalledTimes(1);
});
});

test("should show empty state message when list is empty", () => {
renderWithRedux(<MediaFileTypeListPage />, {
initialState: buildInitialState()
});

expect(screen.getByText("media_file_type.no_results")).toBeInTheDocument();
expect(screen.queryByTestId("mui-table")).not.toBeInTheDocument();
});

test("should show table and total count when media file types are present", () => {
renderWithRedux(<MediaFileTypeListPage />, {
initialState: buildInitialState({
media_file_types: [
{ id: 1, name: "Image", description: "", allowed_extensions: [] },
{ id: 2, name: "Document", description: "", allowed_extensions: [] }
],
totalMediaFileTypes: 2
})
});

expect(screen.getByTestId("mui-table")).toBeInTheDocument();
expect(
screen.getByText(/2\smedia_file_type\.media_file_types/)
).toBeInTheDocument();
expect(
screen.queryByText("media_file_type.no_results")
).not.toBeInTheDocument();
});

test("opens dialog and resets form when Add button is clicked", async () => {
const user = userEvent.setup();
renderWithRedux(<MediaFileTypeListPage />, {
initialState: buildInitialState()
});

await user.click(screen.getByText("media_file_type.add"));

expect(mediaFileTypeActions.resetMediaFileTypeForm).toHaveBeenCalled();
expect(screen.getByTestId("media-file-type-dialog")).toBeInTheDocument();
});

test("closes dialog when dialog close is triggered", async () => {
const user = userEvent.setup();
renderWithRedux(<MediaFileTypeListPage />, {
initialState: buildInitialState()
});

await user.click(screen.getByText("media_file_type.add"));
expect(screen.getByTestId("media-file-type-dialog")).toBeInTheDocument();

await user.click(screen.getByText("dialog-close"));
expect(
screen.queryByTestId("media-file-type-dialog")
).not.toBeInTheDocument();
});

test("should fetch entity and opens dialog on row edit", async () => {
const user = userEvent.setup();
renderWithRedux(<MediaFileTypeListPage />, {
initialState: buildInitialState({
media_file_types: [
{ id: 7, name: "Video", description: "", allowed_extensions: ["MP4"] }
],
totalMediaFileTypes: 1
})
});

await user.click(screen.getByText("edit-row"));

await waitFor(() => {
expect(mediaFileTypeActions.getMediaFileType).toHaveBeenCalledWith(7);
});
});

test("should call deleteMediaFileType and refreshes list on row delete", async () => {
const user = userEvent.setup();
renderWithRedux(<MediaFileTypeListPage />, {
initialState: buildInitialState({
media_file_types: [
{ id: 3, name: "Audio", description: "", allowed_extensions: [] }
],
totalMediaFileTypes: 1
})
});

await user.click(screen.getByText("delete-row"));

await waitFor(() => {
expect(mediaFileTypeActions.deleteMediaFileType).toHaveBeenCalledWith(3);
});
});

test("should call getMediaFileTypes with search term on search", async () => {
const user = userEvent.setup();
renderWithRedux(<MediaFileTypeListPage />, {
initialState: buildInitialState()
});

await user.type(screen.getByTestId("search-input"), "pdf{Enter}");

await waitFor(() => {
expect(mediaFileTypeActions.getMediaFileTypes).toHaveBeenCalledWith(
"pdf",
DEFAULT_CURRENT_PAGE,
DEFAULT_PER_PAGE,
"id",
1
);
});
});
});
Loading
Loading