feat(POSTV1-ASSET-ALL-16): 在编辑器接入全部复杂素材
Dada P0-A isolated Windows CI / validate-and-package (push) Waiting to run

This commit is contained in:
suyx
2026-08-06 11:55:06 +08:00
parent 9de0d2a63c
commit 2cd85cd7cd
17 changed files with 309 additions and 167 deletions
+13
View File
@@ -1,6 +1,10 @@
import { describe, expect, it } from "vitest";
import complexAssetCatalog from "../../apps/web/src/generated/complex-assets.json";
import { P0A_DYNAMIC_STICKERS } from "../../apps/web/src/dynamic-provider.js";
import { DYNAMIC_RENDER_MODELS } from "../../apps/web/src/dynamic-render-models.js";
import { P0A_COLOR_CARDS } from "../../apps/web/src/palette-provider.js";
import { P0A_FONT_OPTIONS, P0A_TEXT_TEMPLATES } from "../../apps/web/src/text-assets.js";
import {
P0A_COLOR_CARD_IDS,
P0A_DYNAMIC_STICKER_IDS,
@@ -36,4 +40,13 @@ describe("POSTV1-ASSET-ALL-16 complete asset catalog", () => {
expect(complexAssetCatalog.text_templates.every((item) => item.available)).toBe(true);
expect(JSON.stringify(complexAssetCatalog)).not.toMatch(/[A-Z]:[\\/]/i);
});
it("exposes every generated definition to the editor", () => {
expect(P0A_TEXT_TEMPLATES).toHaveLength(332);
expect(P0A_TEXT_TEMPLATES.every((item) => item.available && item.fontUrl)).toBe(true);
expect(P0A_FONT_OPTIONS).toHaveLength(86);
expect(P0A_COLOR_CARDS).toHaveLength(16);
expect(P0A_DYNAMIC_STICKERS).toHaveLength(35);
expect(Object.keys(DYNAMIC_RENDER_MODELS)).toHaveLength(35);
});
});
+12 -15
View File
@@ -3,6 +3,7 @@ import type { CanvasState } from "@dada/shared-contracts";
import {
P0A_TEXT_TEMPLATES,
P0A_FONT_OPTIONS,
TextEditSession,
createTextTemplateElement,
effectiveFontSize,
@@ -13,19 +14,15 @@ import { elementHalfExtents } from "../../apps/web/src/editor-elements.js";
const identity = { createdAt: "2026-08-03T03:00:00.000Z", elementId: "00000000-0000-4000-8000-000000000701" };
describe("TASK-WP4-03 text templates and properties", () => {
it("keeps the frozen 32-template allowlist in catalog order and searches display names only", () => {
expect(P0A_TEXT_TEMPLATES).toHaveLength(32);
expect(P0A_TEXT_TEMPLATES.map((template) => template.templateId)).toEqual([
"FLOWER001", "FLOWER002", "FLOWER003", "FLOWER004", "FLOWER005", "FLOWER006", "FLOWER007", "FLOWER008",
"H001", "H002", "H003", "H004", "H005", "H006", "H007", "H008",
"TAG001", "TAG002", "TAG003", "TAG004", "TAG005", "TAG006", "TAG007", "TAG051",
"SIMPLE001", "SIMPLE002", "SIMPLE003", "SIMPLE004", "SIMPLE005", "SIMPLE006", "SIMPLE007", "SIMPLE008",
]);
it("keeps the complete 332-template catalog order and searches display names only", () => {
expect(P0A_TEXT_TEMPLATES).toHaveLength(332);
expect(P0A_TEXT_TEMPLATES[0]?.templateId).toBe("FLOWER001");
expect(P0A_TEXT_TEMPLATES.at(-1)?.templateId).toBe("SIMPLE017");
expect(P0A_TEXT_TEMPLATES.reduce<Record<string, number>>((counts, template) => {
counts[template.category] = (counts[template.category] ?? 0) + 1;
return counts;
}, {})).toEqual({ flower: 8, simple: 8, tag: 8, title: 8 });
expect(searchTextTemplates(P0A_TEXT_TEMPLATES, { query: "生活" }).map((item) => item.templateId)).toEqual(["FLOWER004", "FLOWER005", "H001", "H003", "H006"]);
}, {})).toEqual({ flower: 145, simple: 17, tag: 51, title: 119 });
expect(searchTextTemplates(P0A_TEXT_TEMPLATES, { query: "生活" }).map((item) => item.templateId)).toEqual(expect.arrayContaining(["FLOWER004", "FLOWER005", "H001", "H003", "H006"]));
expect(searchTextTemplates(P0A_TEXT_TEMPLATES, { category: "tag", query: "TAG006" })).toEqual([]);
});
@@ -100,11 +97,11 @@ describe("TASK-WP4-03 text templates and properties", () => {
expect(bounds.y).toBeGreaterThan(0.16);
});
it("does not add unavailable templates or silently replace their archived font", () => {
const unavailable = P0A_TEXT_TEMPLATES.find((template) => !template.available)!;
expect(unavailable).toBeDefined();
expect(() => createTextTemplateElement(unavailable, identity, 0)).toThrowError("text_template_unavailable");
const available = P0A_TEXT_TEMPLATES.find((template) => template.available)!;
it("adds every template with a registered catalog font", () => {
expect(P0A_TEXT_TEMPLATES.every((template) => template.available && template.fontUrl)).toBe(true);
const registeredFonts = new Set(P0A_FONT_OPTIONS.map((font) => font.fontId));
expect(P0A_TEXT_TEMPLATES.every((template) => registeredFonts.has(template.defaultFontId))).toBe(true);
const available = P0A_TEXT_TEMPLATES[0]!;
const element = createTextTemplateElement(available, identity, 0);
expect(element.font_override).toBeUndefined();
expect((element.style_parameters as Record<string, unknown>).default_font_id).toBe(available.defaultFontId);
+16 -16
View File
@@ -45,8 +45,10 @@ describe("TASK-WP4-04 deterministic color cards", () => {
});
});
it("uses the four-item P0-A allowlist and produces a stable five-color MMCQ palette", () => {
expect(P0A_COLOR_CARDS.map((item) => item.cardId)).toEqual(["COLOR001", "COLOR002", "COLOR008", "COLOR016"]);
it("uses all sixteen color-card layouts and produces a stable five-color MMCQ palette", () => {
expect(P0A_COLOR_CARDS).toHaveLength(16);
expect(P0A_COLOR_CARDS[0]?.cardId).toBe("COLOR001");
expect(P0A_COLOR_CARDS.at(-1)?.cardId).toBe("COLOR016");
const pixels = colorPixels([
{ count: 50, rgb: [244, 32, 32] }, { count: 40, rgb: [32, 210, 96] }, { count: 30, rgb: [24, 96, 220] },
{ count: 20, rgb: [248, 210, 48] }, { count: 10, rgb: [120, 64, 180] },
@@ -78,10 +80,10 @@ describe("TASK-WP4-04 dynamic providers", () => {
profile: { creatorName: "Dada Creator", socialId: "@@dada" },
};
it("exposes exactly ten P0-A providers and snapshots time and identity", () => {
expect(P0A_DYNAMIC_STICKERS.map((item) => item.templateId)).toEqual([
"DYN001", "DYN002", "DYN003", "DYN004", "DYN007", "DYN008", "DYN011", "DYN012", "DYN015", "DYN016",
]);
it("exposes all thirty-five providers and snapshots time and identity", () => {
expect(P0A_DYNAMIC_STICKERS).toHaveLength(35);
expect(P0A_DYNAMIC_STICKERS[0]?.templateId).toBe("DYN001");
expect(P0A_DYNAMIC_STICKERS.at(-1)?.templateId).toBe("DYN035");
const time = createDynamicStickerElement("DYN012", context, identity, 0);
expect(time.formatted_value).toBe("09:07");
expect(time.dynamic_fields).toMatchObject({ font_substitution: "FONT081", hour: "09", minute: "07" });
@@ -92,11 +94,10 @@ describe("TASK-WP4-04 dynamic providers", () => {
});
it("uses the archived template categories and only the fields visibly consumed by each source", () => {
expect(P0A_DYNAMIC_STICKERS.map(({ category, templateId }) => [templateId, category])).toEqual([
["DYN001", "location"], ["DYN002", "location"], ["DYN003", "location"], ["DYN004", "location"],
["DYN007", "other"], ["DYN008", "time"], ["DYN011", "time"], ["DYN012", "time"],
["DYN015", "identity"], ["DYN016", "identity"],
]);
expect(P0A_DYNAMIC_STICKERS.reduce<Record<string, number>>((counts, item) => {
counts[item.category] = (counts[item.category] ?? 0) + 1;
return counts;
}, {})).toEqual({ identity: 21, location: 6, other: 1, time: 7 });
const other = createDynamicStickerElement("DYN007", context, identity, 0);
expect(other.dynamic_fields).toEqual({ nickname: "@dada" });
expect(other.formatted_value).toBe("@dada");
@@ -105,11 +106,10 @@ describe("TASK-WP4-04 dynamic providers", () => {
});
it("maps every enabled dynamic sticker to its archived source candidate and original resource version", () => {
expect(Object.entries(DYNAMIC_RENDER_MODELS).map(([id, model]) => [id, model.sourceCandidateId])).toEqual([
["DYN001", "l_POI01"], ["DYN002", "l_POI02"], ["DYN003", "l_POI03"], ["DYN004", "l_POI04"],
["DYN007", "diaoyu"], ["DYN008", "l_shijian2"], ["DYN011", "l_shijian6"], ["DYN012", "l_shijian7"],
["DYN015", "0721userna"], ["DYN016", "l_username00"],
]);
expect(Object.keys(DYNAMIC_RENDER_MODELS)).toHaveLength(35);
expect(DYNAMIC_RENDER_MODELS.DYN001?.sourceCandidateId).toBe("l_POI01");
expect(DYNAMIC_RENDER_MODELS.DYN012?.sourceCandidateId).toBe("l_shijian7");
expect(DYNAMIC_RENDER_MODELS.DYN035?.sourceCandidateId).toBe("l_username23");
const element = createDynamicStickerElement("DYN001", { ...context, location: { formattedValue: "温州" } }, identity, 0);
expect(element.resource_version).toBe(DYNAMIC_RESOURCE_VERSION);
expect(DYNAMIC_RENDER_MODELS.DYN001.imageLayers).toEqual([