File size: 5,106 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
import { describe, expect, it } from "vitest";

const { assertNativeGeneratedArtifactsIsolated, shouldRunNativeI18n, shouldStrictNativeI18n } =
  await import("../../scripts/ci-changed-scope.mjs");

describe("native i18n changed scope", () => {
  it.each([
    "scripts/android-app-i18n.ts",
    "scripts/apple-app-i18n.ts",
    "scripts/native-app-i18n.ts",
    "scripts/native-i18n-locales.ts",
  ])("routes native locale source %s without requiring generated parity", (sourcePath) => {
    expect(shouldRunNativeI18n([sourcePath])).toBe(true);
    expect(shouldStrictNativeI18n([sourcePath])).toBe(false);
  });

  it("routes Android flavor sources through native i18n", () => {
    expect(
      shouldRunNativeI18n([
        "apps/android/app/src/play/java/ai/openclaw/app/PlayBilling.kt",
        "apps/android/app/src/thirdParty/res/values/accessibility_strings.xml",
      ]),
    ).toBe(true);
  });

  it("keeps generated artifacts in isolated automation PRs", () => {
    const generatedCompanionPaths = [
      "apps/android/app/src/main/res/values/strings.xml",
      "apps/android/app/src/main/res/values/assistant.xml",
    ];
    const generatedPaths = [
      "apps/.i18n/native/sv.json",
      "apps/android/app/src/main/java/ai/openclaw/app/i18n/NativeStringResources.kt",
      "apps/android/app/src/main/res/values-sv/strings.xml",
      "apps/android/app/src/thirdParty/res/values-sv/accessibility_strings.xml",
      "apps/android/wear/src/main/res/values-sv/strings.xml",
      "apps/ios/Resources/Localizable.xcstrings",
      "apps/macos/Sources/OpenClaw/Resources/Localizable.xcstrings",
      "apps/ios/WatchApp/sv.lproj/InfoPlist.strings",
    ];

    expect(() => assertNativeGeneratedArtifactsIsolated(generatedPaths)).not.toThrow();
    expect(() =>
      assertNativeGeneratedArtifactsIsolated([...generatedPaths, ...generatedCompanionPaths]),
    ).not.toThrow();
    expect(() =>
      assertNativeGeneratedArtifactsIsolated([...generatedPaths, "apps/.i18n/native-source.json"]),
    ).toThrow("Native generated locale artifacts must be isolated from source changes");
    expect(() =>
      assertNativeGeneratedArtifactsIsolated(
        [...generatedPaths, "apps/ios/Sources/RootTabs.swift"],
        "main",
      ),
    ).not.toThrow();
    expect(() =>
      assertNativeGeneratedArtifactsIsolated([
        ...generatedCompanionPaths,
        "apps/.i18n/native-source.json",
      ]),
    ).not.toThrow();
    expect(() =>
      assertNativeGeneratedArtifactsIsolated([
        ...generatedPaths,
        ...generatedCompanionPaths,
        "apps/.i18n/native-source.json",
      ]),
    ).toThrow("Native generated locale artifacts must be isolated from source changes");
    expect(() =>
      assertNativeGeneratedArtifactsIsolated([
        ...generatedPaths,
        ...generatedCompanionPaths,
        "apps/android/app/src/main/java/ai/openclaw/app/MainActivity.kt",
      ]),
    ).toThrow("Native generated locale artifacts must be isolated from source changes");
  });

  it("allows only the owner-complete one-time native v2 artifact migration", () => {
    const owners = [
      ".gitattributes",
      "scripts/ci-changed-scope.mjs",
      "scripts/native-app-i18n.ts",
      "scripts/android-app-i18n.ts",
      "scripts/apple-app-i18n.ts",
      "test/scripts/native-app-i18n.test.ts",
      "test/scripts/apple-app-i18n.test.ts",
      "src/scripts/ci-changed-scope.native-i18n.test.ts",
    ];
    const generated = [
      "apps/.i18n/native/sv.json",
      "apps/android/app/src/main/res/values-sv/strings.xml",
      "apps/android/wear/src/main/res/values-sv/strings.xml",
      "apps/ios/Resources/Localizable.xcstrings",
      "apps/macos/Sources/OpenClaw/Resources/Localizable.xcstrings",
    ];
    const migration = [...owners, "apps/.i18n/native-source.json", ...generated];

    expect(() => assertNativeGeneratedArtifactsIsolated(migration)).not.toThrow();
    expect(() => assertNativeGeneratedArtifactsIsolated(migration.slice(1))).toThrow(
      "Native generated locale artifacts must be isolated from source changes",
    );
    expect(() =>
      assertNativeGeneratedArtifactsIsolated([
        ...migration,
        "apps/android/app/src/main/java/ai/openclaw/app/i18n/NativeStringResources.kt",
      ]),
    ).toThrow("Native generated locale artifacts must be isolated from source changes");
  });

  it("runs strict parity only for manual or generated-artifact checks", () => {
    expect(shouldStrictNativeI18n(null)).toBe(true);
    expect(shouldStrictNativeI18n(["apps/.i18n/native/sv.json"])).toBe(true);
    expect(
      shouldStrictNativeI18n([
        "apps/android/app/src/thirdParty/res/values-sv/accessibility_strings.xml",
      ]),
    ).toBe(true);
    expect(shouldStrictNativeI18n(["apps/ios/Resources/Localizable.xcstrings"])).toBe(true);
    expect(
      shouldStrictNativeI18n(["apps/macos/Sources/OpenClaw/Resources/Localizable.xcstrings"]),
    ).toBe(true);
    expect(
      shouldStrictNativeI18n(["apps/ios/Sources/RootTabs.swift", "apps/.i18n/native-source.json"]),
    ).toBe(false);
  });
});