2025-06-30 18:58:24 +02:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: © 2025 Hornwitser <code@hornwitser.no>
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
*/
|
2025-06-23 22:46:39 +02:00
|
|
|
import { ClientEntity } from "~/utils/client-entity";
|
|
|
|
import { ClientSchedule, ClientScheduleEventSlot, ClientScheduleLocation, ClientScheduleShiftSlot, toIso } from "~/utils/client-schedule";
|
2025-06-12 21:45:34 +02:00
|
|
|
import { describe, expect, test } from "vitest";
|
2025-06-24 15:19:11 +02:00
|
|
|
import type { ApiEntity, ApiSchedule } from "~/shared/types/api";
|
2025-06-12 21:45:34 +02:00
|
|
|
import { DateTime, FixedOffsetZone } from "~/shared/utils/luxon";
|
|
|
|
|
|
|
|
const locale = "en-GB";
|
|
|
|
const now = DateTime.now().setLocale(locale);
|
2025-06-13 21:06:48 +02:00
|
|
|
const later = now.plus({ minutes: 2 });
|
2025-06-12 21:45:34 +02:00
|
|
|
const zone = now.zone;
|
|
|
|
const nowIso = now.setZone(FixedOffsetZone.utcInstance).toISO();
|
2025-06-23 22:46:39 +02:00
|
|
|
const laterIso = later.setZone(FixedOffsetZone.utcInstance).toISO();
|
2025-06-12 21:45:34 +02:00
|
|
|
|
2025-06-23 22:46:39 +02:00
|
|
|
function fixtureClientSchedule(multiSlot = false) {
|
2025-06-12 21:45:34 +02:00
|
|
|
const left = new ClientScheduleLocation(1, now, false, "Left", "");
|
|
|
|
const right = new ClientScheduleLocation(2, now, false, "Right", "This is the right place");
|
|
|
|
|
|
|
|
const events = [
|
|
|
|
new ClientScheduleEvent(
|
2025-06-23 22:46:39 +02:00
|
|
|
1, now, false, "Up", false, "", false, "What's Up?", 0, new Set(multiSlot ? [1, 2] : [1]),
|
2025-06-12 21:45:34 +02:00
|
|
|
),
|
|
|
|
new ClientScheduleEvent(
|
2025-06-23 22:46:39 +02:00
|
|
|
2, now, false, "Down", false, "", false, "", 0, new Set(multiSlot ? [] : [2]),
|
2025-06-12 21:45:34 +02:00
|
|
|
),
|
|
|
|
];
|
2025-06-23 22:46:39 +02:00
|
|
|
const eventSlots = idMap([
|
|
|
|
new ClientScheduleEventSlot(1, false, false, 1, now, now.plus({ hours: 1 }), new Set([left.id]), new Set(), 0),
|
|
|
|
new ClientScheduleEventSlot(2, false, false, multiSlot ? 1 : 2, now, now.plus({ hours: 2 }), new Set([right.id]), new Set(), 0),
|
|
|
|
]);
|
2025-06-12 21:45:34 +02:00
|
|
|
|
|
|
|
const red = new ClientScheduleRole(1, now, false, "Red", "Is a color.");
|
|
|
|
const blue = new ClientScheduleRole(2, now, false, "Blue", "");
|
|
|
|
const shifts = [
|
|
|
|
new ClientScheduleShift(
|
2025-06-23 22:46:39 +02:00
|
|
|
1, now, false, red.id, "White", "", new Set(multiSlot ? [1, 2] : [1]),
|
2025-06-12 21:45:34 +02:00
|
|
|
),
|
|
|
|
new ClientScheduleShift(
|
2025-06-23 22:46:39 +02:00
|
|
|
2, now, false, blue.id, "Black", "Is dark.", new Set(multiSlot ? [] : [2]),
|
2025-06-12 21:45:34 +02:00
|
|
|
),
|
|
|
|
];
|
2025-06-23 22:46:39 +02:00
|
|
|
const shiftSlots = idMap([
|
|
|
|
new ClientScheduleShiftSlot(1, false, false, 1, now, now.plus({ hours: 1 }), new Set()),
|
|
|
|
new ClientScheduleShiftSlot(2, false, false, multiSlot ? 1 : 2, now, now.plus({ hours: 2 }), new Set()),
|
|
|
|
]);
|
2025-06-12 21:45:34 +02:00
|
|
|
|
2025-06-23 22:46:39 +02:00
|
|
|
const schedule = new ClientSchedule(
|
2025-06-12 21:45:34 +02:00
|
|
|
111,
|
|
|
|
now,
|
|
|
|
false,
|
2025-06-23 22:46:39 +02:00
|
|
|
new ClientMap(ClientScheduleLocation, idMap([left, right]), new Map()),
|
|
|
|
new ClientMap(ClientScheduleEvent, idMap(events), new Map()),
|
|
|
|
eventSlots,
|
|
|
|
new ClientMap(ClientScheduleRole, idMap([red, blue]), new Map()),
|
|
|
|
new ClientMap(ClientScheduleShift, idMap(shifts), new Map()),
|
|
|
|
shiftSlots,
|
2025-06-12 21:45:34 +02:00
|
|
|
);
|
2025-06-23 22:46:39 +02:00
|
|
|
for (const event of events.values()) {
|
|
|
|
event.schedule = schedule;
|
|
|
|
}
|
|
|
|
for (const eventSlot of eventSlots.values()) {
|
|
|
|
eventSlot.schedule = schedule;
|
|
|
|
}
|
|
|
|
for (const shift of shifts.values()) {
|
|
|
|
shift.schedule = schedule;
|
|
|
|
}
|
|
|
|
for (const shiftSlot of shiftSlots.values()) {
|
|
|
|
shiftSlot.schedule = schedule;
|
|
|
|
}
|
|
|
|
return schedule;
|
2025-06-12 21:45:34 +02:00
|
|
|
}
|
|
|
|
|
2025-06-24 15:19:11 +02:00
|
|
|
function fixtureApiSchedule(): ApiSchedule {
|
2025-06-12 21:45:34 +02:00
|
|
|
return {
|
|
|
|
id: 111,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
locations: [
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "Left",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 2,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "Right",
|
|
|
|
description: "This is the right place",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
events: [
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "Up",
|
|
|
|
description: "What's Up?",
|
|
|
|
slots: [{
|
|
|
|
id: 1,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 1 })),
|
|
|
|
locationIds: [1],
|
|
|
|
}],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 2,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "Down",
|
|
|
|
slots: [{
|
|
|
|
id: 2,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 2 })),
|
|
|
|
locationIds: [2],
|
|
|
|
}],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
roles: [
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "Red",
|
|
|
|
description: "Is a color.",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 2,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "Blue",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
shifts: [
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "White",
|
|
|
|
roleId: 1,
|
|
|
|
slots: [{
|
|
|
|
id: 1,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 1 })),
|
|
|
|
}],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 2,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "Black",
|
|
|
|
description: "Is dark.",
|
|
|
|
roleId: 2,
|
|
|
|
slots: [{
|
|
|
|
id: 2,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 2 })),
|
|
|
|
}],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
describe("class ClientSchedule", () => {
|
|
|
|
test("load from api", () => {
|
|
|
|
const schedule = ClientSchedule.fromApi(fixtureApiSchedule(), { zone, locale })
|
|
|
|
expect(schedule).toStrictEqual(fixtureClientSchedule());
|
|
|
|
});
|
|
|
|
|
|
|
|
test("save to api", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
expect(schedule.toApi(false)).toEqual(fixtureApiSchedule())
|
|
|
|
});
|
|
|
|
|
2025-06-24 15:19:11 +02:00
|
|
|
const entityTests: [string, (schedule: ClientSchedule) => ClientEntity<ApiEntity>][] = [
|
2025-06-13 20:49:04 +02:00
|
|
|
[
|
|
|
|
"location",
|
2025-06-23 22:46:39 +02:00
|
|
|
() => ClientScheduleLocation.create(3, "New location", "", { zone, locale })
|
2025-06-13 20:49:04 +02:00
|
|
|
],
|
|
|
|
[
|
|
|
|
"event",
|
2025-06-23 22:46:39 +02:00
|
|
|
(schedule) => ClientScheduleEvent.create(schedule, 3, "New location", false, "", false, "", 0, new Set(), { zone, locale })
|
2025-06-13 20:49:04 +02:00
|
|
|
],
|
|
|
|
[
|
|
|
|
"role",
|
2025-06-23 22:46:39 +02:00
|
|
|
() => ClientScheduleRole.create(3, "New location", "", { zone, locale })
|
2025-06-13 20:49:04 +02:00
|
|
|
],
|
|
|
|
[
|
|
|
|
"shift",
|
2025-06-23 22:46:39 +02:00
|
|
|
(schedule) => ClientScheduleShift.create(schedule, 3, 1, "New location", "", new Set(), { zone, locale })
|
2025-06-13 20:49:04 +02:00
|
|
|
],
|
|
|
|
] as const;
|
|
|
|
for (const [name, create] of entityTests) {
|
|
|
|
describe(name, () => {
|
|
|
|
const Name = name[0].toUpperCase() + name.slice(1);
|
|
|
|
test(`create`, () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
const entity = create(schedule);
|
|
|
|
// Create
|
2025-06-23 22:46:39 +02:00
|
|
|
(schedule as any)[`${name}s`].add(entity);
|
2025-06-13 20:49:04 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
expect((schedule as any)[`${name}s`].get(entity.id).isModified()).toBe(true);
|
|
|
|
expect((schedule as any)[`${name}s`].get(entity.id).isNew()).toBe(true);
|
2025-06-13 20:49:04 +02:00
|
|
|
expect((schedule as any)[`${name}s`].get(entity.id)).toBe(entity);
|
|
|
|
});
|
|
|
|
test("edit", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
const entity = (schedule as any)[`${name}s`].get(1);
|
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(entity.isModified()).toBe(false);
|
|
|
|
const originalName = entity.name;
|
2025-06-13 20:49:04 +02:00
|
|
|
// Edit
|
2025-06-23 22:46:39 +02:00
|
|
|
entity.name = `Modified ${name}`;
|
2025-06-13 20:49:04 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
expect(entity.isModified()).toBe(true);
|
|
|
|
expect(entity.serverName).toBe(originalName);
|
2025-06-13 20:49:04 +02:00
|
|
|
});
|
|
|
|
if (name === "location") {
|
2025-06-23 22:46:39 +02:00
|
|
|
test.skip("delete location in use throws", () => {
|
2025-06-13 20:49:04 +02:00
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
expect(
|
2025-06-23 22:46:39 +02:00
|
|
|
() => { schedule.locations.get(1)!.deleted = true; }
|
2025-06-13 20:49:04 +02:00
|
|
|
).toThrow(new Error('Cannot delete location, event "Up" depends on it'));
|
|
|
|
});
|
|
|
|
} else if (name === "role") {
|
2025-06-23 22:46:39 +02:00
|
|
|
test.skip("delete role in use throws", () => {
|
2025-06-13 20:49:04 +02:00
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
expect(
|
2025-06-23 22:46:39 +02:00
|
|
|
() => { schedule.roles.get(1)!.deleted = true; }
|
2025-06-13 20:49:04 +02:00
|
|
|
).toThrow(new Error('Cannot delete role, shift "White" depends on it'));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
test("delete", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
const entity = (schedule as any)[`${name}s`].get(1);
|
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(entity.isModified()).toBe(false);
|
2025-06-13 20:49:04 +02:00
|
|
|
// Delete
|
|
|
|
if (name === "location") {
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.events.get(1)!.deleted = true;
|
2025-06-13 20:49:04 +02:00
|
|
|
} else if (name === "role") {
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.shifts.get(1)!.deleted = true;
|
2025-06-13 20:49:04 +02:00
|
|
|
}
|
2025-06-23 22:46:39 +02:00
|
|
|
entity.deleted = true;
|
2025-06-13 20:49:04 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
expect(entity.isModified()).toBe(true);
|
|
|
|
expect(entity.serverDeleted).toBe(false);
|
|
|
|
expect(entity.deleted).toBe(true);
|
2025-06-13 20:49:04 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2025-06-14 19:12:31 +02:00
|
|
|
|
|
|
|
describe("event slot", () => {
|
|
|
|
test("edit", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
|
|
|
// Modify
|
|
|
|
schedule.eventSlots.get(1)!.start = later;
|
|
|
|
// Check
|
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(1)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
|
|
|
});
|
|
|
|
test("add and apply", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
// Add
|
|
|
|
const slot = ClientScheduleEventSlot.create(
|
|
|
|
schedule,
|
|
|
|
3,
|
|
|
|
1,
|
|
|
|
now,
|
|
|
|
now.plus({ hours: 3 }),
|
|
|
|
new Set(),
|
|
|
|
new Set(),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
schedule.eventSlots.set(slot.id, slot);
|
|
|
|
schedule.events.get(1)!.slotIds.add(slot.id);
|
|
|
|
// Sanity check
|
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
// Apply change
|
|
|
|
schedule.apiUpdate({
|
|
|
|
id: 111,
|
|
|
|
updatedAt: laterIso,
|
|
|
|
events: [{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "Up",
|
|
|
|
description: "What's Up?",
|
|
|
|
slots: [
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 1 })),
|
|
|
|
locationIds: [1],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 3,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 3 })),
|
|
|
|
locationIds: [],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}],
|
|
|
|
}, { zone, locale });
|
|
|
|
// Check
|
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(3)!.isNewEntity).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(2);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
|
|
|
});
|
|
|
|
test("edit and apply", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-14 19:12:31 +02:00
|
|
|
// Modify
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.eventSlots.get(1)!.locationIds.add(2);
|
|
|
|
schedule.eventSlots.get(1)!.locationIds.delete(1);
|
|
|
|
// Sanity check
|
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
// Apply change
|
|
|
|
schedule.apiUpdate({
|
|
|
|
id: 111,
|
|
|
|
updatedAt: laterIso,
|
|
|
|
events: [{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: laterIso,
|
|
|
|
name: "Up",
|
|
|
|
description: "What's Up?",
|
|
|
|
slots: [{
|
|
|
|
id: 1,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 1 })),
|
|
|
|
locationIds: [2],
|
|
|
|
}],
|
|
|
|
}],
|
|
|
|
}, { zone, locale });
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(1)!.locationIds).toEqual(new Set([2]));
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
2025-06-23 22:46:39 +02:00
|
|
|
});
|
|
|
|
test("delete and apply", () => {
|
|
|
|
const schedule = fixtureClientSchedule(true);
|
|
|
|
// delete
|
|
|
|
schedule.eventSlots.get(1)!.deleted = true;
|
|
|
|
// Sanity check
|
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
// Apply change
|
|
|
|
schedule.apiUpdate({
|
|
|
|
id: 111,
|
|
|
|
updatedAt: laterIso,
|
|
|
|
events: [{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: laterIso,
|
|
|
|
name: "Up",
|
|
|
|
description: "What's Up?",
|
|
|
|
slots: [{
|
|
|
|
id: 2,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 2 })),
|
|
|
|
locationIds: [2],
|
|
|
|
}],
|
|
|
|
}],
|
|
|
|
}, { zone, locale });
|
|
|
|
// Check
|
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.has(1)).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(0);
|
2025-06-14 19:12:31 +02:00
|
|
|
});
|
|
|
|
test("move to another event", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
// Modify
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.eventSlots.get(1)!.setEventId(2);
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.eventSlots.get(1)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(0);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(2);
|
|
|
|
// Move back
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.eventSlots.get(1)!.setEventId(1);
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
|
|
|
});
|
2025-06-23 22:46:39 +02:00
|
|
|
test("discard", () => {
|
2025-06-14 19:12:31 +02:00
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.eventSlots.get(1)!.start = later;
|
|
|
|
schedule.eventSlots.get(1)!.discard();
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
|
|
|
});
|
2025-06-23 22:46:39 +02:00
|
|
|
test("discard from another event", () => {
|
2025-06-14 19:12:31 +02:00
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.eventSlots.get(1)!.setEventId(2);
|
|
|
|
schedule.eventSlots.get(1)!.discard();
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.events.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.eventSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("shift slot", () => {
|
|
|
|
test("edit", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
// Modify
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.shiftSlots.get(1)!.start = later;
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
2025-06-23 22:46:39 +02:00
|
|
|
});
|
|
|
|
test("add and apply", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
// Add
|
|
|
|
const slot = ClientScheduleShiftSlot.create(
|
|
|
|
schedule,
|
|
|
|
3,
|
|
|
|
1,
|
|
|
|
now,
|
|
|
|
now.plus({ hours: 3 }),
|
|
|
|
new Set(),
|
|
|
|
);
|
|
|
|
schedule.shiftSlots.set(slot.id, slot);
|
|
|
|
schedule.shifts.get(1)!.slotIds.add(slot.id);
|
|
|
|
// Sanity check
|
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
// Apply change
|
|
|
|
schedule.apiUpdate({
|
|
|
|
id: 111,
|
|
|
|
updatedAt: laterIso,
|
|
|
|
shifts: [{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "White",
|
|
|
|
roleId: 1,
|
|
|
|
slots: [
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 1 })),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 3,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 3 })),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}],
|
|
|
|
}, { zone, locale });
|
|
|
|
// Check
|
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(3)!.isNewEntity).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(2);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
|
|
|
});
|
|
|
|
test("edit and apply", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
// Modify
|
|
|
|
schedule.shiftSlots.get(1)!.assigned.add(2);
|
|
|
|
// Sanity check
|
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
// Apply change
|
|
|
|
schedule.apiUpdate({
|
|
|
|
id: 111,
|
|
|
|
updatedAt: laterIso,
|
|
|
|
shifts: [{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "White",
|
|
|
|
roleId: 1,
|
|
|
|
slots: [{
|
|
|
|
id: 1,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 1 })),
|
|
|
|
assigned: [2],
|
|
|
|
}],
|
|
|
|
}],
|
|
|
|
}, { zone, locale });
|
|
|
|
// Check
|
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.assigned).toEqual(new Set([2]));
|
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
});
|
|
|
|
test("delete and apply", () => {
|
|
|
|
const schedule = fixtureClientSchedule(true);
|
|
|
|
// delete
|
|
|
|
schedule.shiftSlots.get(1)!.deleted = true;
|
|
|
|
// Sanity check
|
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
// Apply change
|
|
|
|
schedule.apiUpdate({
|
|
|
|
id: 111,
|
|
|
|
updatedAt: laterIso,
|
|
|
|
shifts: [{
|
|
|
|
id: 1,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
name: "White",
|
|
|
|
roleId: 1,
|
|
|
|
slots: [{
|
|
|
|
id: 2,
|
|
|
|
start: nowIso,
|
|
|
|
end: toIso(now.plus({ hours: 2 })),
|
|
|
|
}],
|
|
|
|
}],
|
|
|
|
}, { zone, locale });
|
|
|
|
// Check
|
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.has(1)).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(0);
|
2025-06-14 19:12:31 +02:00
|
|
|
});
|
|
|
|
test("move to another shift", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
// Modify
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.shiftSlots.get(1)!.setShiftId(2);
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(true);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.isModified()).toBe(true);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(0);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(2);
|
|
|
|
// Move back
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.shiftSlots.get(1)!.setShiftId(1);
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
|
|
|
});
|
2025-06-23 22:46:39 +02:00
|
|
|
test("discard", () => {
|
2025-06-14 19:12:31 +02:00
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.shiftSlots.get(1)!.start = later;
|
|
|
|
schedule.shiftSlots.get(1)!.discard();
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
|
|
|
});
|
2025-06-23 22:46:39 +02:00
|
|
|
test("discard from another shift", () => {
|
2025-06-14 19:12:31 +02:00
|
|
|
const schedule = fixtureClientSchedule();
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.shiftSlots.get(1)!.setShiftId(2);
|
|
|
|
schedule.shiftSlots.get(1)!.discard();
|
2025-06-14 19:12:31 +02:00
|
|
|
// Check
|
2025-06-23 22:46:39 +02:00
|
|
|
expect(schedule.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shifts.get(2)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(1)!.isModified()).toBe(false);
|
|
|
|
expect(schedule.shiftSlots.get(2)!.isModified()).toBe(false);
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
|
|
|
});
|
|
|
|
});
|
2025-06-12 21:45:34 +02:00
|
|
|
});
|