feat(housekeeping): unify complete rebuild in gated preview
This commit is contained in:
1 parent
4ad007d241
commit
43470ebf82
402 files changed
+78313
-1663
No files matched your search
@@ -0,0 +1,159 @@
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { PERMS } from "@/lib/permission-slugs";
|
||||
import {
|
||||
fail,
|
||||
type HousekeepingCapabilityContext,
|
||||
ok,
|
||||
} from "../../../foundation/contracts";
|
||||
import { EconomyCatalogPage } from "./catalog";
|
||||
import { EconomyCommercePage } from "./commerce";
|
||||
import { EconomyHistoryPage } from "./history";
|
||||
import { EconomyItemsPage } from "./items";
|
||||
import { EconomyRewardsPage } from "./rewards";
|
||||
import { EconomyValuePage } from "./value";
|
||||
|
||||
function context(granted: readonly string[]): HousekeepingCapabilityContext {
|
||||
const permissions = new Set(granted);
|
||||
return {
|
||||
actor: { id: 42, username: "operator", rank: 7 },
|
||||
isSuperAdmin: false,
|
||||
has: (slug) => permissions.has(slug),
|
||||
hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)),
|
||||
hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)),
|
||||
};
|
||||
}
|
||||
|
||||
const cases = [
|
||||
["catalog", EconomyCatalogPage],
|
||||
["items", EconomyItemsPage],
|
||||
["commerce", EconomyCommercePage],
|
||||
["history", EconomyHistoryPage],
|
||||
["value", EconomyValuePage],
|
||||
["rewards", EconomyRewardsPage],
|
||||
] as const;
|
||||
|
||||
describe.each(cases)("Economy %s page", (kind, Component) => {
|
||||
it("renders loading, forbidden, empty, and canonical ready states", () => {
|
||||
const render = (result?: unknown) =>
|
||||
renderToStaticMarkup(
|
||||
<Component
|
||||
context={context(Object.values(PERMS))}
|
||||
result={result as never}
|
||||
routeId={null}
|
||||
/>,
|
||||
);
|
||||
expect(render()).toContain('data-housekeeping-state="loading"');
|
||||
expect(
|
||||
render(fail("FORBIDDEN", "errors.housekeeping.forbidden", "denied")),
|
||||
).toContain('data-housekeeping-state="forbidden"');
|
||||
expect(
|
||||
render(
|
||||
ok({ kind, items: [], total: 0, partialDependencies: [] }, "empty"),
|
||||
),
|
||||
).toContain('data-housekeeping-state="empty"');
|
||||
const ready = render(
|
||||
ok(
|
||||
{
|
||||
kind,
|
||||
items: [
|
||||
{
|
||||
id: "18446744073709551615",
|
||||
title: "Canonical economy item",
|
||||
status: "active",
|
||||
href: `/ase-next/economy/${kind}`,
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
partialDependencies: [],
|
||||
},
|
||||
"ready",
|
||||
),
|
||||
);
|
||||
expect(ready).toContain('data-housekeeping-state="ready"');
|
||||
expect(ready).toContain("Canonical economy item");
|
||||
expect(ready).not.toContain("/admin");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Economy mutation forms", () => {
|
||||
it("renders edit controls only with the exact mutation capability", () => {
|
||||
const result = ok(
|
||||
{
|
||||
kind: "commerce" as const,
|
||||
items: [{ id: "1", title: "Package", status: "active" }],
|
||||
total: 1,
|
||||
partialDependencies: [],
|
||||
},
|
||||
"shop",
|
||||
);
|
||||
const readOnly = renderToStaticMarkup(
|
||||
<EconomyCommercePage
|
||||
context={context([PERMS.SHOP_VIEW])}
|
||||
result={result}
|
||||
routeId="economy.commerce.shop"
|
||||
/>,
|
||||
);
|
||||
const editor = renderToStaticMarkup(
|
||||
<EconomyCommercePage
|
||||
context={context([PERMS.SHOP_VIEW, PERMS.SHOP_EDIT])}
|
||||
result={result}
|
||||
routeId="economy.commerce.shop"
|
||||
/>,
|
||||
);
|
||||
expect(readOnly).not.toContain("economy.commerce.shop-article.change");
|
||||
expect(editor).toContain("economy.commerce.shop-article.change");
|
||||
expect(editor).toContain("<form");
|
||||
});
|
||||
|
||||
it("renders validator-shaped catalog, voucher, value, and soundtrack fields", () => {
|
||||
const all = context(Object.values(PERMS));
|
||||
const empty = (kind: "catalog" | "commerce" | "value" | "rewards") =>
|
||||
ok({ kind, items: [], total: 0, partialDependencies: [] }, kind);
|
||||
const catalog = renderToStaticMarkup(
|
||||
<EconomyCatalogPage
|
||||
context={all}
|
||||
result={empty("catalog")}
|
||||
routeId="economy.catalog.detail"
|
||||
/>,
|
||||
);
|
||||
for (const field of ["id", "caption", "parentId", "pageLayout"]) {
|
||||
expect(catalog).toContain(`name="${field}"`);
|
||||
}
|
||||
const commerce = renderToStaticMarkup(
|
||||
<EconomyCommercePage
|
||||
context={all}
|
||||
result={empty("commerce")}
|
||||
routeId="economy.commerce.vouchers"
|
||||
/>,
|
||||
);
|
||||
for (const field of ["code", "amount", "maxUses", "expiresAt"]) {
|
||||
expect(commerce).toContain(`name="${field}"`);
|
||||
}
|
||||
const value = renderToStaticMarkup(
|
||||
<EconomyValuePage
|
||||
context={all}
|
||||
result={empty("value")}
|
||||
routeId="economy.value.rare-values"
|
||||
/>,
|
||||
);
|
||||
for (const field of [
|
||||
"categoryId",
|
||||
"name",
|
||||
"currencyValue",
|
||||
"currencyType",
|
||||
]) {
|
||||
expect(value).toContain(`name="${field}"`);
|
||||
}
|
||||
const rewards = renderToStaticMarkup(
|
||||
<EconomyRewardsPage
|
||||
context={all}
|
||||
result={empty("rewards")}
|
||||
routeId="economy.rewards.sounds"
|
||||
/>,
|
||||
);
|
||||
for (const field of ["id", "name", "author", "track", "length"]) {
|
||||
expect(rewards).toContain(`name="${field}"`);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user