Skip to content
Merged
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
107 changes: 107 additions & 0 deletions src/actions/__tests__/sponsor-pages-actions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { normalizeSponsorManagedPageToCustomize } from "../sponsor-pages-actions";
import {
PAGE_MODULES_DOWNLOAD,
PAGES_MODULE_KINDS
} from "../../utils/constants";

jest.mock("moment-timezone", () => {
const mockMoment = { unix: jest.fn(() => 1700000000) };
const moment = jest.fn(() => mockMoment);
moment.tz = jest.fn(() => mockMoment);
moment.utc = jest.fn(() => mockMoment);
return moment;
});

jest.mock("openstack-uicore-foundation/lib/utils/actions", () => ({
createAction: jest.fn(),
getRequest: jest.fn(),
postRequest: jest.fn(),
putRequest: jest.fn(),
deleteRequest: jest.fn(),
startLoading: jest.fn(),
stopLoading: jest.fn(),
escapeFilterValue: jest.fn()
}));

jest.mock("openstack-uicore-foundation/lib/security/actions", () => ({
LOGOUT_USER: "LOGOUT_USER"
}));

jest.mock("../../utils/methods", () => ({
getAccessTokenSafely: jest.fn(),
normalizeSelectAllField: jest.fn(() => ({
apply_to_all_add_ons: false,
allowed_add_ons: []
}))
}));

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

const buildEntity = (modules = []) => ({
id: 10,
code: "P1",
allowed_add_ons: [],
page_ptr_id: 99,
sponsorship_types: [1],
summit_id: 5,
template_id: 3,
modules_count: 2,
modules
});

describe("normalizeSponsorManagedPageToCustomize", () => {
describe("DOCUMENT module — FILE type", () => {
it("includes the file when it is new (no id or file_id)", () => {
const newFile = { name: "contract.pdf" };
const entity = buildEntity([
{
kind: PAGES_MODULE_KINDS.DOCUMENT,
type: PAGE_MODULES_DOWNLOAD.FILE,
file: [newFile]
}
]);

const result = normalizeSponsorManagedPageToCustomize(entity);

expect(result.modules[0].file).toEqual(newFile);
});

it("omits the file when it already exists (id present) — isNewFile guard", () => {
const existingFile = { id: 42, name: "brief.pdf", file_id: 7 };
const entity = buildEntity([
{
kind: PAGES_MODULE_KINDS.DOCUMENT,
type: PAGE_MODULES_DOWNLOAD.FILE,
file: [existingFile]
}
]);

const result = normalizeSponsorManagedPageToCustomize(entity);

expect(result.modules[0].file).toBeUndefined();
});
});

describe("DOCUMENT module — URL type", () => {
it("omits file and file_id from the payload", () => {
const entity = buildEntity([
{
kind: PAGES_MODULE_KINDS.DOCUMENT,
type: PAGE_MODULES_DOWNLOAD.URL,
file: [{ id: 5 }],
file_id: 5,
external_url: "https://example.com"
}
]);

const result = normalizeSponsorManagedPageToCustomize(entity);

expect(result.modules[0].file).toBeUndefined();
expect(result.modules[0].file_id).toBeUndefined();
expect(result.modules[0].external_url).toBe("https://example.com");
});
});
});
29 changes: 3 additions & 26 deletions src/actions/sponsor-pages-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
escapeFilterValue
} from "openstack-uicore-foundation/lib/utils/actions";
import T from "i18n-react/dist/i18n-react";
import moment from "moment-timezone";
import {
getAccessTokenSafely,
normalizeSelectAllField
Expand All @@ -31,8 +30,7 @@ import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions";
import {
DEFAULT_CURRENT_PAGE,
DEFAULT_ORDER_DIR,
DEFAULT_PER_PAGE,
PAGES_MODULE_KINDS
DEFAULT_PER_PAGE
} from "../utils/constants";
import { normalizePageTemplateModules } from "../utils/page-template";

Expand Down Expand Up @@ -299,7 +297,7 @@ const normalizeSponsorManagedPage = (entity) => {
return normalizedEntity;
};

const normalizeSponsorManagedPageToCustomize = (entity) => {
export const normalizeSponsorManagedPageToCustomize = (entity) => {
const normalizedEntity = {
...entity,
...normalizeSelectAllField(
Expand All @@ -309,28 +307,7 @@ const normalizeSponsorManagedPageToCustomize = (entity) => {
)
};

normalizedEntity.modules = entity.modules.map((module) => {
const normalizedModule = { ...module };

if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.upload_deadline) {
normalizedModule.upload_deadline = moment
.utc(module.upload_deadline)
.unix();
}

if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.file_type_id) {
normalizedModule.file_type_id =
module.file_type_id?.value || module.file_type_id;
}

if (module.kind === PAGES_MODULE_KINDS.DOCUMENT && module.file) {
normalizedModule.file = module.file[0] || null;
}

delete normalizedModule._tempId;

return normalizedModule;
});
normalizedEntity.modules = normalizePageTemplateModules(entity.modules);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normalizePageTemplateModules is now shared with the customized-page flow. The old inline code sent every file unconditionally; this shared helper only sends a file if it lacks an id/file_id (the isNewFile guard) — a silent behavioral change for the managed-page PUT path.

Missing regression tests for normalizeSponsorManagedPageToCustomize in src/actions/__tests__/:

  • DOCUMENT module with a new file (no id/file_id) → verify file is included in the PUT payload
  • DOCUMENT module with an existing file (id present) → verify file is NOT included in the PUT payload (isNewFile guard fires)
  • DOCUMENT/URL module → verify no file appears in the PUT payload


delete normalizedEntity.page_ptr_id;
delete normalizedEntity.sponsorship_types;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { RECEIVE_SUMMIT_SPONSORSHIP_TYPES } from "../../../actions/summit-actions";
import {
RECEIVE_SPONSOR_MANAGED_PAGE,
RECEIVE_SPONSOR_CUSTOMIZED_PAGE
} from "../../../actions/sponsor-pages-actions";
import {
PAGE_MODULES_DOWNLOAD,
PAGES_MODULE_KINDS
} from "../../../utils/constants";
import sponsorPagePagesListReducer, {
DEFAULT_STATE
} from "../sponsor-page-pages-list-reducer";
Expand All @@ -16,6 +24,115 @@ function createInitialState(overrides = {}) {
}

describe("sponsorPagePagesListReducer", () => {
const dispatchReceiveManagedPage = (state, modules) =>
sponsorPagePagesListReducer(state, {
type: RECEIVE_SPONSOR_MANAGED_PAGE,
payload: { response: { id: 5, code: "P1", modules } }
});

const dispatchReceiveCustomizedPage = (state, modules) =>
sponsorPagePagesListReducer(state, {
type: RECEIVE_SPONSOR_CUSTOMIZED_PAGE,
payload: { response: { id: 5, code: "P1", modules } }
});

describe("RECEIVE_SPONSOR_MANAGED_PAGE", () => {
it("wraps a DOCUMENT module file in an array and sets type to FILE", () => {
const file = {
id: 10,
storage_key: "uploads/doc.pdf",
file_url: "https://cdn/doc.pdf"
};
const state = createInitialState({ summitTZ: "America/Los_Angeles" });

const result = dispatchReceiveManagedPage(state, [
{ id: 1, kind: PAGES_MODULE_KINDS.DOCUMENT, file }
]);

const [mod] = result.currentEditPage.modules;
expect(mod.file).toEqual([
{ ...file, file_path: file.storage_key, public_url: file.file_url }
]);
expect(mod.type).toBe(PAGE_MODULES_DOWNLOAD.FILE);
});

it("sets type to URL for a DOCUMENT module without a file", () => {
const state = createInitialState({ summitTZ: "UTC" });

const result = dispatchReceiveManagedPage(state, [
{ id: 2, kind: PAGES_MODULE_KINDS.DOCUMENT, file: null }
]);

const [mod] = result.currentEditPage.modules;
expect(mod.type).toBe(PAGE_MODULES_DOWNLOAD.URL);
});

it("converts upload_deadline for a MEDIA module using summitTZ", () => {
const state = createInitialState({ summitTZ: "America/Los_Angeles" });

const result = dispatchReceiveManagedPage(state, [
{ id: 3, kind: PAGES_MODULE_KINDS.MEDIA, upload_deadline: 1700000000 }
]);

const [mod] = result.currentEditPage.modules;
expect(mod.upload_deadline).toBe("moment-1700000000-America/Los_Angeles");
});

it("falls back to UTC when summitTZ is not set", () => {
const state = createInitialState({ summitTZ: "" });

const result = dispatchReceiveManagedPage(state, [
{ id: 4, kind: PAGES_MODULE_KINDS.MEDIA, upload_deadline: 1700000000 }
]);

const [mod] = result.currentEditPage.modules;
expect(mod.upload_deadline).toBe("moment-1700000000-UTC");
});
});

describe("RECEIVE_SPONSOR_CUSTOMIZED_PAGE", () => {
it("wraps a DOCUMENT module file in an array and sets type to FILE", () => {
const file = {
id: 20,
storage_key: "uploads/spec.pdf",
file_url: "https://cdn/spec.pdf"
};
const state = createInitialState({ summitTZ: "UTC" });

const result = dispatchReceiveCustomizedPage(state, [
{ id: 1, kind: PAGES_MODULE_KINDS.DOCUMENT, file }
]);

const [mod] = result.currentEditPage.modules;
expect(mod.file).toEqual([
{ ...file, file_path: file.storage_key, public_url: file.file_url }
]);
expect(mod.type).toBe(PAGE_MODULES_DOWNLOAD.FILE);
});

it("sets type to URL for a DOCUMENT module without a file", () => {
const state = createInitialState({ summitTZ: "UTC" });

const result = dispatchReceiveCustomizedPage(state, [
{ id: 2, kind: PAGES_MODULE_KINDS.DOCUMENT, file: null }
]);

const [mod] = result.currentEditPage.modules;
expect(mod.type).toBe(PAGE_MODULES_DOWNLOAD.URL);
});

it("converts upload_deadline for a MEDIA module using summitTZ", () => {
const state = createInitialState({ summitTZ: "America/Los_Angeles" });

const result = dispatchReceiveCustomizedPage(state, [
{ id: 3, kind: PAGES_MODULE_KINDS.MEDIA, upload_deadline: 1700000000 }
]);

const [mod] = result.currentEditPage.modules;
expect(mod.upload_deadline).toBe("moment-1700000000-America/Los_Angeles");
});
});

describe("RECEIVE_SUMMIT_SPONSORSHIP_TYPES", () => {
const makePayload = (currentPage, data) => ({
response: {
Expand Down
61 changes: 12 additions & 49 deletions src/reducers/sponsors/sponsor-page-pages-list-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* */

import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions";
import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods";
import {
REQUEST_SPONSOR_MANAGED_PAGES,
RECEIVE_SPONSOR_MANAGED_PAGES,
Expand All @@ -28,10 +27,8 @@ import {
SET_CURRENT_SUMMIT,
RECEIVE_SUMMIT_SPONSORSHIP_TYPES
} from "../../actions/summit-actions";
import {
PAGE_MODULES_DOWNLOAD,
PAGES_MODULE_KINDS
} from "../../utils/constants";
import { PAGES_MODULE_KINDS } from "../../utils/constants";
import { denormalizePageModules } from "../../utils/page-template";

const DEFAULT_PAGE = {
code: "",
Expand Down Expand Up @@ -182,54 +179,20 @@ const sponsorPagePagesListReducer = (state = DEFAULT_STATE, action) => {
case RECEIVE_SPONSOR_MANAGED_PAGE: {
const editPage = payload.response;

const currentEditPage = {
...editPage,
modules: editPage.modules.map((m) => ({
...m,
...(m.upload_deadline
? {
upload_deadline: epochToMomentTimeZone(
m.upload_deadline,
state.summitTZ || "UTC"
)
}
: {})
}))
};
return { ...state, currentEditPage };
const modules = denormalizePageModules(
editPage.modules,
state.summitTZ || "UTC"
);

return { ...state, currentEditPage: { ...editPage, modules } };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing regression tests for this new delegation to denormalizePageModules.

Suggested test cases in src/reducers/sponsors/__tests__/sponsor-page-pages-list-reducer.test.js:

  • MEDIA module with upload_deadline → verify timezone conversion is applied via epochToMomentTimeZone
  • DOCUMENT module with file present → verify file is array-wrapped and type is set to FILE
  • DOCUMENT module without file → verify type is set to URL

}
case RECEIVE_SPONSOR_CUSTOMIZED_PAGE: {
const customizedPage = payload.response;

const modules = customizedPage.modules.map((module) => {
const tmpModule = {
...module,
...(module.upload_deadline
? {
upload_deadline: epochToMomentTimeZone(
module.upload_deadline,
state.summitTZ || "UTC"
)
}
: {})
};

if (module.kind === PAGES_MODULE_KINDS.DOCUMENT) {
if (module.file) {
tmpModule.file = [
{
...module.file,
file_path: module.file.storage_key,
public_url: module.file.file_url
}
];
tmpModule.type = PAGE_MODULES_DOWNLOAD.FILE;
} else {
tmpModule.type = PAGE_MODULES_DOWNLOAD.URL;
}
}
return tmpModule;
});
const modules = denormalizePageModules(
customizedPage.modules,
state.summitTZ || "UTC"
);

return { ...state, currentEditPage: { ...customizedPage, modules } };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing regression test for this new delegation to denormalizePageModules.

Suggested test case in src/reducers/sponsors/__tests__/sponsor-page-pages-list-reducer.test.js:

  • DOCUMENT module with file present → verify the file is array-wrapped with file_path and public_url mapped, and type is set to FILE

}
Expand Down
Loading