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
198 changes: 198 additions & 0 deletions src/pages/sponsors/__tests__/edit-sponsor-page.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import React from "react";
import userEvent from "@testing-library/user-event";
import { act, screen } from "@testing-library/react";
import EditSponsorPage, {
getFragmentFromValue,
getTabFromUrlFragment
} from "../edit-sponsor-page";
import { renderWithRedux } from "../../../utils/test-utils";
import { DEFAULT_STATE as currentSponsorDefaultState } from "../../../reducers/sponsors/sponsor-reducer";
import {
DEFAULT_ENTITY as defaultSummitEntity,
DEFAULT_STATE as currentSummitDefaultState
} from "../../../reducers/summits/current-summit-reducer";

global.window = { location: { pathname: "/sponsor-forms/items" } };
jest.mock(
"../sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js"
);
jest.mock("../sponsor-users-list-per-sponsor/index.js");

describe("EditSponsorPage", () => {
describe("getFragmentFromValue", () => {
it("returns correct values", () => {
const result1 = getFragmentFromValue(0);
expect(result1).toBe("general");

const result2 = getFragmentFromValue(2);
expect(result2).toBe("pages");

const result3 = getFragmentFromValue(3);
expect(result3).toBe("media_uploads");

const result4 = getFragmentFromValue(7);
expect(result4).toBe("badge_scans");
});
});

describe("getTabFromUrlFragment", () => {
it("returns correct values for defined fragments", () => {
const newUrl1 = "#general";
window.location.hash = newUrl1;

const result1 = getTabFromUrlFragment();
expect(result1).toBe(0);

const newUrl2 = "#pages";
window.location.hash = newUrl2;

const result2 = getTabFromUrlFragment();
expect(result2).toBe(2);

const newUrl3 = "#media_uploads";
window.location.hash = newUrl3;

const result3 = getTabFromUrlFragment();
expect(result3).toBe(3);

const newUrl4 = "#badge_scans";
window.location.hash = newUrl4;

const result4 = getTabFromUrlFragment();
expect(result4).toBe(7);
});
});

describe("Component", () => {
const originalWindowLocation = window.location;
it("should change the url fragment on tab click", async () => {
delete window.location;

Object.defineProperty(window, "location", {
configurable: true,
writable: true,
value: {
...originalWindowLocation,
hash: "#general"
}
});

renderWithRedux(
<EditSponsorPage
history={{}}
location={{
pathname: "/sponsor-forms/items"
}}
match={{}}
/>,
{
initialState: {
currentSummitState: {
currentSummit: defaultSummitEntity,
...currentSummitDefaultState
},
loggedUserState: {
member: {
groups: {}
}
},
currentSummitSponsorshipListState: {
sponsorships: [],
currentPage: 1,
lastPage: 1,
perPage: 100,
order: "order",
orderDir: 1,
totalSponsorships: 0
},
currentSponsorState: {
sponsorships: [],
...currentSponsorDefaultState
}
}
}
);

const usersTabReference = screen.getByText("edit_sponsor.tab.forms");

await act(async () => {
await userEvent.click(usersTabReference);
});

expect(window.location.hash).toBe("forms");
});
Comment on lines +116 to +123
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find and read the test file
cat -n src/pages/sponsors/__tests__/edit-sponsor-page.test.js | head -200

Repository: fntechgit/summit-admin

Length of output: 6883


🏁 Script executed:

# Find and read the EditSponsorPage component
cat -n src/pages/sponsors/edit-sponsor-page.js | head -300

Repository: fntechgit/summit-admin

Length of output: 11190


Fix hash format to match JSDOM behavior.
window.location.hash always includes the leading # when accessed in real JSDOM. The component assigns "forms" (line 129 of edit-sponsor-page.js calls getFragmentFromValue() which returns fragment values without #), but the test assertion should expect the actual stored value with the # prefix.

🔧 Suggested fix
-      expect(window.location.hash).toBe("forms");
+      expect(window.location.hash).toBe("#forms");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const usersTabReference = screen.getByText("edit_sponsor.tab.forms");
await act(async () => {
await userEvent.click(usersTabReference);
});
expect(window.location.hash).toBe("forms");
});
const usersTabReference = screen.getByText("edit_sponsor.tab.forms");
await act(async () => {
await userEvent.click(usersTabReference);
});
expect(window.location.hash).toBe("#forms");
});
🤖 Prompt for AI Agents
In `@src/pages/sponsors/__tests__/edit-sponsor-page.test.js` around lines 144 -
151, The test's assertion expects window.location.hash without the leading '#'
but JSDOM stores hashes with the '#' prefix; update the assertion in
src/pages/sponsors/__tests__/edit-sponsor-page.test.js to expect "#forms"
instead of "forms" (the component sets the fragment via getFragmentFromValue()
in edit-sponsor-page.js which returns values without '#', but
window.location.hash will include it). Ensure any other assertions in this test
that check window.location.hash are adjusted the same way.


it("should change the tab rendered on fragment change", async () => {
delete window.location;

Object.defineProperty(window, "location", {
configurable: true,
writable: true,
value: {
...originalWindowLocation,
hash: "#general"
}
});

renderWithRedux(
<EditSponsorPage
history={{}}
location={{
pathname: "/sponsor-forms/items"
}}
match={{}}
/>,
{
initialState: {
currentSummitState: {
currentSummit: defaultSummitEntity,
...currentSummitDefaultState
},
loggedUserState: {
member: {
groups: {}
}
},
currentSummitSponsorshipListState: {
sponsorships: [],
currentPage: 1,
lastPage: 1,
perPage: 100,
order: "order",
orderDir: 1,
totalSponsorships: 0
},
currentSponsorState: {
sponsorships: [],
...currentSponsorDefaultState
}
}
}
);

const generalTabPanel = screen.getByTestId("simple-tabpanel-0");
expect(generalTabPanel).toBeDefined();

delete window.location;

Object.defineProperty(window, "location", {
configurable: true,
writable: true,
value: {
...originalWindowLocation,
hash: "#users"
}
});

const usersTabPanel = screen.getByTestId("simple-tabpanel-1");
expect(usersTabPanel).toBeDefined();
});

afterEach(() => {
Object.defineProperty(window, "location", {
configurable: true,
value: originalWindowLocation
});
});
});
});
40 changes: 32 additions & 8 deletions src/pages/sponsors/edit-sponsor-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,35 @@ import SponsorCartTab from "./sponsor-cart-tab";
import SponsorFormsManageItems from "./sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items";
import { SPONSOR_TABS } from "../../utils/constants";

const CustomTabPanel = (props) => {
export const tabsToFragmentMap = [
"general",
"users",
"pages",
"media_uploads",
"forms",
"cart",
"purchases",
"badge_scans"
];

export const getFragmentFromValue = (index) => tabsToFragmentMap[index];

export const getTabFromUrlFragment = () => {
const currentHash = window.location.hash.replace("#", "");
const result = tabsToFragmentMap.indexOf(currentHash);
if (result > -1) return result;
return 0;
};

export const CustomTabPanel = (props) => {
const { children, value, index, ...other } = props;

return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
data-testid={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
Expand Down Expand Up @@ -97,18 +118,21 @@ const EditSponsorPage = (props) => {
getExtraQuestionMeta
} = props;

const [selectedTab, setSelectedTab] = useState(
location.pathname.includes("/sponsor-forms/") &&
location.pathname.includes("/items")
? SPONSOR_TABS.FORMS
: 0
);
const [selectedTab, setSelectedTab] = useState(getTabFromUrlFragment());

const handleTabChange = (event, newValue) => {
setSelectedTab(newValue);
history.push(`/app/summits/${currentSummit.id}/sponsors/${entity.id}`);
window.location.hash = getFragmentFromValue(newValue);
};

useEffect(() => {
const onHashChange = () => setSelectedTab(getTabFromUrlFragment());
window.addEventListener("hashchange", onHashChange);
// default call
if (!window.location.hash) handleTabChange(null, getTabFromUrlFragment());
return () => window.removeEventListener("hashchange", onHashChange);
}, []);

useEffect(() => {
if (entity.id > 0) {
getSponsorAdvertisements(entity.id);
Expand Down
2 changes: 1 addition & 1 deletion src/reducers/sponsors/sponsor-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export const DEFAULT_ENTITY = {
sponsorships_collection: DEFAULT_SPONSORHIPS_STATE
};

const DEFAULT_STATE = {
export const DEFAULT_STATE = {
entity: DEFAULT_ENTITY,
selectedSponsorship: null,
errors: {}
Expand Down
2 changes: 1 addition & 1 deletion src/reducers/summits/current-summit-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ const DEFAULT_PRINT_APP_MARKETING_SETTINGS = {
PRINT_APP_HIDE_FIND_TICKET_BY_FULLNAME: { id: 0, value: false }
};

const DEFAULT_STATE = {
export const DEFAULT_STATE = {
currentSummit: DEFAULT_ENTITY,
errors: {},
loading: true,
Expand Down
11 changes: 1 addition & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11048,16 +11048,7 @@ prop-types-extra@^1.0.1:
react-is "^16.3.2"
warning "^4.0.0"

prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.13.1"

prop-types@^15.8.1:
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
Expand Down