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
20 changes: 18 additions & 2 deletions src/actions/__tests__/dropbox-sync-actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ describe("dropbox sync actions", () => {
});

test("resyncRoom dispatches START_LOADING, RESYNC_ROOM_DISPATCHED, STOP_LOADING", async () => {
store.dispatch(DropboxSyncActions.resyncRoom("Main Venue", "Room A"));
store.dispatch(DropboxSyncActions.resyncRoom(12, 345));
await flushPromises();

const actions = store.getActions();
Expand All @@ -394,6 +394,22 @@ describe("dropbox sync actions", () => {
expect(postRequest).toHaveBeenCalled();
});

test("resyncRoom targets the materializer's integer venue/room route", async () => {
// The materializer route is /<int:summit_id>/<int:venue_id>/<int:room_id>/.
// It previously took names, and sending names does not resolve at all —
// the request 404s and the resync silently never happens. The older
// assertions here only checked that postRequest was called, so they stayed
// green through that regression; pin the URL itself.
store.dispatch(DropboxSyncActions.resyncRoom(12, 345));
await flushPromises();

expect(postRequest).toHaveBeenCalledTimes(1);
const url = postRequest.mock.calls[0][2];
expect(url).toBe(
"https://test-api.example.com/api/v1/sync/materialize/1/12/345/"
);
});

test("getSyncConfig dispatches RECEIVE_SYNC_CONFIG with empty payload on failure", async () => {
getRequest.mockImplementation(mockRequestImplReject);

Expand Down Expand Up @@ -442,7 +458,7 @@ describe("dropbox sync actions", () => {
test("resyncRoom dispatches STOP_LOADING on failure", async () => {
postRequest.mockImplementation(() => mockRequestImplReject());

store.dispatch(DropboxSyncActions.resyncRoom("Main Venue", "Room A"));
store.dispatch(DropboxSyncActions.resyncRoom(12, 345));
await flushPromises();

const actions = store.getActions();
Expand Down
60 changes: 30 additions & 30 deletions src/actions/dropbox-sync-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,38 +352,38 @@ export const rebuildSync = () => async (dispatch, getState) => {
});
};

export const resyncRoom =
(venueName, roomName) => async (dispatch, getState) => {
const { currentSummitState } = getState();
const baseUrl = getBaseUrl();
const summitId = currentSummitState?.currentSummit?.id;

if (!baseUrl || !summitId) return;
// The materializer's per-room route takes integer venue/room ids
// (/api/v1/sync/materialize/{summitId}/{venueId}/{roomId}/). It previously
// took names; sending names now does not resolve at all.
export const resyncRoom = (venueId, roomId) => async (dispatch, getState) => {
const { currentSummitState } = getState();
const baseUrl = getBaseUrl();
const summitId = currentSummitState?.currentSummit?.id;

const accessToken = await getAccessTokenSafely();
if (!baseUrl || !summitId) return;

dispatch(startLoading());
const accessToken = await getAccessTokenSafely();

const params = {
access_token: accessToken
};
dispatch(startLoading());

return postRequest(
null,
createAction(RESYNC_ROOM_DISPATCHED),
`${baseUrl}/api/v1/sync/materialize/${summitId}/${encodeURIComponent(
venueName
)}/${encodeURIComponent(roomName)}/`,
null,
authErrorHandler
)(params)(dispatch)
.then(() => {
dispatch(stopLoading());
dispatch(
showSuccessMessage(T.translate("dropbox_sync.resync_dispatched"))
);
})
.catch(() => {
dispatch(stopLoading());
});
const params = {
access_token: accessToken
};

return postRequest(
null,
createAction(RESYNC_ROOM_DISPATCHED),
`${baseUrl}/api/v1/sync/materialize/${summitId}/${venueId}/${roomId}/`,
null,
authErrorHandler
)(params)(dispatch)
.then(() => {
dispatch(stopLoading());
dispatch(
showSuccessMessage(T.translate("dropbox_sync.resync_dispatched"))
);
})
.catch(() => {
dispatch(stopLoading());
});
};
261 changes: 149 additions & 112 deletions src/components/forms/__tests__/location-form.test.js
Original file line number Diff line number Diff line change
@@ -1,124 +1,129 @@
import React from "react";
import { render } from "@testing-library/react";
import { screen } from "@testing-library/dom";
import userEvent from "@testing-library/user-event";
import LocationForm from "../location-form";
import currentSummitMock from "../../../__mocks__/currentSummitMock";

describe("LocationForm", () => {
beforeEach(() => {
const props = {
history: {
length: 8,
action: "POP",
location: {
pathname: "/app/summits/69/locations/781",
search: "",
hash: "",
key: "wh0sst"
}
},
currentSummit: currentSummitMock,
allClasses: [
{
name: "string",
short_name: "string",
description: "string",
type: ["External", "Internal"],
banners: "array",
order: "integer",
opening_hour: "integer",
closing_hour: "integer",
address_1: "string",
address_2: "string",
zip_code: "string",
city: "string",
state: "string",
country: "string",
website_url: "string",
lng: "string",
lat: "string",
display_on_site: "boolean",
details_page: "boolean",
location_message: "string",
images: "array",
class_name: "SummitVenue",
is_main: "boolean",
floors: "array",
rooms: "array"
},
{
name: "string",
short_name: "string",
description: "string",
type: ["External", "Internal"],
banners: "array",
order: "integer",
opening_hour: "integer",
closing_hour: "integer",
address_1: "string",
address_2: "string",
zip_code: "string",
city: "string",
state: "string",
country: "string",
website_url: "string",
lng: "string",
lat: "string",
display_on_site: "boolean",
details_page: "boolean",
location_message: "string",
images: "array",
class_name: "SummitAirport",
capacity: "integer",
airport_type: ["International", "Domestic"]
}
],
entity: {
id: 781,
name: "International Barcelona Convention Center",
short_name: "CCIB",
const buildProps = (overrides = {}) => {
const props = {
history: {
length: 8,
action: "POP",
location: {
pathname: "/app/summits/69/locations/781",
search: "",
hash: "",
key: "wh0sst"
}
},
currentSummit: currentSummitMock,
allClasses: [
{
name: "string",
short_name: "string",
description: "string",
type: ["External", "Internal"],
banners: "array",
order: "integer",
opening_hour: "integer",
closing_hour: "integer",
address_1: "string",
address_2: "string",
zip_code: "string",
city: "string",
state: "string",
country: "string",
website_url: "string",
lng: "string",
lat: "string",
display_on_site: "boolean",
details_page: "boolean",
location_message: "string",
images: "array",
class_name: "SummitVenue",
description: "",
location_type: "Internal",
address_1: "Plaça de Willy Brandt, 11-14",
address_2: "",
zip_code: "08019",
city: "Sant Marti",
state: "Barcelona",
country: "ES",
website_url: "",
lng: "2.2193",
lat: "41.4088",
display_on_site: false,
details_page: false,
is_main: false,
location_message: "",
maps: [],
images: [],
rooms: [],
floors: [],
capacity: 0,
booking_link: "",
sold_out: false,
airport_type: "",
hotel_type: "",
created: 1762190581,
last_edited: 1762190581,
order: 48,
opening_hour: "",
closing_hour: ""
is_main: "boolean",
floors: "array",
rooms: "array"
},
errors: {},
onSubmit: jest.fn(),
onMapUpdate: jest.fn(),
onMarkerDragged: jest.fn(),
onFloorDelete: jest.fn(),
onRoomDelete: jest.fn(),
onImageDelete: jest.fn(),
onMapDelete: jest.fn()
};
{
name: "string",
short_name: "string",
description: "string",
type: ["External", "Internal"],
banners: "array",
order: "integer",
opening_hour: "integer",
closing_hour: "integer",
address_1: "string",
address_2: "string",
zip_code: "string",
city: "string",
state: "string",
country: "string",
website_url: "string",
lng: "string",
lat: "string",
display_on_site: "boolean",
details_page: "boolean",
location_message: "string",
images: "array",
class_name: "SummitAirport",
capacity: "integer",
airport_type: ["International", "Domestic"]
}
],
entity: {
id: 781,
name: "International Barcelona Convention Center",
short_name: "CCIB",
class_name: "SummitVenue",
description: "",
location_type: "Internal",
address_1: "Plaça de Willy Brandt, 11-14",
address_2: "",
zip_code: "08019",
city: "Sant Marti",
state: "Barcelona",
country: "ES",
website_url: "",
lng: "2.2193",
lat: "41.4088",
display_on_site: false,
details_page: false,
is_main: false,
location_message: "",
maps: [],
images: [],
rooms: [],
floors: [],
capacity: 0,
booking_link: "",
sold_out: false,
airport_type: "",
hotel_type: "",
created: 1762190581,
last_edited: 1762190581,
order: 48,
opening_hour: "",
closing_hour: ""
},
errors: {},
onSubmit: jest.fn(),
onMapUpdate: jest.fn(),
onMarkerDragged: jest.fn(),
onFloorDelete: jest.fn(),
onRoomDelete: jest.fn(),
onImageDelete: jest.fn(),
onMapDelete: jest.fn()
};

render(<LocationForm {...props} />);
return { ...props, ...overrides };
};

describe("LocationForm", () => {
beforeEach(() => {
render(<LocationForm {...buildProps()} />);
});

describe("IsMain? checkbox", () => {
Expand All @@ -128,3 +133,35 @@ describe("LocationForm", () => {
});
});
});

describe("LocationForm Resync Room action", () => {
// The materializer's per-room resync route takes integer venue/room ids.
// This form is handed the room id already; it previously looked the room's
// NAME back up and sent that, which cannot resolve against the route.
const VENUE_ID = 781;
const ROOM_ID = 5002;

test("passes the venue id and room id, not their names", async () => {
const onRoomResync = jest.fn();
const props = buildProps({ syncEnabled: true, onRoomResync });
props.entity = {
...props.entity,
id: VENUE_ID,
rooms: [{ id: ROOM_ID, name: "Room A", capacity: 10, floor_name: "" }]
};

const { container } = render(<LocationForm {...props} />);

// The Rooms panel is collapsed by default; open it the way a user would.
await userEvent.click(screen.getByText("edit_location.rooms"));

// uicore's Table renders custom row actions as <a data-tip={tooltip}>.
const resyncButton = container.querySelector(
"[data-tip='dropbox_sync.resync_tooltip']"
);
expect(resyncButton).toBeInTheDocument();
await userEvent.click(resyncButton);

expect(onRoomResync).toHaveBeenCalledWith(VENUE_ID, ROOM_ID);
});
});
Loading
Loading