owltide/utils/client-schedule.nuxt.test.ts

285 lines
7.5 KiB
TypeScript
Raw Normal View History

import { ClientSchedule, ClientScheduleEventSlot, ClientScheduleLocation, toIso } from "./client-schedule";
import { describe, expect, test } from "vitest";
import type { ApiSchedule } from "~/shared/types/api";
import type { Living } from "~/shared/types/common";
import { DateTime, FixedOffsetZone } from "~/shared/utils/luxon";
const locale = "en-GB";
const now = DateTime.now().setLocale(locale);
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,
[new ClientScheduleEventSlot(1, now, now.plus({ hours: 1 }), [left], new Set(), 0)],
),
new ClientScheduleEvent(
2, now, false, "Down", false, "", false, "", 0,
[new ClientScheduleEventSlot(2, now, now.plus({ hours: 2 }), [right], new Set(), 0)],
),
];
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", "",
[new ClientScheduleShiftSlot(1, now, now.plus({ hours: 1 }), new Set())],
),
new ClientScheduleShift(
2, now, false, blue, "Black", "Is dark.",
[new ClientScheduleShiftSlot(2, now, now.plus({ hours: 2 }), new Set())],
),
];
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}`, () => {
const fixture: Record<string, ClientScheduleLocation> = {
a: new ClientScheduleLocation(1, now, false, "A", ""),
b: new ClientScheduleLocation(1, now, false, "B", ""),
x: new ClientScheduleLocation(1, now, true, "X", ""),
};
const schedule = new ClientSchedule(111, now, false, new Map(), new Map(), new Map(), new Map());
if (fixture[pattern[0]])
schedule.originalLocations.set(1, fixture[pattern[0]]);
if (fixture[pattern[1]])
schedule.locations.set(1, fixture[pattern[1]]);
const update = fixture[pattern[3]];
const expectedOriginalLocation = pattern[5] === "x" ? undefined : fixture[pattern[5]];
const expectedLocation = fixture[pattern[6]];
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));
});
}
test("create location", () => {
const schedule = fixtureClientSchedule();
const location = new ClientScheduleLocation(3, now, false, "New location", "");
schedule.setLocation(location);
expect(schedule.originalLocations.get(3)).toBe(undefined);
expect(schedule.locations.get(3)).toBe(location);
});
test("update location", () => {
const schedule = fixtureClientSchedule();
const original = schedule.locations.get(1)!;
const copy = original.clone();
copy.name = "Modified Location";
schedule.setLocation(copy);
expect(schedule.originalLocations.get(1)).toBe(original);
expect(schedule.locations.get(1)).toBe(copy);
expect(schedule.events.get(1)!.slots[0].locations[0]).toBe(copy);
});
test("delete location in use throws", () => {
const schedule = fixtureClientSchedule();
const original = schedule.locations.get(1)!;
const copy = original.clone();
copy.deleted = true;
expect(
() => { schedule.setLocation(copy); }
).toThrow(new Error('Cannot delete location, event "Up" depends on it'));
});
test("delete location", () => {
const schedule = fixtureClientSchedule();
const event = schedule.events.get(1)!.clone();
event.slots = [];
schedule.setEvent(event);
const original = schedule.locations.get(1)!;
const copy = original.clone();
copy.deleted = true;
schedule.setLocation(copy);
expect(schedule.originalLocations.get(1)).toBe(original);
expect(schedule.locations.get(1)).toBe(copy);
});
test("create role", () => {
const schedule = fixtureClientSchedule();
const role = new ClientScheduleRole(3, now, false, "New role", "");
schedule.setRole(role);
expect(schedule.originalRoles.get(3)).toBe(undefined);
expect(schedule.roles.get(3)).toBe(role);
});
test("update role", () => {
const schedule = fixtureClientSchedule();
const original = schedule.roles.get(1)!;
const copy = original.clone();
copy.name = "Modified Role";
schedule.setRole(copy);
expect(schedule.originalRoles.get(1)).toBe(original);
expect(schedule.roles.get(1)).toBe(copy);
expect(schedule.shifts.get(1)!.role).toBe(copy);
});
test("delete role in use throws", () => {
const schedule = fixtureClientSchedule();
const original = schedule.roles.get(1)!;
const copy = original.clone();
copy.deleted = true;
expect(
() => { schedule.setRole(copy); }
).toThrow(new Error('Cannot delete role, shift "White" depends on it'));
});
test("delete role", () => {
const schedule = fixtureClientSchedule();
const shift = schedule.shifts.get(1)!.clone();
shift.role = schedule.roles.get(2)!;
schedule.setShift(shift);
const original = schedule.roles.get(1)!;
const copy = original.clone();
copy.deleted = true;
schedule.setRole(copy);
expect(schedule.originalRoles.get(1)).toBe(original);
expect(schedule.roles.get(1)).toBe(copy);
});
});