File size: 5,589 Bytes
d197cf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Control UI i18n report tests cover locale report generation.
import { describe, expect, it } from "vitest";
import {
  filterRawCopyEntries,
  filterTranslationKeysBySurface,
  formatReport,
  parseArgs,
  summarizeRawCopy,
  type RawCopyBaselineEntry,
} from "../../scripts/control-ui-i18n-report.ts";

const entries: RawCopyBaselineEntry[] = [
  {
    count: 2,
    kind: "html-text",
    name: "text",
    path: "ui/src/ui/chat/render.ts",
    text: "Send",
  },
  {
    count: 1,
    kind: "object-property",
    name: "label",
    path: "ui/src/ui/views/agents-panels-tools.ts",
    text: "Tools",
  },
  {
    count: 4,
    kind: "html-attribute",
    name: "title",
    path: "ui/src/ui/views/config-form.render.ts",
    text: "Open config",
  },
];

describe("control-ui-i18n report helpers", () => {
  it.each(["--surface", "--locale", "--top"])("rejects option-shaped values for %s", (flag) => {
    expect(() => parseArgs([flag, "-h"])).toThrow(`${flag} requires a value`);
  });

  it("rejects invalid numeric limits", () => {
    expect(() => parseArgs(["--top", "3abc"])).toThrow("--top must be a positive integer");
    expect(() => parseArgs(["--top", "1.5"])).toThrow("--top must be a positive integer");
    expect(() => parseArgs(["--top", "0"])).toThrow("--top must be a positive integer");
    expect(() => parseArgs(["--top", "999999999999999999999999999"])).toThrow(
      "--top must be a positive integer",
    );
  });

  it("rejects locale path traversal before filesystem access", () => {
    expect(parseArgs(["--locale", "zh-CN"])).toMatchObject({ locale: "zh-CN" });
    expect(() => parseArgs(["--locale", "../zh-CN"])).toThrow("unknown locale");
    expect(() => parseArgs(["--locale", "../../../../scripts/control-ui-i18n-report"])).toThrow(
      "unknown locale",
    );
  });

  it("filters raw-copy entries by path surface token", () => {
    expect(filterRawCopyEntries(entries, "agents")).toEqual([entries[1]]);
    expect(filterRawCopyEntries(entries, "config")).toEqual([entries[2]]);
    expect(filterRawCopyEntries(entries, "missing")).toEqual([]);
  });

  it("summarizes raw-copy occurrences deterministically", () => {
    expect(summarizeRawCopy(entries, 2)).toEqual({
      entries: 3,
      occurrences: 7,
      topPaths: [
        { count: 4, path: "ui/src/ui/views/config-form.render.ts" },
        { count: 2, path: "ui/src/ui/chat/render.ts" },
      ],
    });
  });

  it("filters translation keys by surface token", () => {
    expect(
      filterTranslationKeysBySurface(
        [
          "agents.tabs.cronJobs",
          "chat.composer.send",
          "sessionsView.thinking",
          "usage.common.emptyValue",
        ],
        "chat",
      ),
    ).toEqual(["chat.composer.send"]);
    expect(
      filterTranslationKeysBySurface(
        [
          "agents.tabs.cronJobs",
          "chat.composer.send",
          "sessionsView.thinking",
          "usage.common.emptyValue",
        ],
        "sessions",
      ),
    ).toEqual(["sessionsView.thinking"]);
  });

  it("formats pasteable report text", () => {
    const report = formatReport({
      locale: {
        fallbackKeysInScope: ["actions.cancel"],
        meta: {
          fallbackKeys: ["actions.cancel"],
          generatedAt: "2026-05-13T00:00:00.000Z",
          locale: "zh-CN",
          model: "gpt-5.5",
          provider: "openai",
          sourceHash: "hash",
          totalKeys: 3,
          translatedKeys: 2,
          workflow: 1,
        },
      },
      rawCopy: summarizeRawCopy(entries, 1),
      surface: "chat",
    });

    expect(report).toContain("Control UI i18n baseline report\n");
    expect(report).toContain("Scope: Chat, Simplified Chinese (zh-CN)\n");
    expect(report).toContain(
      "Based on: current raw-copy baseline and locale metadata. Not a drift check.\n",
    );
    expect(report).toContain(
      "Hardcoded UI text outside i18n: 3 pieces in code, 7 total occurrences.\n",
    );
    expect(report).toContain(
      "Existing zh-CN translation keys (all Control UI): 2/3 filled, 1 fallback.\n",
    );
    expect(report).toContain("Chat still has UI text written directly in code.\n");
    expect(report).toContain("zh-CN has 1 missing key.\n");
    expect(report).not.toContain("matches English");
    expect(report).toContain("4 Config form: ui/src/ui/views/config-form.render.ts\n");
    expect(report).toContain("Move text from the focus modules into translation keys.\n");
    expect(report).toContain(
      "Do not hand-edit generated locale, translation memory, or i18n metadata files.\n",
    );
    expect(report).toContain("Run pnpm ui:i18n:sync after adding translation keys.\n");
    expect(report).not.toContain("raw-copy entries");
  });

  it("keeps fallback-key issues scoped to the selected surface", () => {
    const report = formatReport({
      locale: {
        fallbackKeysInScope: [],
        meta: {
          fallbackKeys: ["usage.common.emptyValue"],
          generatedAt: "2026-05-13T00:00:00.000Z",
          locale: "zh-CN",
          model: "gpt-5.5",
          provider: "openai",
          sourceHash: "hash",
          totalKeys: 3,
          translatedKeys: 2,
          workflow: 1,
        },
      },
      rawCopy: summarizeRawCopy(entries, 1),
      surface: "chat",
    });

    expect(report).toContain(
      "Existing zh-CN translation keys (all Control UI): 2/3 filled, 1 fallback.\n",
    );
    expect(report).toContain("No zh-CN locale problems found in this scope.\n");
    expect(report).not.toContain("zh-CN has 1 missing key.\n");
  });
});