Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/modules/events/utils/EventUtils.test.ts
T

399 lines
11 KiB
TypeScript

// @vitest-environment node
import { describe, expect, it } from "vitest";
import EventUtils from "./EventUtils";
import type { TFunction } from "i18next";
function createTranslationSpy() {
const calls: Array<{ key: string; values: Record<string, unknown> }> = [];
const t = ((key: string, values: Record<string, unknown>) => {
calls.push({ key, values });
return `translated:${key}`;
}) as TFunction;
return { calls, t };
}
function stripDefaultValues(calls: Array<{ key: string; values: Record<string, unknown> }>) {
return calls.map(({ key, values }) => {
const { defaultValue: _defaultValue, ...rest } = values;
return { key, values: rest };
});
}
describe("EventUtils user preference descriptions", () => {
it("describes locale preference updates", () => {
expect(
EventUtils.getDescription("user.preferences.updated", {
old: { locale: "en" },
new: { locale: "de" },
user: { id: "user-1", email: "user@example.com" },
})
).toBe("user@example.com updated their language: en -> de");
});
it("translates locale preference updates when a translator is provided", () => {
const { calls, t } = createTranslationSpy();
expect(
EventUtils.getDescription(
"user.preferences.updated",
{
old: { locale: "en" },
new: { locale: "de" },
user: { id: "user-1", email: "user@example.com" },
},
t
)
).toBe("translated:events.descriptions.userPreferences.localeUpdated");
expect(stripDefaultValues(calls)).toEqual([
{
key: "shared.locales.en",
values: {},
},
{
key: "shared.locales.de",
values: {},
},
{
key: "events.descriptions.userPreferences.localeUpdated",
values: {
email: "user@example.com",
newLocale: "translated:shared.locales.de",
oldLocale: "translated:shared.locales.en",
},
},
]);
});
it("describes light/dark mode preference updates", () => {
expect(
EventUtils.getDescription("user.preferences.updated", {
old: { lightOrDarkMode: "light" },
new: { lightOrDarkMode: "dark" },
user: { id: "user-1", email: "user@example.com" },
})
).toBe("user@example.com updated their theme mode: light -> dark");
});
it("translates theme preference updates with light as the default mode", () => {
const { calls, t } = createTranslationSpy();
expect(
EventUtils.getDescription(
"user.preferences.updated",
{
old: {},
new: { lightOrDarkMode: "dark" },
user: { id: "user-1", email: "user@example.com" },
},
t
)
).toBe("translated:events.descriptions.userPreferences.themeUpdated");
expect(stripDefaultValues(calls)).toEqual([
{
key: "shared.light",
values: {},
},
{
key: "shared.dark",
values: {},
},
{
key: "events.descriptions.userPreferences.themeUpdated",
values: {
email: "user@example.com",
newTheme: "translated:shared.dark",
oldTheme: "translated:shared.light",
},
},
]);
});
it("translates generic preference updates", () => {
const { calls, t } = createTranslationSpy();
expect(
EventUtils.getDescription(
"user.preferences.updated",
{
old: {},
new: {},
user: { id: "user-1", email: "user@example.com" },
},
t
)
).toBe("translated:events.descriptions.userPreferences.updated");
expect(stripDefaultValues(calls)).toEqual([
{
key: "events.descriptions.userPreferences.updated",
values: {
email: "user@example.com",
},
},
]);
});
});
describe("EventUtils translated event descriptions", () => {
it("passes readable default values to translated event descriptions", () => {
const { calls, t } = createTranslationSpy();
EventUtils.getDescription(
"account.created",
{
tenant: { id: "tenant-1", name: "Acme" },
},
t
);
expect(calls).toEqual([
{
key: "events.descriptions.account.created",
values: {
defaultValue: "Account created: Acme",
tenant: "Acme",
},
},
]);
});
it("translates unmapped event names with the raw name as fallback", () => {
const { calls, t } = createTranslationSpy();
expect(EventUtils.parseName("provider.webhook.delivered", t)).toBe("translated:events.names.unmapped");
expect(EventUtils.parseName("provider.webhook.delivered")).toBe("provider.webhook.delivered");
expect(calls).toEqual([
{
key: "events.names.unmapped",
values: {
defaultValue: "provider.webhook.delivered",
name: "provider.webhook.delivered",
},
},
]);
});
it("recomputes persisted descriptions with the active translator", () => {
const { calls, t } = createTranslationSpy();
expect(
EventUtils.parseDescription(
{
id: "event-1",
tenantId: "tenant-1",
userId: "user-1",
name: "account.created",
data: JSON.stringify({ tenant: { id: "tenant-1", name: "Acme" } }),
description: "Account created: Acme",
createdAt: new Date("2026-05-08T10:00:00.000Z"),
},
t
)
).toBe("translated:events.descriptions.account.created");
expect(stripDefaultValues(calls)).toEqual([
{
key: "events.descriptions.account.created",
values: {
tenant: "Acme",
},
},
]);
});
it("falls back to persisted descriptions when event payloads cannot be parsed", () => {
const { t } = createTranslationSpy();
expect(
EventUtils.parseDescription(
{
id: "event-1",
tenantId: "tenant-1",
userId: "user-1",
name: "account.created",
data: "{",
description: "Persisted fallback",
createdAt: new Date("2026-05-08T10:00:00.000Z"),
},
t
)
).toBe("Persisted fallback");
});
it("uses the unknown event translation key when an unmapped payload has no persisted description", () => {
const { calls, t } = createTranslationSpy();
expect(
EventUtils.parseDescription(
{
id: "event-1",
tenantId: "tenant-1",
userId: "user-1",
name: "provider.webhook.delivered",
data: "{",
description: null,
createdAt: new Date("2026-05-08T10:00:00.000Z"),
},
t
)
).toBe("translated:events.descriptions.unknownWithName");
expect(calls).toEqual([
{
key: "events.descriptions.unknownWithName",
values: {
defaultValue: "Unknown event: provider.webhook.delivered",
name: "provider.webhook.delivered",
},
},
]);
expect(
EventUtils.parseDescription({
id: "event-1",
tenantId: "tenant-1",
userId: "user-1",
name: "provider.webhook.delivered",
data: "{",
description: null,
createdAt: new Date("2026-05-08T10:00:00.000Z"),
})
).toBe("Unknown event: provider.webhook.delivered");
});
it("translates account descriptions", () => {
const { calls, t } = createTranslationSpy();
expect(
EventUtils.getDescription(
"account.created",
{
tenant: { id: "tenant-1", name: "Acme" },
},
t
)
).toBe("translated:events.descriptions.account.created");
expect(stripDefaultValues(calls)).toEqual([
{
key: "events.descriptions.account.created",
values: {
tenant: "Acme",
},
},
]);
});
it("translates new-user invitation prefixes before composing accepted invitation descriptions", () => {
const calls: Array<{ key: string; values: Record<string, unknown> }> = [];
const t = ((key: string, values: Record<string, unknown>) => {
calls.push({ key, values });
if (key === "events.descriptions.memberInvitation.newUserPrefix") return "translated-prefix ";
return `translated:${key}`;
}) as TFunction;
expect(
EventUtils.getDescription(
"member.invitation.accepted",
{
newUser: true,
user: { id: "user-1", email: "ada@example.com" },
tenant: { id: "tenant-1", name: "Acme" },
},
t
)
).toBe("translated:events.descriptions.memberInvitation.accepted");
expect(stripDefaultValues(calls)).toEqual([
{
key: "events.descriptions.memberInvitation.newUserPrefix",
values: {},
},
{
key: "events.descriptions.memberInvitation.accepted",
values: {
email: "ada@example.com",
prefix: "translated-prefix ",
tenant: "Acme",
},
},
]);
});
it("translates row descriptions with actor values", () => {
const { calls, t } = createTranslationSpy();
expect(
EventUtils.getDescription(
"row.created",
{
entity: { id: "entity-1", name: "Contact" },
title: "Ada Lovelace",
user: { id: "user-1", email: "ada@example.com" },
},
t
)
).toBe("translated:events.descriptions.row.created");
expect(stripDefaultValues(calls)).toEqual([
{
key: "events.descriptions.row.created",
values: {
actor: "ada@example.com",
entity: "Contact",
title: "Ada Lovelace",
},
},
]);
});
it("translates row task actors and status fragments", () => {
const calls: Array<{ key: string; values: Record<string, unknown> }> = [];
const t = ((key: string, values: Record<string, unknown>) => {
calls.push({ key, values });
if (key === "events.actors.apiKey") return `api-key:${values.alias}`;
if (key === "events.descriptions.rowTasks.changes.completedAt") return `completed:${values.date}`;
return `translated:${key}`;
}) as TFunction;
expect(
EventUtils.getDescription(
"row.tasks.updated",
{
rowId: "row-1",
new: { name: "Follow up", completedAt: "2026-05-27T01:00:00.000Z" },
apiKey: { id: "key-1", alias: "Zapier" },
},
t,
{ dateTime: () => "May 27, 2026, 3:00 AM" }
)
).toBe("translated:events.descriptions.rowTasks.updated");
expect(stripDefaultValues(calls)).toEqual([
{
key: "events.descriptions.rowTasks.changes.completedAt",
values: {
date: "May 27, 2026, 3:00 AM",
},
},
{
key: "events.actors.apiKey",
values: {
alias: "Zapier",
},
},
{
key: "events.descriptions.rowTasks.updated",
values: {
actor: "api-key:Zapier",
change: "completed:May 27, 2026, 3:00 AM",
rowId: "row-1",
task: "Follow up",
},
},
]);
});
});