2025-06-23 12:48:09 +02:00
|
|
|
import { ClientEntity, ClientSchedule, ClientScheduleEventSlot, ClientScheduleLocation, ClientScheduleShiftSlot, toIso } from "~/utils/client-schedule";
|
2025-06-12 21:45:34 +02:00
|
|
|
import { describe, expect, test } from "vitest";
|
|
|
|
import type { ApiSchedule } from "~/shared/types/api";
|
2025-06-14 19:12:31 +02:00
|
|
|
import type { Id, Living } from "~/shared/types/common";
|
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();
|
|
|
|
|
|
|
|
function fixtureClientSchedule() {
|
|
|
|
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(
|
|
|
|
1, now, false, "Up", false, "", false, "What's Up?", 0,
|
2025-06-14 19:12:31 +02:00
|
|
|
idMap([new ClientScheduleEventSlot(1, false, 1, now, now.plus({ hours: 1 }), [left], new Set(), 0)]),
|
2025-06-12 21:45:34 +02:00
|
|
|
),
|
|
|
|
new ClientScheduleEvent(
|
|
|
|
2, now, false, "Down", false, "", false, "", 0,
|
2025-06-14 19:12:31 +02:00
|
|
|
idMap([new ClientScheduleEventSlot(2, false, 2, now, now.plus({ hours: 2 }), [right], 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(
|
|
|
|
1, now, false, red, "White", "",
|
2025-06-14 19:12:31 +02:00
|
|
|
idMap([new ClientScheduleShiftSlot(1, false, 1, now, now.plus({ hours: 1 }), new Set())]),
|
2025-06-12 21:45:34 +02:00
|
|
|
),
|
|
|
|
new ClientScheduleShift(
|
|
|
|
2, now, false, blue, "Black", "Is dark.",
|
2025-06-14 19:12:31 +02:00
|
|
|
idMap([new ClientScheduleShiftSlot(2, false, 2, now, now.plus({ hours: 2 }), new Set())]),
|
2025-06-12 21:45:34 +02:00
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
return new ClientSchedule(
|
|
|
|
111,
|
|
|
|
now,
|
|
|
|
false,
|
|
|
|
new Map([
|
|
|
|
[left.id, left],
|
|
|
|
[right.id, right],
|
|
|
|
]),
|
|
|
|
new Map(events.map(event => [event.id, event])),
|
|
|
|
new Map([
|
|
|
|
[red.id, red],
|
|
|
|
[blue.id, blue],
|
|
|
|
]),
|
|
|
|
new Map(shifts.map(shift => [shift.id, shift])),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function fixtureApiSchedule(): Living<ApiSchedule> {
|
|
|
|
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())
|
|
|
|
});
|
|
|
|
|
|
|
|
const updatePatterns = [
|
|
|
|
"aa a aa",
|
|
|
|
"ba a aa",
|
|
|
|
"-a a aa",
|
|
|
|
"ab a ab",
|
|
|
|
"bb a aa",
|
|
|
|
"-b a ab",
|
|
|
|
"ax a ax",
|
|
|
|
"bx a ax",
|
|
|
|
"-- a aa",
|
|
|
|
"-x a ax",
|
|
|
|
"aa x --",
|
|
|
|
"ba x -a",
|
|
|
|
"-a x -a",
|
|
|
|
"ab x -b",
|
|
|
|
"bb x --",
|
|
|
|
"-b x -b",
|
|
|
|
"ax x --",
|
|
|
|
"bx x --",
|
|
|
|
"-x x --",
|
|
|
|
"-- x --",
|
|
|
|
];
|
|
|
|
for (const pattern of updatePatterns) {
|
|
|
|
test(`apply diff pattern ${pattern}`, () => {
|
2025-06-13 21:06:48 +02:00
|
|
|
const fixtureClient: Record<string, ClientScheduleLocation> = {
|
2025-06-12 21:45:34 +02:00
|
|
|
a: new ClientScheduleLocation(1, now, false, "A", ""),
|
|
|
|
b: new ClientScheduleLocation(1, now, false, "B", ""),
|
|
|
|
x: new ClientScheduleLocation(1, now, true, "X", ""),
|
|
|
|
};
|
2025-06-13 21:06:48 +02:00
|
|
|
const fixtureServer: Record<string, ClientScheduleLocation> = {
|
|
|
|
a: new ClientScheduleLocation(1, later, false, "A", ""),
|
|
|
|
b: new ClientScheduleLocation(1, later, false, "B", ""),
|
|
|
|
x: new ClientScheduleLocation(1, later, true, "X", ""),
|
|
|
|
};
|
2025-06-12 21:45:34 +02:00
|
|
|
const schedule = new ClientSchedule(111, now, false, new Map(), new Map(), new Map(), new Map());
|
2025-06-13 21:06:48 +02:00
|
|
|
if (fixtureClient[pattern[0]])
|
|
|
|
schedule.originalLocations.set(1, fixtureClient[pattern[0]]);
|
|
|
|
if (fixtureClient[pattern[1]])
|
|
|
|
schedule.locations.set(1, fixtureClient[pattern[1]]);
|
|
|
|
const update = fixtureServer[pattern[3]];
|
|
|
|
const expectedOriginalLocation = pattern[5] === "x" ? undefined : fixtureServer[pattern[5]];
|
|
|
|
const expectedLocation = pattern[5] === pattern[6] ? fixtureServer[pattern[6]] : fixtureClient[pattern[6]];
|
2025-06-12 21:45:34 +02:00
|
|
|
|
|
|
|
schedule.applyUpdate({
|
|
|
|
id: 111,
|
|
|
|
updatedAt: nowIso,
|
|
|
|
locations: [update.toApi()],
|
|
|
|
}, { zone, locale });
|
|
|
|
expect(schedule.originalLocations.get(1)).toEqual(expectedOriginalLocation);
|
|
|
|
expect(schedule.locations.get(1)).toEqual(expectedLocation);
|
|
|
|
if (pattern.slice(5) === "aa")
|
|
|
|
expect(schedule.originalLocations.get(1)).toBe(schedule.locations.get(1));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2025-06-13 20:49:04 +02:00
|
|
|
const entityTests: [string, (schedule: ClientSchedule) => ClientEntity][] = [
|
|
|
|
[
|
|
|
|
"location",
|
|
|
|
() => new ClientScheduleLocation(3, now, false, "New location", "")
|
|
|
|
],
|
|
|
|
[
|
|
|
|
"event",
|
2025-06-14 19:12:31 +02:00
|
|
|
() => new ClientScheduleEvent(3, now, false, "New location", false, "", false, "", 0, new Map())
|
2025-06-13 20:49:04 +02:00
|
|
|
],
|
|
|
|
[
|
|
|
|
"role",
|
|
|
|
() => new ClientScheduleRole(3, now, false, "New location", "")
|
|
|
|
],
|
|
|
|
[
|
|
|
|
"shift",
|
2025-06-14 19:12:31 +02:00
|
|
|
(schedule) => new ClientScheduleShift(3, now, false, schedule.roles.get(1)!, "New location", "", new Map())
|
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);
|
|
|
|
expect(schedule.modified).toBe(false);
|
|
|
|
// Create
|
|
|
|
(schedule as any)[`set${Name}`](entity);
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect((schedule as any)[`isModified${Name}`](entity.id)).toBe(true);
|
|
|
|
expect((schedule as any)[`original${Name}s`].get(entity.id)).toBe(undefined);
|
|
|
|
expect((schedule as any)[`${name}s`].get(entity.id)).toBe(entity);
|
|
|
|
});
|
|
|
|
test("edit", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
const original = (schedule as any)[`${name}s`].get(1);
|
|
|
|
expect(schedule.modified).toBe(false);
|
|
|
|
expect((schedule as any)[`isModified${Name}`](1)).toBe(false);
|
|
|
|
// Edit
|
|
|
|
(schedule as any)[`edit${Name}`](original, { name: `Modified ${name}` })
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect((schedule as any)[`isModified${Name}`](1)).toBe(true);
|
|
|
|
expect((schedule as any)[`original${Name}s`].get(1)).toBe(original);
|
|
|
|
if (name === "location") {
|
2025-06-14 19:12:31 +02:00
|
|
|
expect(schedule.events.get(1)!.slots.get(1)!.locations[0]).toBe(schedule.locations.get(1));
|
2025-06-13 20:49:04 +02:00
|
|
|
} else if (name === "role") {
|
|
|
|
expect(schedule.shifts.get(1)!.role).toBe(schedule.roles.get(1));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (name === "location") {
|
|
|
|
test("delete location in use throws", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
expect(
|
|
|
|
() => { schedule.editLocation(schedule.locations.get(1)!, { deleted: true }); }
|
|
|
|
).toThrow(new Error('Cannot delete location, event "Up" depends on it'));
|
|
|
|
});
|
|
|
|
} else if (name === "role") {
|
|
|
|
test("delete role in use throws", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
expect(
|
|
|
|
() => { schedule.editRole(schedule.roles.get(1)!, { deleted: true }); }
|
|
|
|
).toThrow(new Error('Cannot delete role, shift "White" depends on it'));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
test("delete", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
const original = (schedule as any)[`${name}s`].get(1);
|
|
|
|
expect(schedule.modified).toBe(false);
|
|
|
|
expect((schedule as any)[`isModified${Name}`](1)).toBe(false);
|
|
|
|
// Delete
|
|
|
|
if (name === "location") {
|
|
|
|
schedule.editEvent(schedule.events.get(1)!, { deleted: true });
|
|
|
|
} else if (name === "role") {
|
|
|
|
schedule.editShift(schedule.shifts.get(1)!, { deleted: true });
|
|
|
|
}
|
|
|
|
(schedule as any)[`edit${Name}`](original, { deleted: true })
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect((schedule as any)[`isModified${Name}`](1)).toBe(true);
|
|
|
|
expect((schedule as any)[`original${Name}s`].get(1)).toBe(original);
|
|
|
|
expect((schedule as any)[`${name}s`].get(1).deleted).toBe(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2025-06-14 19:12:31 +02:00
|
|
|
|
|
|
|
function validateSlotRelations(
|
|
|
|
slots: Map<Id, ClientScheduleShiftSlot> | Map<Id, ClientScheduleEventSlot>,
|
|
|
|
entites: Map<Id, ClientScheduleShift> | Map<Id, ClientScheduleEvent>,
|
|
|
|
prefix: string,
|
|
|
|
) {
|
|
|
|
const remainingIds = new Set(slots.keys())
|
|
|
|
for (const shift of entites.values()) {
|
|
|
|
for (const slot of shift.slots.values()) {
|
|
|
|
if (!slots.has(slot.id)) {
|
|
|
|
throw Error(`${prefix}: shift ${shift.name}:${shift.id} has slot ${slot.id} that is not in shiftSlots`);
|
|
|
|
}
|
|
|
|
if (slots.get(slot.id) !== slot) {
|
|
|
|
throw Error(`${prefix}: shift ${shift.name}:${shift.id} has slot ${slot.id} which does not match the corresponding slot in shiftSlots.`);
|
|
|
|
}
|
|
|
|
if (!remainingIds.has(slot.id)) {
|
|
|
|
throw Error(`${prefix}: shift ${shift.name}:${shift.id} has slot ${slot.id} that has been seen twice.`);
|
|
|
|
}
|
|
|
|
remainingIds.delete(slot.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (remainingIds.size) {
|
|
|
|
throw Error(`${prefix}: shiftSlots ${[...remainingIds].join(", ")} does not have a corresponding shift`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
describe("event slot", () => {
|
|
|
|
test("edit", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
expect(schedule.modified).toBe(false);
|
|
|
|
expect(schedule.isModifiedEvent(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEvent(2)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEventSlot(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
|
|
|
// Modify
|
|
|
|
schedule.editEventSlot(schedule.eventSlots.get(1)!, { start: later });
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(2)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEventSlot(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
|
|
|
});
|
|
|
|
test("move to another event", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
expect(schedule.modified).toBe(false);
|
|
|
|
expect(schedule.isModifiedEvent(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEvent(2)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEventSlot(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
|
|
|
// Modify
|
|
|
|
schedule.editEventSlot(schedule.eventSlots.get(1)!, { eventId: 2 });
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(2)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEventSlot(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(0);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(2);
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
|
|
|
// Move back
|
|
|
|
schedule.editEventSlot(schedule.eventSlots.get(1)!, { eventId: 1 });
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(2)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEventSlot(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
|
|
|
});
|
|
|
|
test("restore", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
schedule.editEventSlot(schedule.eventSlots.get(1)!, { start: later });
|
|
|
|
schedule.restoreEventSlot(1);
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(2)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEventSlot(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
|
|
|
});
|
|
|
|
test("restore from another event", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
schedule.editEventSlot(schedule.eventSlots.get(1)!, { eventId: 2 });
|
|
|
|
schedule.restoreEventSlot(1);
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEvent(2)).toBe(true);
|
|
|
|
expect(schedule.isModifiedEventSlot(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
|
|
|
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
|
|
|
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("shift slot", () => {
|
|
|
|
test("edit", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
expect(schedule.modified).toBe(false);
|
|
|
|
expect(schedule.isModifiedShift(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShift(2)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShiftSlot(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
|
|
|
// Modify
|
|
|
|
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { start: later });
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(2)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShiftSlot(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
|
|
|
});
|
|
|
|
test("move to another shift", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
expect(schedule.modified).toBe(false);
|
|
|
|
expect(schedule.isModifiedShift(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShift(2)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShiftSlot(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
|
|
|
// Modify
|
|
|
|
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { shiftId: 2 });
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(2)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShiftSlot(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(0);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(2);
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
|
|
|
// Move back
|
|
|
|
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { shiftId: 1 });
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(2)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShiftSlot(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
|
|
|
});
|
|
|
|
test("restore", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { start: later });
|
|
|
|
schedule.restoreShiftSlot(1);
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(2)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShiftSlot(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
|
|
|
});
|
|
|
|
test("restore from another shift", () => {
|
|
|
|
const schedule = fixtureClientSchedule();
|
|
|
|
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { shiftId: 2 });
|
|
|
|
schedule.restoreShiftSlot(1);
|
|
|
|
// Check
|
|
|
|
expect(schedule.modified).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(1)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShift(2)).toBe(true);
|
|
|
|
expect(schedule.isModifiedShiftSlot(1)).toBe(false);
|
|
|
|
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
|
|
|
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
|
|
|
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
|
|
|
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
|
|
|
});
|
|
|
|
});
|
2025-06-12 21:45:34 +02:00
|
|
|
});
|