feat(ops): wall board härlett ur summary-värden
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildWall } from "../src/ops-summary.js";
|
||||
import type { OpsSummary } from "@app/shared-types";
|
||||
|
||||
function baseSummary(overrides: Partial<OpsSummary> = {}): OpsSummary {
|
||||
return {
|
||||
app: { app: "cibello", generated_at: new Date().toISOString() },
|
||||
ekonomi: { mrr: 0, intakt_24h: 0, intakt_7d: 0, valuta: "SEK" },
|
||||
anvandare: { aktiva_nu: 0, dau: 0, mau: 0, nya_24h: 0, nya_7d: 0 },
|
||||
prenumerationer: {
|
||||
trial_aktiva: 0,
|
||||
trial_konverterade_24h: 0,
|
||||
trial_konverterade_7d: 0,
|
||||
konverteringsgrad_30d: null,
|
||||
betalande: 0,
|
||||
avslutade_24h: 0,
|
||||
},
|
||||
butik: { butik: null, all_fields_phase_2: null },
|
||||
feedback: { oppna: 0, nya_24h: 0, senaste: null },
|
||||
ai_scan: {
|
||||
scans_24h: 0,
|
||||
scans_7d: 0,
|
||||
lyckandegrad_24h: null,
|
||||
latens_p50_ms: null,
|
||||
latens_p95_ms: null,
|
||||
felfrekvens_24h: null,
|
||||
senaste_fel: null,
|
||||
kostnad_usd_mikrocent_manad: 0,
|
||||
kostnad_usd_mikrocent_24h: null,
|
||||
kostnad_usd_mikrocent_24h_uppskattad: false,
|
||||
budget_andel: null,
|
||||
as_of: new Date().toISOString(),
|
||||
},
|
||||
aktivering: {
|
||||
scan_inom_24h_andel: null,
|
||||
lagad_maltid_inom_7d_andel: null,
|
||||
hushall_andra_medlem_aktiv_andel: null,
|
||||
retention_d1: null,
|
||||
retention_d7: null,
|
||||
retention_d30: null,
|
||||
},
|
||||
engagemang: {
|
||||
lagade_maltider_24h: 0,
|
||||
lagade_maltider_7d: 0,
|
||||
rek_ctr_7d: null,
|
||||
puffar_skickade_24h: 0,
|
||||
puffar_atgardsgrad_7d: null,
|
||||
},
|
||||
betalning: {
|
||||
failed_nu: 0,
|
||||
grace_period_nu: 0,
|
||||
trials_utgar_48h: 0,
|
||||
aterbetalningar_7d: null,
|
||||
chargebacks_7d: null,
|
||||
},
|
||||
jobb: {
|
||||
vantande: 0,
|
||||
aktiva: 0,
|
||||
misslyckade_24h: 0,
|
||||
aldsta_vantande_sek: null,
|
||||
workers_ok: true,
|
||||
},
|
||||
sakerhet: {
|
||||
allergen_invariant_brott: 0,
|
||||
overifierade_visade: 0,
|
||||
food_safety_lint_avvisade_7d: null,
|
||||
senaste_kontroll: null,
|
||||
},
|
||||
wall: { boards: [] },
|
||||
as_of: new Date().toISOString(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Wall-testet verifierar att buildWall är härlett uteslutande ur summary-värden.
|
||||
* Inga hårdkodade tal får förekomma.
|
||||
*/
|
||||
describe("buildWall", () => {
|
||||
it("formaterar MRR i öre till kr med mellanslag", () => {
|
||||
const summary = baseSummary({ ekonomi: { ...baseSummary().ekonomi, mrr: 1234567 } });
|
||||
const tile = buildWall(summary).boards[0]!.tiles.find((t) => t.label === "MRR")!;
|
||||
expect(tile.value).toBe("12 346 kr");
|
||||
expect(tile.tone).toBe("ok");
|
||||
});
|
||||
|
||||
it("MRR null blir — med warn", () => {
|
||||
const summary = baseSummary({ ekonomi: { ...baseSummary().ekonomi, mrr: null } });
|
||||
const tile = buildWall(summary).boards[0]!.tiles.find((t) => t.label === "MRR")!;
|
||||
expect(tile.value).toBe("—");
|
||||
expect(tile.tone).toBe("warn");
|
||||
});
|
||||
|
||||
it("aktiva nu och betalande visas som heltal", () => {
|
||||
const summary = baseSummary({
|
||||
anvandare: { ...baseSummary().anvandare, aktiva_nu: 42 },
|
||||
prenumerationer: { ...baseSummary().prenumerationer, betalande: 7 },
|
||||
});
|
||||
const tiles = buildWall(summary).boards[0]!.tiles;
|
||||
expect(tiles.find((t) => t.label === "Aktiva nu")!.value).toBe("42");
|
||||
expect(tiles.find((t) => t.label === "Betalande")!.value).toBe("7");
|
||||
});
|
||||
|
||||
it("trial-konvertering blir procent; null blir —/warn", () => {
|
||||
const withValue = baseSummary({
|
||||
prenumerationer: { ...baseSummary().prenumerationer, konverteringsgrad_30d: 0.3149 },
|
||||
});
|
||||
const tileValue = buildWall(withValue).boards[0]!.tiles.find((t) => t.label === "Trial-konv")!;
|
||||
expect(tileValue.value).toBe("31 %");
|
||||
expect(tileValue.tone).toBe("ok");
|
||||
|
||||
const withNull = baseSummary();
|
||||
const tileNull = buildWall(withNull).boards[0]!.tiles.find((t) => t.label === "Trial-konv")!;
|
||||
expect(tileNull.value).toBe("—");
|
||||
expect(tileNull.tone).toBe("warn");
|
||||
});
|
||||
|
||||
it("workers ok → OK; nere → NERE/warn", () => {
|
||||
const ok = buildWall(baseSummary()).boards[0]!.tiles.find((t) => t.label === "Workers")!;
|
||||
expect(ok.value).toBe("OK");
|
||||
expect(ok.tone).toBe("ok");
|
||||
|
||||
const nere = buildWall(baseSummary({ jobb: { ...baseSummary().jobb, workers_ok: false } })).boards[0]!.tiles.find(
|
||||
(t) => t.label === "Workers",
|
||||
)!;
|
||||
expect(nere.value).toBe("NERE");
|
||||
expect(nere.tone).toBe("warn");
|
||||
});
|
||||
|
||||
it("allergen-brott 0 är ok; >0 varnar", () => {
|
||||
const zero = buildWall(baseSummary()).boards[0]!.tiles.find((t) => t.label === "Allergen-brott")!;
|
||||
expect(zero.value).toBe("0");
|
||||
expect(zero.tone).toBe("ok");
|
||||
|
||||
const brott = buildWall(
|
||||
baseSummary({ sakerhet: { ...baseSummary().sakerhet, allergen_invariant_brott: 3 } }),
|
||||
).boards[0]!.tiles.find((t) => t.label === "Allergen-brott")!;
|
||||
expect(brott.value).toBe("3");
|
||||
expect(brott.tone).toBe("warn");
|
||||
});
|
||||
|
||||
it("alla sex tiles finns i rätt ordning", () => {
|
||||
const tiles = buildWall(baseSummary()).boards[0]!.tiles;
|
||||
expect(tiles.map((t) => t.label)).toEqual([
|
||||
"MRR",
|
||||
"Aktiva nu",
|
||||
"Betalande",
|
||||
"Trial-konv",
|
||||
"Workers",
|
||||
"Allergen-brott",
|
||||
]);
|
||||
});
|
||||
|
||||
it("ändrat summary-värde ändrar tile (inga hårdkodade tal)", () => {
|
||||
const first = buildWall(baseSummary({ anvandare: { ...baseSummary().anvandare, aktiva_nu: 1 } }));
|
||||
const second = buildWall(baseSummary({ anvandare: { ...baseSummary().anvandare, aktiva_nu: 2 } }));
|
||||
expect(first.boards[0]!.tiles.find((t) => t.label === "Aktiva nu")!.value).toBe("1");
|
||||
expect(second.boards[0]!.tiles.find((t) => t.label === "Aktiva nu")!.value).toBe("2");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user