@messagevisor/core 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +7 -0
- package/jest.config.js +8 -0
- package/lib/benchmark/index.d.ts +2 -0
- package/lib/benchmark/index.js +417 -0
- package/lib/benchmark/index.js.map +1 -0
- package/lib/builder/index.d.ts +70 -0
- package/lib/builder/index.js +831 -0
- package/lib/builder/index.js.map +1 -0
- package/lib/cli/index.d.ts +28 -0
- package/lib/cli/index.js +182 -0
- package/lib/cli/index.js.map +1 -0
- package/lib/config/index.d.ts +61 -0
- package/lib/config/index.js +255 -0
- package/lib/config/index.js.map +1 -0
- package/lib/create/index.d.ts +2 -0
- package/lib/create/index.js +405 -0
- package/lib/create/index.js.map +1 -0
- package/lib/datasource/filesystemAdapter.d.ts +44 -0
- package/lib/datasource/filesystemAdapter.js +424 -0
- package/lib/datasource/filesystemAdapter.js.map +1 -0
- package/lib/datasource/index.d.ts +39 -0
- package/lib/datasource/index.js +96 -0
- package/lib/datasource/index.js.map +1 -0
- package/lib/error.d.ts +6 -0
- package/lib/error.js +49 -0
- package/lib/error.js.map +1 -0
- package/lib/evaluate/cli.d.ts +8 -0
- package/lib/evaluate/cli.js +179 -0
- package/lib/evaluate/cli.js.map +1 -0
- package/lib/evaluate/index.d.ts +10 -0
- package/lib/evaluate/index.js +131 -0
- package/lib/evaluate/index.js.map +1 -0
- package/lib/examples/coerceExampleIsoDates.d.ts +12 -0
- package/lib/examples/coerceExampleIsoDates.js +81 -0
- package/lib/examples/coerceExampleIsoDates.js.map +1 -0
- package/lib/examples/index.d.ts +63 -0
- package/lib/examples/index.js +713 -0
- package/lib/examples/index.js.map +1 -0
- package/lib/exporter/index.d.ts +60 -0
- package/lib/exporter/index.js +610 -0
- package/lib/exporter/index.js.map +1 -0
- package/lib/find-duplicates/index.d.ts +41 -0
- package/lib/find-duplicates/index.js +297 -0
- package/lib/find-duplicates/index.js.map +1 -0
- package/lib/generate-code/index.d.ts +11 -0
- package/lib/generate-code/index.js +157 -0
- package/lib/generate-code/index.js.map +1 -0
- package/lib/generate-code/typescript.d.ts +14 -0
- package/lib/generate-code/typescript.js +307 -0
- package/lib/generate-code/typescript.js.map +1 -0
- package/lib/importer/index.d.ts +64 -0
- package/lib/importer/index.js +1092 -0
- package/lib/importer/index.js.map +1 -0
- package/lib/index.d.ts +18 -0
- package/lib/index.js +35 -0
- package/lib/index.js.map +1 -0
- package/lib/info/index.d.ts +17 -0
- package/lib/info/index.js +132 -0
- package/lib/info/index.js.map +1 -0
- package/lib/init/index.d.ts +30 -0
- package/lib/init/index.js +348 -0
- package/lib/init/index.js.map +1 -0
- package/lib/lint/index.d.ts +1 -0
- package/lib/lint/index.js +6 -0
- package/lib/lint/index.js.map +1 -0
- package/lib/linter/attributeSchema.d.ts +7 -0
- package/lib/linter/attributeSchema.js +36 -0
- package/lib/linter/attributeSchema.js.map +1 -0
- package/lib/linter/checkLocaleCircularDependency.d.ts +7 -0
- package/lib/linter/checkLocaleCircularDependency.js +42 -0
- package/lib/linter/checkLocaleCircularDependency.js.map +1 -0
- package/lib/linter/conditionSchema.d.ts +3 -0
- package/lib/linter/conditionSchema.js +283 -0
- package/lib/linter/conditionSchema.js.map +1 -0
- package/lib/linter/formatSchema.d.ts +325 -0
- package/lib/linter/formatSchema.js +165 -0
- package/lib/linter/formatSchema.js.map +1 -0
- package/lib/linter/icuStyleLint.d.ts +6 -0
- package/lib/linter/icuStyleLint.js +226 -0
- package/lib/linter/icuStyleLint.js.map +1 -0
- package/lib/linter/index.d.ts +34 -0
- package/lib/linter/index.js +557 -0
- package/lib/linter/index.js.map +1 -0
- package/lib/linter/localeSchema.d.ts +672 -0
- package/lib/linter/localeSchema.js +50 -0
- package/lib/linter/localeSchema.js.map +1 -0
- package/lib/linter/messageSchema.d.ts +35 -0
- package/lib/linter/messageSchema.js +115 -0
- package/lib/linter/messageSchema.js.map +1 -0
- package/lib/linter/printError.d.ts +8 -0
- package/lib/linter/printError.js +41 -0
- package/lib/linter/printError.js.map +1 -0
- package/lib/linter/schema.d.ts +33 -0
- package/lib/linter/schema.js +192 -0
- package/lib/linter/schema.js.map +1 -0
- package/lib/linter/segmentSchema.d.ts +8 -0
- package/lib/linter/segmentSchema.js +18 -0
- package/lib/linter/segmentSchema.js.map +1 -0
- package/lib/linter/targetSchema.d.ts +337 -0
- package/lib/linter/targetSchema.js +39 -0
- package/lib/linter/targetSchema.js.map +1 -0
- package/lib/linter/testSchema.d.ts +71 -0
- package/lib/linter/testSchema.js +165 -0
- package/lib/linter/testSchema.js.map +1 -0
- package/lib/linter/zodHelpers.d.ts +2 -0
- package/lib/linter/zodHelpers.js +15 -0
- package/lib/linter/zodHelpers.js.map +1 -0
- package/lib/list/index.d.ts +8 -0
- package/lib/list/index.js +524 -0
- package/lib/list/index.js.map +1 -0
- package/lib/matrix.d.ts +4 -0
- package/lib/matrix.js +66 -0
- package/lib/matrix.js.map +1 -0
- package/lib/promoter/index.d.ts +65 -0
- package/lib/promoter/index.js +1208 -0
- package/lib/promoter/index.js.map +1 -0
- package/lib/prune/index.d.ts +37 -0
- package/lib/prune/index.js +673 -0
- package/lib/prune/index.js.map +1 -0
- package/lib/sets.d.ts +10 -0
- package/lib/sets.js +120 -0
- package/lib/sets.js.map +1 -0
- package/lib/tester/cliFormat.d.ts +8 -0
- package/lib/tester/cliFormat.js +15 -0
- package/lib/tester/cliFormat.js.map +1 -0
- package/lib/tester/index.d.ts +35 -0
- package/lib/tester/index.js +713 -0
- package/lib/tester/index.js.map +1 -0
- package/lib/tester/matrix.d.ts +14 -0
- package/lib/tester/matrix.js +76 -0
- package/lib/tester/matrix.js.map +1 -0
- package/lib/tester/prettyDuration.d.ts +1 -0
- package/lib/tester/prettyDuration.js +30 -0
- package/lib/tester/prettyDuration.js.map +1 -0
- package/lib/tester/printTestResult.d.ts +2 -0
- package/lib/tester/printTestResult.js +32 -0
- package/lib/tester/printTestResult.js.map +1 -0
- package/lib/tester/types.d.ts +29 -0
- package/lib/tester/types.js +3 -0
- package/lib/tester/types.js.map +1 -0
- package/package.json +41 -13
- package/src/benchmark/index.spec.ts +375 -0
- package/src/benchmark/index.ts +433 -0
- package/src/builder/index.spec.ts +822 -0
- package/src/builder/index.ts +920 -0
- package/src/cli/index.spec.ts +54 -0
- package/src/cli/index.ts +150 -0
- package/src/config/index.spec.ts +70 -0
- package/src/config/index.ts +259 -0
- package/src/create/index.spec.ts +272 -0
- package/src/create/index.ts +295 -0
- package/src/datasource/filesystemAdapter.ts +313 -0
- package/src/datasource/index.ts +135 -0
- package/src/error.ts +33 -0
- package/src/evaluate/cli.spec.ts +368 -0
- package/src/evaluate/cli.ts +130 -0
- package/src/evaluate/index.ts +161 -0
- package/src/examples/coerceExampleIsoDates.spec.ts +81 -0
- package/src/examples/coerceExampleIsoDates.ts +98 -0
- package/src/examples/index.spec.ts +453 -0
- package/src/examples/index.ts +854 -0
- package/src/exporter/index.spec.ts +443 -0
- package/src/exporter/index.ts +643 -0
- package/src/find-duplicates/index.spec.ts +289 -0
- package/src/find-duplicates/index.ts +314 -0
- package/src/generate-code/index.ts +92 -0
- package/src/generate-code/typescript.spec.ts +241 -0
- package/src/generate-code/typescript.ts +284 -0
- package/src/importer/index.spec.ts +1101 -0
- package/src/importer/index.ts +1190 -0
- package/src/index.ts +18 -0
- package/src/info/index.ts +67 -0
- package/src/init/index.spec.ts +279 -0
- package/src/init/index.ts +292 -0
- package/src/lint/index.ts +1 -0
- package/src/linter/attributeSchema.ts +38 -0
- package/src/linter/checkLocaleCircularDependency.ts +51 -0
- package/src/linter/conditionSchema.ts +386 -0
- package/src/linter/formatSchema.ts +170 -0
- package/src/linter/icuStyleLint.ts +312 -0
- package/src/linter/index.spec.ts +824 -0
- package/src/linter/index.ts +460 -0
- package/src/linter/localeSchema.ts +70 -0
- package/src/linter/messageSchema.ts +152 -0
- package/src/linter/printError.ts +52 -0
- package/src/linter/schema.ts +230 -0
- package/src/linter/segmentSchema.ts +15 -0
- package/src/linter/targetSchema.ts +50 -0
- package/src/linter/testSchema.spec.ts +405 -0
- package/src/linter/testSchema.ts +239 -0
- package/src/linter/zodHelpers.ts +16 -0
- package/src/list/index.spec.ts +431 -0
- package/src/list/index.ts +463 -0
- package/src/matrix.ts +69 -0
- package/src/promoter/index.spec.ts +584 -0
- package/src/promoter/index.ts +1267 -0
- package/src/prune/index.spec.ts +418 -0
- package/src/prune/index.ts +693 -0
- package/src/sets.ts +74 -0
- package/src/tester/cliFormat.ts +11 -0
- package/src/tester/featurevisorIntegration.spec.ts +101 -0
- package/src/tester/index.spec.ts +577 -0
- package/src/tester/index.ts +679 -0
- package/src/tester/matrix.ts +106 -0
- package/src/tester/prettyDuration.ts +34 -0
- package/src/tester/printTestResult.ts +40 -0
- package/src/tester/types.ts +32 -0
- package/tsconfig.cjs.json +11 -0
- package/tsconfig.typecheck.json +4 -0
|
@@ -0,0 +1,822 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
|
|
5
|
+
import { Datasource } from "../datasource";
|
|
6
|
+
import { getProjectConfig } from "../config";
|
|
7
|
+
import { buildProject, buildProjectSets, mergeFormats } from "./index";
|
|
8
|
+
|
|
9
|
+
async function writeFile(root: string, relativePath: string, content: string) {
|
|
10
|
+
const filePath = path.join(root, relativePath);
|
|
11
|
+
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
12
|
+
await fs.promises.writeFile(filePath, content);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function createProject() {
|
|
16
|
+
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "messagevisor-"));
|
|
17
|
+
|
|
18
|
+
await writeFile(root, "messagevisor.config.js", "module.exports = {};\n");
|
|
19
|
+
await writeFile(
|
|
20
|
+
root,
|
|
21
|
+
"locales/en.yml",
|
|
22
|
+
"description: English\nformats:\n number:\n money:\n style: currency\n currency: USD\n",
|
|
23
|
+
);
|
|
24
|
+
await writeFile(
|
|
25
|
+
root,
|
|
26
|
+
"locales/en-US.yml",
|
|
27
|
+
"description: English US\ndirection: ltr\ninheritFormatsFrom: en\ninheritTranslationsFrom: en\n",
|
|
28
|
+
);
|
|
29
|
+
await writeFile(
|
|
30
|
+
root,
|
|
31
|
+
"targets/web.yml",
|
|
32
|
+
"description: Web\nincludeMessages:\n - auth*\nlocales:\n - en-US\n",
|
|
33
|
+
);
|
|
34
|
+
await writeFile(root, "attributes/platform.yml", "description: Platform\ntype: string\n");
|
|
35
|
+
await writeFile(
|
|
36
|
+
root,
|
|
37
|
+
"segments/platform-web.yml",
|
|
38
|
+
"description: Web\npromotable: false\nconditions:\n - attribute: platform\n operator: equals\n value: web\n",
|
|
39
|
+
);
|
|
40
|
+
await writeFile(
|
|
41
|
+
root,
|
|
42
|
+
"messages/auth/signin.yml",
|
|
43
|
+
"description: Sign in\npromotable: false\ntranslations:\n en: Sign in\n en-US: Sign in now\noverrides:\n - key: platform-web\n segments: platform-web\n translations:\n en-US: Sign in on web\n",
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return root;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe("buildProject", function () {
|
|
50
|
+
it("deep merges inherited format presets", function () {
|
|
51
|
+
const formats = mergeFormats(
|
|
52
|
+
{
|
|
53
|
+
number: {
|
|
54
|
+
money: {
|
|
55
|
+
style: "currency",
|
|
56
|
+
currency: "USD",
|
|
57
|
+
currencyDisplay: "symbol",
|
|
58
|
+
minimumFractionDigits: 2,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
date: {
|
|
62
|
+
long: {
|
|
63
|
+
year: "numeric",
|
|
64
|
+
month: "long",
|
|
65
|
+
day: "numeric",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
number: {
|
|
71
|
+
money: {
|
|
72
|
+
currency: "EUR",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
date: {
|
|
76
|
+
long: {
|
|
77
|
+
hour: "numeric",
|
|
78
|
+
minute: "2-digit",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
} as any,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(formats?.number?.money).toEqual({
|
|
85
|
+
style: "currency",
|
|
86
|
+
currency: "EUR",
|
|
87
|
+
currencyDisplay: "symbol",
|
|
88
|
+
minimumFractionDigits: 2,
|
|
89
|
+
});
|
|
90
|
+
expect(formats?.date?.long).toEqual({
|
|
91
|
+
year: "numeric",
|
|
92
|
+
month: "long",
|
|
93
|
+
day: "numeric",
|
|
94
|
+
hour: "numeric",
|
|
95
|
+
minute: "2-digit",
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("builds target-specific locale datafiles and revision", async function () {
|
|
100
|
+
const root = await createProject();
|
|
101
|
+
const projectConfig = getProjectConfig(root);
|
|
102
|
+
const datasource = new Datasource(projectConfig, root);
|
|
103
|
+
|
|
104
|
+
const datafiles = await buildProject(projectConfig, datasource);
|
|
105
|
+
|
|
106
|
+
expect(datafiles).toHaveLength(1);
|
|
107
|
+
expect(datafiles[0].target).toEqual("web");
|
|
108
|
+
expect(datafiles[0].locale).toEqual("en-US");
|
|
109
|
+
expect(datafiles[0].direction).toEqual("ltr");
|
|
110
|
+
expect(datafiles[0].translations["auth.signin"]).toEqual("Sign in now");
|
|
111
|
+
expect(datafiles[0].messages["auth.signin"].overrides?.[0].translation).toEqual(
|
|
112
|
+
"Sign in on web",
|
|
113
|
+
);
|
|
114
|
+
expect(datafiles[0].formats?.number?.money?.style).toEqual("currency");
|
|
115
|
+
expect(datafiles[0].segments["platform-web"].key).toBeUndefined();
|
|
116
|
+
expect(datafiles[0].segments["platform-web"].description).toBeUndefined();
|
|
117
|
+
expect((datafiles[0].segments["platform-web"] as any).promotable).toBeUndefined();
|
|
118
|
+
expect((datafiles[0].messages["auth.signin"] as any).description).toBeUndefined();
|
|
119
|
+
expect((datafiles[0].messages["auth.signin"] as any).promotable).toBeUndefined();
|
|
120
|
+
expect(await datasource.readRevision()).toEqual("1");
|
|
121
|
+
|
|
122
|
+
const written = JSON.parse(
|
|
123
|
+
await fs.promises.readFile(path.join(root, "datafiles/messagevisor-web-en-US.json"), "utf8"),
|
|
124
|
+
);
|
|
125
|
+
expect(written.direction).toEqual("ltr");
|
|
126
|
+
expect(written.translations["auth.signin"]).toEqual("Sign in now");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("writes nested target datafiles under matching subdirectories", async function () {
|
|
130
|
+
const root = await createProject();
|
|
131
|
+
await writeFile(
|
|
132
|
+
root,
|
|
133
|
+
"targets/apps/web.yml",
|
|
134
|
+
"description: App Web\nincludeMessages:\n - auth*\nlocales:\n - en-US\n",
|
|
135
|
+
);
|
|
136
|
+
const projectConfig = getProjectConfig(root);
|
|
137
|
+
const datasource = new Datasource(projectConfig, root);
|
|
138
|
+
const localeBuiltEvents: string[] = [];
|
|
139
|
+
|
|
140
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
141
|
+
target: "apps.web",
|
|
142
|
+
onProgress(event) {
|
|
143
|
+
if (event.type === "localeBuilt" && event.filePath) {
|
|
144
|
+
localeBuiltEvents.push(path.relative(root, event.filePath));
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(datafiles).toHaveLength(1);
|
|
150
|
+
expect(datafiles[0].target).toEqual("apps.web");
|
|
151
|
+
expect(datafiles[0].translations["auth.signin"]).toEqual("Sign in now");
|
|
152
|
+
expect(localeBuiltEvents).toEqual(["datafiles/apps/messagevisor-web-en-US.json"]);
|
|
153
|
+
|
|
154
|
+
const writtenPath = path.join(root, "datafiles/apps/messagevisor-web-en-US.json");
|
|
155
|
+
const written = JSON.parse(await fs.promises.readFile(writtenPath, "utf8"));
|
|
156
|
+
expect(written.target).toEqual("apps.web");
|
|
157
|
+
expect((await datasource.readDatafile("apps.web", "en-US")).target).toEqual("apps.web");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("builds independent datafiles and revisions for sets", async function () {
|
|
161
|
+
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "messagevisor-"));
|
|
162
|
+
|
|
163
|
+
await writeFile(root, "messagevisor.config.js", "module.exports = { sets: true };\n");
|
|
164
|
+
|
|
165
|
+
for (const set of ["storefront", "admin"]) {
|
|
166
|
+
await writeFile(root, `sets/${set}/locales/en.yml`, "description: English\n");
|
|
167
|
+
await writeFile(
|
|
168
|
+
root,
|
|
169
|
+
`sets/${set}/locales/en-US.yml`,
|
|
170
|
+
"description: English US\ninheritTranslationsFrom: en\n",
|
|
171
|
+
);
|
|
172
|
+
await writeFile(
|
|
173
|
+
root,
|
|
174
|
+
`sets/${set}/targets/web.yml`,
|
|
175
|
+
"description: Web\nincludeMessages:\n - common*\nlocales:\n - en-US\n",
|
|
176
|
+
);
|
|
177
|
+
await writeFile(
|
|
178
|
+
root,
|
|
179
|
+
`sets/${set}/messages/common/welcome.yml`,
|
|
180
|
+
`description: Welcome\ntranslations:\n en: ${set} welcome\n`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const projectConfig = getProjectConfig(root);
|
|
185
|
+
const datasource = new Datasource(projectConfig, root);
|
|
186
|
+
const datafiles = await buildProjectSets(projectConfig, datasource);
|
|
187
|
+
|
|
188
|
+
expect(datafiles).toHaveLength(2);
|
|
189
|
+
expect(datafiles.map((datafile) => datafile.translations["common.welcome"]).sort()).toEqual([
|
|
190
|
+
"admin welcome",
|
|
191
|
+
"storefront welcome",
|
|
192
|
+
]);
|
|
193
|
+
expect(await datasource.readRevision()).toEqual("1");
|
|
194
|
+
expect(await datasource.forSet("storefront").readRevision()).toEqual("1");
|
|
195
|
+
expect(await datasource.forSet("admin").readRevision()).toEqual("1");
|
|
196
|
+
expect(
|
|
197
|
+
await fs.promises.readFile(path.join(root, ".messagevisor/sets/storefront/REVISION"), "utf8"),
|
|
198
|
+
).toEqual("1");
|
|
199
|
+
expect(
|
|
200
|
+
await fs.promises.readFile(path.join(root, ".messagevisor/sets/admin/REVISION"), "utf8"),
|
|
201
|
+
).toEqual("1");
|
|
202
|
+
|
|
203
|
+
const selectedDatafiles = await buildProjectSets(projectConfig, datasource, {
|
|
204
|
+
set: "admin",
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
expect(selectedDatafiles).toHaveLength(1);
|
|
208
|
+
expect(selectedDatafiles[0].translations["common.welcome"]).toEqual("admin welcome");
|
|
209
|
+
expect(await datasource.readRevision()).toEqual("2");
|
|
210
|
+
expect(await datasource.forSet("admin").readRevision()).toEqual("2");
|
|
211
|
+
expect(await datasource.forSet("storefront").readRevision()).toEqual("1");
|
|
212
|
+
|
|
213
|
+
await buildProjectSets(projectConfig, datasource, {
|
|
214
|
+
set: "storefront",
|
|
215
|
+
noStateFiles: true,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
expect(await datasource.readRevision()).toEqual("2");
|
|
219
|
+
expect(await datasource.forSet("admin").readRevision()).toEqual("2");
|
|
220
|
+
expect(await datasource.forSet("storefront").readRevision()).toEqual("1");
|
|
221
|
+
|
|
222
|
+
const storefront = JSON.parse(
|
|
223
|
+
await fs.promises.readFile(
|
|
224
|
+
path.join(root, "datafiles/storefront/messagevisor-web-en-US.json"),
|
|
225
|
+
"utf8",
|
|
226
|
+
),
|
|
227
|
+
);
|
|
228
|
+
const admin = JSON.parse(
|
|
229
|
+
await fs.promises.readFile(
|
|
230
|
+
path.join(root, "datafiles/admin/messagevisor-web-en-US.json"),
|
|
231
|
+
"utf8",
|
|
232
|
+
),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
expect(storefront.translations["common.welcome"]).toEqual("storefront welcome");
|
|
236
|
+
expect(admin.translations["common.welcome"]).toEqual("admin welcome");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("reports the starting revision before the build and latest revision at completion", async function () {
|
|
240
|
+
const root = await createProject();
|
|
241
|
+
const projectConfig = getProjectConfig(root);
|
|
242
|
+
const datasource = new Datasource(projectConfig, root);
|
|
243
|
+
const events: any[] = [];
|
|
244
|
+
|
|
245
|
+
await buildProject(projectConfig, datasource, {
|
|
246
|
+
onProgress: (event) => events.push(event),
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
expect(events[0]).toEqual({
|
|
250
|
+
type: "start",
|
|
251
|
+
previousRevision: "0",
|
|
252
|
+
revision: "1",
|
|
253
|
+
targets: ["web"],
|
|
254
|
+
});
|
|
255
|
+
expect(events[events.length - 1]).toMatchObject({
|
|
256
|
+
type: "complete",
|
|
257
|
+
revision: "1",
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("reports datafile size when requested", async function () {
|
|
262
|
+
const root = await createProject();
|
|
263
|
+
const projectConfig = getProjectConfig(root);
|
|
264
|
+
const datasource = new Datasource(projectConfig, root);
|
|
265
|
+
const events: any[] = [];
|
|
266
|
+
|
|
267
|
+
await buildProject(projectConfig, datasource, {
|
|
268
|
+
noStateFiles: true,
|
|
269
|
+
showSize: true,
|
|
270
|
+
onProgress: (event) => events.push(event),
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const localeBuiltEvent = events.find((event) => event.type === "localeBuilt");
|
|
274
|
+
|
|
275
|
+
expect(localeBuiltEvent.sizeInBytes).toBeGreaterThan(0);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("uses configured namespace character for nested entity keys", async function () {
|
|
279
|
+
const root = await createProject();
|
|
280
|
+
await writeFile(
|
|
281
|
+
root,
|
|
282
|
+
"messagevisor.config.js",
|
|
283
|
+
'module.exports = { namespaceCharacter: ":", exportOverrideKeySeparator: "#" };\n',
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
const projectConfig = getProjectConfig(root);
|
|
287
|
+
const datasource = new Datasource(projectConfig, root);
|
|
288
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
289
|
+
target: "web",
|
|
290
|
+
locale: "en-US",
|
|
291
|
+
noStateFiles: true,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
expect(datafiles[0].translations["auth:signin"]).toEqual("Sign in now");
|
|
295
|
+
expect(datafiles[0].messages["auth:signin"].overrides?.[0].translation).toEqual(
|
|
296
|
+
"Sign in on web",
|
|
297
|
+
);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("rejects entity path names containing the namespace character", async function () {
|
|
301
|
+
const root = await createProject();
|
|
302
|
+
await writeFile(
|
|
303
|
+
root,
|
|
304
|
+
"messages/auth.signout.yml",
|
|
305
|
+
"description: Sign out\ntranslations:\n en: Sign out\n",
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const projectConfig = getProjectConfig(root);
|
|
309
|
+
const datasource = new Datasource(projectConfig, root);
|
|
310
|
+
|
|
311
|
+
await expect(datasource.listMessages()).rejects.toThrow(
|
|
312
|
+
'namespaceCharacter "." is not allowed in directory or file names',
|
|
313
|
+
);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("omits message metadata entries when there is no meaningful metadata", async function () {
|
|
317
|
+
const root = await createProject();
|
|
318
|
+
await writeFile(
|
|
319
|
+
root,
|
|
320
|
+
"messages/auth/signout.yml",
|
|
321
|
+
"description: Sign out\ntranslations:\n en: Sign out\n",
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const projectConfig = getProjectConfig(root);
|
|
325
|
+
const datasource = new Datasource(projectConfig, root);
|
|
326
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
327
|
+
target: "web",
|
|
328
|
+
locale: "en-US",
|
|
329
|
+
noStateFiles: true,
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
expect(datafiles[0].translations["auth.signout"]).toEqual("Sign out");
|
|
333
|
+
expect(datafiles[0].messages["auth.signout"]).toBeUndefined();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("includes message metadata entries when meta is present", async function () {
|
|
337
|
+
const root = await createProject();
|
|
338
|
+
await writeFile(
|
|
339
|
+
root,
|
|
340
|
+
"messages/auth/signout.yml",
|
|
341
|
+
[
|
|
342
|
+
"description: Sign out",
|
|
343
|
+
"meta:",
|
|
344
|
+
" tags:",
|
|
345
|
+
" - auth",
|
|
346
|
+
" - exit",
|
|
347
|
+
" analytics:",
|
|
348
|
+
" event: signout_click",
|
|
349
|
+
"translations:",
|
|
350
|
+
" en: Sign out",
|
|
351
|
+
"",
|
|
352
|
+
].join("\n"),
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
const projectConfig = getProjectConfig(root);
|
|
356
|
+
const datasource = new Datasource(projectConfig, root);
|
|
357
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
358
|
+
target: "web",
|
|
359
|
+
locale: "en-US",
|
|
360
|
+
noStateFiles: true,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
expect(datafiles[0].translations["auth.signout"]).toEqual("Sign out");
|
|
364
|
+
expect(datafiles[0].messages["auth.signout"]).toEqual({
|
|
365
|
+
meta: {
|
|
366
|
+
tags: ["auth", "exit"],
|
|
367
|
+
analytics: {
|
|
368
|
+
event: "signout_click",
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("includes non-archived messages regardless of any former published concept", async function () {
|
|
375
|
+
const root = await createProject();
|
|
376
|
+
await writeFile(
|
|
377
|
+
root,
|
|
378
|
+
"messages/auth/available.yml",
|
|
379
|
+
"description: Available\ntranslations:\n en: Available\n",
|
|
380
|
+
);
|
|
381
|
+
await writeFile(
|
|
382
|
+
root,
|
|
383
|
+
"messages/auth/also-available.yml",
|
|
384
|
+
"description: Also available\ntranslations:\n en: Also available\n",
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
const projectConfig = getProjectConfig(root);
|
|
388
|
+
const datasource = new Datasource(projectConfig, root);
|
|
389
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
390
|
+
target: "web",
|
|
391
|
+
locale: "en-US",
|
|
392
|
+
noStateFiles: true,
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
expect(datafiles[0].translations["auth.available"]).toEqual("Available");
|
|
396
|
+
expect(datafiles[0].translations["auth.also-available"]).toEqual("Also available");
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("only includes segments referenced by resolved message overrides", async function () {
|
|
400
|
+
const root = await createProject();
|
|
401
|
+
await writeFile(
|
|
402
|
+
root,
|
|
403
|
+
"segments/platform-ios.yml",
|
|
404
|
+
"description: iOS\nconditions:\n - attribute: platform\n operator: equals\n value: ios\n",
|
|
405
|
+
);
|
|
406
|
+
await writeFile(
|
|
407
|
+
root,
|
|
408
|
+
"segments/unused.yml",
|
|
409
|
+
"description: Unused\nconditions:\n - attribute: platform\n operator: equals\n value: unused\n",
|
|
410
|
+
);
|
|
411
|
+
await writeFile(
|
|
412
|
+
root,
|
|
413
|
+
"messages/auth/install.yml",
|
|
414
|
+
"description: Install\ntranslations:\n en: Install\n en-US: Install now\noverrides:\n - key: platform-web\n segments:\n or:\n - platform-web\n - not:\n - platform-ios\n translations:\n en-US: Install on this device\n",
|
|
415
|
+
);
|
|
416
|
+
await writeFile(
|
|
417
|
+
root,
|
|
418
|
+
"messages/auth/locale-only.yml",
|
|
419
|
+
"description: Locale only\ntranslations:\n en: Locale only\noverrides:\n - key: platform-web\n segments: unused\n translations:\n nl: Alleen Nederlands\n",
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
const projectConfig = getProjectConfig(root);
|
|
423
|
+
const datasource = new Datasource(projectConfig, root);
|
|
424
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
425
|
+
target: "web",
|
|
426
|
+
locale: "en-US",
|
|
427
|
+
noStateFiles: true,
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
expect(Object.keys(datafiles[0].segments).sort()).toEqual(["platform-ios", "platform-web"]);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it("stringifies datafile conditions and segment groups by default", async function () {
|
|
434
|
+
const root = await createProject();
|
|
435
|
+
await writeFile(
|
|
436
|
+
root,
|
|
437
|
+
"segments/platform-ios.yml",
|
|
438
|
+
"description: iOS\nconditions:\n - attribute: platform\n operator: equals\n value: ios\n",
|
|
439
|
+
);
|
|
440
|
+
await writeFile(
|
|
441
|
+
root,
|
|
442
|
+
"messages/auth/install.yml",
|
|
443
|
+
[
|
|
444
|
+
"description: Install",
|
|
445
|
+
"translations:",
|
|
446
|
+
" en: Install",
|
|
447
|
+
" en-US: Install now",
|
|
448
|
+
"overrides:",
|
|
449
|
+
" - key: audience",
|
|
450
|
+
" segments:",
|
|
451
|
+
" or:",
|
|
452
|
+
" - platform-web",
|
|
453
|
+
" - not:",
|
|
454
|
+
" - platform-ios",
|
|
455
|
+
" translations:",
|
|
456
|
+
" en-US: Install on this device",
|
|
457
|
+
" - key: plan",
|
|
458
|
+
" conditions:",
|
|
459
|
+
" - attribute: plan",
|
|
460
|
+
" operator: equals",
|
|
461
|
+
" value: pro",
|
|
462
|
+
" - attribute: region",
|
|
463
|
+
" operator: equals",
|
|
464
|
+
" value: EU",
|
|
465
|
+
" translations:",
|
|
466
|
+
" en-US: Install for Pro EU",
|
|
467
|
+
" - key: everyone",
|
|
468
|
+
' conditions: "*"',
|
|
469
|
+
' segments: "*"',
|
|
470
|
+
" translations:",
|
|
471
|
+
" en-US: Install for everyone",
|
|
472
|
+
"",
|
|
473
|
+
].join("\n"),
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
const projectConfig = getProjectConfig(root);
|
|
477
|
+
const datasource = new Datasource(projectConfig, root);
|
|
478
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
479
|
+
target: "web",
|
|
480
|
+
locale: "en-US",
|
|
481
|
+
noStateFiles: true,
|
|
482
|
+
});
|
|
483
|
+
const datafile = datafiles[0];
|
|
484
|
+
|
|
485
|
+
expect(datafile.segments["platform-web"].conditions).toEqual(
|
|
486
|
+
JSON.stringify({ attribute: "platform", operator: "equals", value: "web" }),
|
|
487
|
+
);
|
|
488
|
+
expect(datafile.segments["platform-ios"].conditions).toEqual(
|
|
489
|
+
JSON.stringify({ attribute: "platform", operator: "equals", value: "ios" }),
|
|
490
|
+
);
|
|
491
|
+
expect(datafile.messages["auth.install"].overrides).toEqual([
|
|
492
|
+
{
|
|
493
|
+
key: "audience",
|
|
494
|
+
segments: JSON.stringify({
|
|
495
|
+
or: ["platform-web", { not: ["platform-ios"] }],
|
|
496
|
+
}),
|
|
497
|
+
translation: "Install on this device",
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
key: "plan",
|
|
501
|
+
conditions: JSON.stringify([
|
|
502
|
+
{ attribute: "plan", operator: "equals", value: "pro" },
|
|
503
|
+
{ attribute: "region", operator: "equals", value: "EU" },
|
|
504
|
+
]),
|
|
505
|
+
translation: "Install for Pro EU",
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
key: "everyone",
|
|
509
|
+
conditions: "*",
|
|
510
|
+
segments: "*",
|
|
511
|
+
translation: "Install for everyone",
|
|
512
|
+
},
|
|
513
|
+
]);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it("preserves structured datafile conditions and segment groups when target stringify is disabled", async function () {
|
|
517
|
+
const root = await createProject();
|
|
518
|
+
await writeFile(
|
|
519
|
+
root,
|
|
520
|
+
"targets/web.yml",
|
|
521
|
+
"description: Web\nincludeMessages:\n - auth*\nlocales:\n - en-US\nstringify: false\n",
|
|
522
|
+
);
|
|
523
|
+
await writeFile(
|
|
524
|
+
root,
|
|
525
|
+
"segments/platform-ios.yml",
|
|
526
|
+
"description: iOS\nconditions:\n - attribute: platform\n operator: equals\n value: ios\n",
|
|
527
|
+
);
|
|
528
|
+
await writeFile(
|
|
529
|
+
root,
|
|
530
|
+
"messages/auth/install.yml",
|
|
531
|
+
[
|
|
532
|
+
"description: Install",
|
|
533
|
+
"translations:",
|
|
534
|
+
" en: Install",
|
|
535
|
+
" en-US: Install now",
|
|
536
|
+
"overrides:",
|
|
537
|
+
" - key: audience",
|
|
538
|
+
" segments:",
|
|
539
|
+
" or:",
|
|
540
|
+
" - platform-web",
|
|
541
|
+
" - platform-ios",
|
|
542
|
+
" translations:",
|
|
543
|
+
" en-US: Install on this device",
|
|
544
|
+
" - key: plan",
|
|
545
|
+
" conditions:",
|
|
546
|
+
" attribute: plan",
|
|
547
|
+
" operator: equals",
|
|
548
|
+
" value: pro",
|
|
549
|
+
" translations:",
|
|
550
|
+
" en-US: Install for Pro",
|
|
551
|
+
"",
|
|
552
|
+
].join("\n"),
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
const projectConfig = getProjectConfig(root);
|
|
556
|
+
const datasource = new Datasource(projectConfig, root);
|
|
557
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
558
|
+
target: "web",
|
|
559
|
+
locale: "en-US",
|
|
560
|
+
noStateFiles: true,
|
|
561
|
+
});
|
|
562
|
+
const datafile = datafiles[0];
|
|
563
|
+
|
|
564
|
+
expect(datafile.segments["platform-web"].conditions).toEqual({
|
|
565
|
+
attribute: "platform",
|
|
566
|
+
operator: "equals",
|
|
567
|
+
value: "web",
|
|
568
|
+
});
|
|
569
|
+
expect(datafile.messages["auth.install"].overrides).toEqual([
|
|
570
|
+
{
|
|
571
|
+
key: "audience",
|
|
572
|
+
segments: {
|
|
573
|
+
or: ["platform-web", "platform-ios"],
|
|
574
|
+
},
|
|
575
|
+
translation: "Install on this device",
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
key: "plan",
|
|
579
|
+
conditions: { attribute: "plan", operator: "equals", value: "pro" },
|
|
580
|
+
translation: "Install for Pro",
|
|
581
|
+
},
|
|
582
|
+
]);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it("applies target-level stringify independently across targets", async function () {
|
|
586
|
+
const root = await createProject();
|
|
587
|
+
await writeFile(
|
|
588
|
+
root,
|
|
589
|
+
"targets/structured.yml",
|
|
590
|
+
"description: Structured\nincludeMessages:\n - auth*\nlocales:\n - en-US\nstringify: false\n",
|
|
591
|
+
);
|
|
592
|
+
await writeFile(
|
|
593
|
+
root,
|
|
594
|
+
"messages/auth/install.yml",
|
|
595
|
+
[
|
|
596
|
+
"description: Install",
|
|
597
|
+
"translations:",
|
|
598
|
+
" en: Install",
|
|
599
|
+
" en-US: Install now",
|
|
600
|
+
"overrides:",
|
|
601
|
+
" - key: plan",
|
|
602
|
+
" conditions:",
|
|
603
|
+
" attribute: plan",
|
|
604
|
+
" operator: equals",
|
|
605
|
+
" value: pro",
|
|
606
|
+
" translations:",
|
|
607
|
+
" en-US: Install for Pro",
|
|
608
|
+
"",
|
|
609
|
+
].join("\n"),
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
const projectConfig = getProjectConfig(root);
|
|
613
|
+
const datasource = new Datasource(projectConfig, root);
|
|
614
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
615
|
+
locale: "en-US",
|
|
616
|
+
noStateFiles: true,
|
|
617
|
+
});
|
|
618
|
+
const compact = datafiles.find((datafile) => datafile.target === "web");
|
|
619
|
+
const structured = datafiles.find((datafile) => datafile.target === "structured");
|
|
620
|
+
|
|
621
|
+
expect(compact?.messages["auth.install"].overrides?.[0].conditions).toEqual(
|
|
622
|
+
JSON.stringify({ attribute: "plan", operator: "equals", value: "pro" }),
|
|
623
|
+
);
|
|
624
|
+
expect(structured?.messages["auth.install"].overrides?.[0].conditions).toEqual({
|
|
625
|
+
attribute: "plan",
|
|
626
|
+
operator: "equals",
|
|
627
|
+
value: "pro",
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it("writes pretty datafiles for targets with pretty enabled", async function () {
|
|
632
|
+
const root = await createProject();
|
|
633
|
+
await writeFile(
|
|
634
|
+
root,
|
|
635
|
+
"targets/web.yml",
|
|
636
|
+
"description: Web\nincludeMessages:\n - auth*\nlocales:\n - en-US\npretty: true\n",
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
const projectConfig = getProjectConfig(root);
|
|
640
|
+
const datasource = new Datasource(projectConfig, root);
|
|
641
|
+
|
|
642
|
+
await buildProject(projectConfig, datasource, {
|
|
643
|
+
target: "web",
|
|
644
|
+
locale: "en-US",
|
|
645
|
+
noStateFiles: true,
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
const content = await fs.promises.readFile(
|
|
649
|
+
path.join(root, "datafiles/messagevisor-web-en-US.json"),
|
|
650
|
+
"utf8",
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
expect(content.startsWith("{\n")).toEqual(true);
|
|
654
|
+
expect(content).toContain(' "schemaVersion":');
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it("uses target-level pretty for JSON output and allows CLI pretty to force formatting", async function () {
|
|
658
|
+
const root = await createProject();
|
|
659
|
+
const projectConfig = getProjectConfig(root);
|
|
660
|
+
const datasource = new Datasource(projectConfig, root);
|
|
661
|
+
const logSpy = jest.spyOn(console, "log").mockImplementation(() => undefined);
|
|
662
|
+
|
|
663
|
+
try {
|
|
664
|
+
await buildProject(projectConfig, datasource, {
|
|
665
|
+
target: "web",
|
|
666
|
+
locale: "en-US",
|
|
667
|
+
noStateFiles: true,
|
|
668
|
+
json: true,
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
expect(logSpy.mock.calls[0][0]).not.toContain("\n");
|
|
672
|
+
|
|
673
|
+
await writeFile(
|
|
674
|
+
root,
|
|
675
|
+
"targets/web.yml",
|
|
676
|
+
"description: Web\nincludeMessages:\n - auth*\nlocales:\n - en-US\npretty: true\n",
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
await buildProject(projectConfig, datasource, {
|
|
680
|
+
target: "web",
|
|
681
|
+
locale: "en-US",
|
|
682
|
+
noStateFiles: true,
|
|
683
|
+
json: true,
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
expect(logSpy.mock.calls[1][0]).toContain("\n");
|
|
687
|
+
|
|
688
|
+
await writeFile(
|
|
689
|
+
root,
|
|
690
|
+
"targets/web.yml",
|
|
691
|
+
"description: Web\nincludeMessages:\n - auth*\nlocales:\n - en-US\n",
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
await buildProject(projectConfig, datasource, {
|
|
695
|
+
target: "web",
|
|
696
|
+
locale: "en-US",
|
|
697
|
+
noStateFiles: true,
|
|
698
|
+
json: true,
|
|
699
|
+
pretty: true,
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
expect(logSpy.mock.calls[2][0]).toContain("\n");
|
|
703
|
+
} finally {
|
|
704
|
+
logSpy.mockRestore();
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it("uses target-level revision hashes and still writes state revisions", async function () {
|
|
709
|
+
const root = await createProject();
|
|
710
|
+
await writeFile(
|
|
711
|
+
root,
|
|
712
|
+
"targets/web.yml",
|
|
713
|
+
"description: Web\nincludeMessages:\n - auth*\nlocales:\n - en-US\nrevisionFromHash: true\n",
|
|
714
|
+
);
|
|
715
|
+
|
|
716
|
+
const projectConfig = getProjectConfig(root);
|
|
717
|
+
const datasource = new Datasource(projectConfig, root);
|
|
718
|
+
const datafiles = await buildProject(projectConfig, datasource);
|
|
719
|
+
|
|
720
|
+
expect(datafiles[0].revision).toMatch(/^[a-f0-9]{40}$/);
|
|
721
|
+
expect(datafiles[0].revision).not.toEqual("1");
|
|
722
|
+
expect(await datasource.readRevision()).toEqual("1");
|
|
723
|
+
|
|
724
|
+
const explicit = await buildProject(projectConfig, datasource, {
|
|
725
|
+
target: "web",
|
|
726
|
+
locale: "en-US",
|
|
727
|
+
revision: "manual",
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
expect(explicit[0].revision).toMatch(/^[a-f0-9]{40}$/);
|
|
731
|
+
expect(explicit[0].revision).not.toEqual("manual");
|
|
732
|
+
expect(await datasource.readRevision()).toEqual("1");
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
it("applies target-level datafile options inside sets", async function () {
|
|
736
|
+
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "messagevisor-"));
|
|
737
|
+
|
|
738
|
+
await writeFile(root, "messagevisor.config.js", "module.exports = { sets: true };\n");
|
|
739
|
+
await writeFile(root, "sets/staging/locales/en.yml", "description: English\n");
|
|
740
|
+
await writeFile(
|
|
741
|
+
root,
|
|
742
|
+
"sets/staging/targets/web.yml",
|
|
743
|
+
"description: Web\nincludeMessages:\n - auth*\nlocales:\n - en\nstringify: false\npretty: true\nrevisionFromHash: true\n",
|
|
744
|
+
);
|
|
745
|
+
await writeFile(
|
|
746
|
+
root,
|
|
747
|
+
"sets/staging/messages/auth/signin.yml",
|
|
748
|
+
"description: Sign in\ntranslations:\n en: Sign in\noverrides:\n - key: pro\n conditions:\n attribute: plan\n operator: equals\n value: pro\n translations:\n en: Sign in Pro\n",
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
const projectConfig = getProjectConfig(root);
|
|
752
|
+
const datasource = new Datasource(projectConfig, root);
|
|
753
|
+
const datafiles = await buildProjectSets(projectConfig, datasource);
|
|
754
|
+
|
|
755
|
+
expect(datafiles[0].revision).toMatch(/^[a-f0-9]{40}$/);
|
|
756
|
+
expect(datafiles[0].messages["auth.signin"].overrides?.[0].conditions).toEqual({
|
|
757
|
+
attribute: "plan",
|
|
758
|
+
operator: "equals",
|
|
759
|
+
value: "pro",
|
|
760
|
+
});
|
|
761
|
+
expect(await datasource.readRevision()).toEqual("1");
|
|
762
|
+
expect(await datasource.forSet("staging").readRevision()).toEqual("1");
|
|
763
|
+
|
|
764
|
+
const content = await fs.promises.readFile(
|
|
765
|
+
path.join(root, "datafiles/staging/messagevisor-web-en.json"),
|
|
766
|
+
"utf8",
|
|
767
|
+
);
|
|
768
|
+
expect(content.startsWith("{\n")).toEqual(true);
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
it("targets overrides and segments using context defined in the target", async function () {
|
|
772
|
+
const root = await createProject();
|
|
773
|
+
await writeFile(
|
|
774
|
+
root,
|
|
775
|
+
"targets/web.yml",
|
|
776
|
+
"description: Web\nincludeMessages:\n - auth*\nlocales:\n - en-US\ncontext:\n platform: web\n",
|
|
777
|
+
);
|
|
778
|
+
await writeFile(
|
|
779
|
+
root,
|
|
780
|
+
"segments/platform-ios.yml",
|
|
781
|
+
"description: iOS\nconditions:\n - attribute: platform\n operator: equals\n value: ios\n",
|
|
782
|
+
);
|
|
783
|
+
await writeFile(
|
|
784
|
+
root,
|
|
785
|
+
"segments/plan-pro.yml",
|
|
786
|
+
"description: Pro plan\nconditions:\n - attribute: plan\n operator: equals\n value: pro\n",
|
|
787
|
+
);
|
|
788
|
+
await writeFile(
|
|
789
|
+
root,
|
|
790
|
+
"messages/auth/install.yml",
|
|
791
|
+
"description: Install\ntranslations:\n en: Install\n en-US: Install now\noverrides:\n - key: platform-web\n segments: platform-web\n translations:\n en-US: Install on web\n - key: platform-ios\n segments: platform-ios\n translations:\n en-US: Install on iOS\n",
|
|
792
|
+
);
|
|
793
|
+
await writeFile(
|
|
794
|
+
root,
|
|
795
|
+
"messages/auth/pro.yml",
|
|
796
|
+
"description: Pro\ntranslations:\n en: Pro\n en-US: Pro\noverrides:\n - key: platform-web\n segments:\n and:\n - platform-web\n - plan-pro\n translations:\n en-US: Pro on web\n",
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
const projectConfig = getProjectConfig(root);
|
|
800
|
+
const datasource = new Datasource(projectConfig, root);
|
|
801
|
+
const datafiles = await buildProject(projectConfig, datasource, {
|
|
802
|
+
target: "web",
|
|
803
|
+
locale: "en-US",
|
|
804
|
+
noStateFiles: true,
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
expect(datafiles[0].messages["auth.install"].overrides).toEqual([
|
|
808
|
+
{
|
|
809
|
+
key: "platform-web",
|
|
810
|
+
translation: "Install on web",
|
|
811
|
+
},
|
|
812
|
+
]);
|
|
813
|
+
expect(datafiles[0].messages["auth.pro"].overrides).toEqual([
|
|
814
|
+
{
|
|
815
|
+
key: "platform-web",
|
|
816
|
+
segments: "plan-pro",
|
|
817
|
+
translation: "Pro on web",
|
|
818
|
+
},
|
|
819
|
+
]);
|
|
820
|
+
expect(Object.keys(datafiles[0].segments)).toEqual(["plan-pro"]);
|
|
821
|
+
});
|
|
822
|
+
});
|