@multiplatform.one/i18n 6.0.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 +17 -0
- package/LICENSE +201 -0
- package/package.json +45 -0
- package/src/createI18nConfig.ts +77 -0
- package/src/detectLanguage.spec.ts +83 -0
- package/src/detectLanguage.ts +48 -0
- package/src/env.d.ts +6 -0
- package/src/formatters.spec.ts +101 -0
- package/src/formatters.ts +56 -0
- package/src/frappeBackend.ts +50 -0
- package/src/index.ts +15 -0
- package/src/localePersistence.ts +22 -0
- package/src/useLanguage.ts +27 -0
- package/tests/createI18nConfig.test.ts +58 -0
- package/tests/detectLanguage.test.ts +66 -0
- package/tests/formatters.test.ts +50 -0
- package/tests/frappeBackend.test.ts +57 -0
- package/tests/frappeBackendEnabled.test.ts +111 -0
- package/tests/integrationFrappeDisabled.test.ts +182 -0
- package/tests/integrationFrappeEnabled.test.ts +232 -0
- package/tests/integrationLanguagePersistence.test.ts +241 -0
- package/tests/integrationTreeshaking.test.ts +126 -0
- package/tests/localFallbackDictionaries.test.ts +294 -0
- package/tests/localePersistence.test.ts +65 -0
- package/tests/makeMkFlag.test.ts +25 -0
- package/tests/useLanguagePersistence.test.ts +74 -0
- package/tsconfig.json +11 -0
- package/vitest.config.mjs +13 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import i18next from "i18next";
|
|
3
|
+
import enCommon from "../../../packages/i18n/en/common.json";
|
|
4
|
+
import teCommon from "../../../packages/i18n/te/common.json";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Complete inventory of flat English string keys used in frappe-ui components
|
|
8
|
+
* (Groups 5-7). Each string is used as its own translation key following
|
|
9
|
+
* Frappe's __() convention.
|
|
10
|
+
*/
|
|
11
|
+
const frappeUiStrings = [
|
|
12
|
+
// FrappeForm
|
|
13
|
+
"Validation error occurred",
|
|
14
|
+
"Failed to save document",
|
|
15
|
+
"Delete this {{doctype}}?",
|
|
16
|
+
"Failed to delete",
|
|
17
|
+
"Loading {{doctype}}...",
|
|
18
|
+
"Loading form...",
|
|
19
|
+
"Not Saved",
|
|
20
|
+
"Delete",
|
|
21
|
+
"Cancel",
|
|
22
|
+
"Saving...",
|
|
23
|
+
"Save",
|
|
24
|
+
"Activity",
|
|
25
|
+
|
|
26
|
+
// FrappeListView
|
|
27
|
+
"Name",
|
|
28
|
+
"Loading {{doctype}} schema...",
|
|
29
|
+
"List view",
|
|
30
|
+
"Table view",
|
|
31
|
+
"Refresh",
|
|
32
|
+
"Add {{doctype}}",
|
|
33
|
+
"Filters",
|
|
34
|
+
"Saved Filters",
|
|
35
|
+
|
|
36
|
+
// FrappeReportBuilder
|
|
37
|
+
"Count",
|
|
38
|
+
"Sum",
|
|
39
|
+
"Average",
|
|
40
|
+
"Min",
|
|
41
|
+
"Max",
|
|
42
|
+
"Bar chart",
|
|
43
|
+
"Toggle Columns",
|
|
44
|
+
"Group By:",
|
|
45
|
+
"None",
|
|
46
|
+
"Clear group by",
|
|
47
|
+
"Load",
|
|
48
|
+
"Saved Reports",
|
|
49
|
+
"Delete {{name}}",
|
|
50
|
+
"Columns",
|
|
51
|
+
"Chart",
|
|
52
|
+
"Export",
|
|
53
|
+
"Save Report",
|
|
54
|
+
"Report name",
|
|
55
|
+
"Loading data...",
|
|
56
|
+
"No data to display",
|
|
57
|
+
"(empty)",
|
|
58
|
+
"record",
|
|
59
|
+
"records",
|
|
60
|
+
"Yes",
|
|
61
|
+
"No",
|
|
62
|
+
|
|
63
|
+
// FrappeTreeView
|
|
64
|
+
"This DocType does not support tree view",
|
|
65
|
+
"Loading {{doctype}} tree...",
|
|
66
|
+
"Updating...",
|
|
67
|
+
"Expand All",
|
|
68
|
+
"Collapse All",
|
|
69
|
+
"No items found",
|
|
70
|
+
|
|
71
|
+
// FrappeImageView
|
|
72
|
+
"No image field configured for {{doctype}}",
|
|
73
|
+
|
|
74
|
+
// FrappeCalendar
|
|
75
|
+
"Today",
|
|
76
|
+
"Month",
|
|
77
|
+
"Week",
|
|
78
|
+
"Day",
|
|
79
|
+
"Events",
|
|
80
|
+
"+{{n}} more",
|
|
81
|
+
|
|
82
|
+
// FrappeKanban
|
|
83
|
+
"Drop here",
|
|
84
|
+
"No items",
|
|
85
|
+
|
|
86
|
+
// FrappeDashboard
|
|
87
|
+
"vs {{previousValue}}",
|
|
88
|
+
"Loading dashboard...",
|
|
89
|
+
"Last updated: {{time}}",
|
|
90
|
+
|
|
91
|
+
// FrappeList
|
|
92
|
+
"No {{doctype}} records found",
|
|
93
|
+
"{{n}} selected",
|
|
94
|
+
"Select all",
|
|
95
|
+
"Showing {{n}} items with virtual scrolling",
|
|
96
|
+
|
|
97
|
+
// FrappeTable
|
|
98
|
+
"Error loading {{doctype}}",
|
|
99
|
+
|
|
100
|
+
// ViewSwitcher
|
|
101
|
+
"List",
|
|
102
|
+
"Table",
|
|
103
|
+
"Report",
|
|
104
|
+
"Image",
|
|
105
|
+
"Tree",
|
|
106
|
+
"Kanban",
|
|
107
|
+
"Calendar",
|
|
108
|
+
|
|
109
|
+
// FormSidebar
|
|
110
|
+
"Write",
|
|
111
|
+
"Read",
|
|
112
|
+
"Submit",
|
|
113
|
+
"Share",
|
|
114
|
+
"Assigned To",
|
|
115
|
+
"Add assignment",
|
|
116
|
+
"No assignments",
|
|
117
|
+
"Attachments",
|
|
118
|
+
"Tags",
|
|
119
|
+
"Shared With",
|
|
120
|
+
"Add share",
|
|
121
|
+
"Not shared",
|
|
122
|
+
"Info",
|
|
123
|
+
"Created by {{owner}} on {{date}}",
|
|
124
|
+
"Last edited by {{modified_by}} on {{date}}",
|
|
125
|
+
|
|
126
|
+
// DeskShell / DeskFormPage / DeskSidebar / DeskListPage
|
|
127
|
+
"Desk",
|
|
128
|
+
"No DocType specified",
|
|
129
|
+
"New",
|
|
130
|
+
"Modules",
|
|
131
|
+
"Recent",
|
|
132
|
+
"Favorites",
|
|
133
|
+
|
|
134
|
+
// Awesomebar
|
|
135
|
+
"Search or type a command (Ctrl+K)",
|
|
136
|
+
"Result:",
|
|
137
|
+
"Commands",
|
|
138
|
+
"Navigation",
|
|
139
|
+
"Documents",
|
|
140
|
+
"Searching documents...",
|
|
141
|
+
|
|
142
|
+
// FilterManager
|
|
143
|
+
"Filter name is required",
|
|
144
|
+
"Failed to save filter",
|
|
145
|
+
"Save Filter",
|
|
146
|
+
"Save the current filter configuration for quick access later.",
|
|
147
|
+
"Filter Name",
|
|
148
|
+
"e.g., Big Pokemon",
|
|
149
|
+
"Make this filter global (visible to all users)",
|
|
150
|
+
"Current filters: {{n}} active",
|
|
151
|
+
|
|
152
|
+
// SavedFilterDropdown
|
|
153
|
+
"(Global)",
|
|
154
|
+
|
|
155
|
+
// Attachments
|
|
156
|
+
"B",
|
|
157
|
+
"KB",
|
|
158
|
+
"MB",
|
|
159
|
+
"GB",
|
|
160
|
+
"File size exceeds {{limit}} limit",
|
|
161
|
+
"Failed to read file",
|
|
162
|
+
"Upload failed",
|
|
163
|
+
"Delete failed",
|
|
164
|
+
"No attachments",
|
|
165
|
+
"Uploading...",
|
|
166
|
+
"Upload",
|
|
167
|
+
"Download",
|
|
168
|
+
"Close",
|
|
169
|
+
|
|
170
|
+
// Tags
|
|
171
|
+
"Add tag...",
|
|
172
|
+
'Tag "{{name}}" is already applied',
|
|
173
|
+
"Failed to add tag",
|
|
174
|
+
"Failed to remove tag",
|
|
175
|
+
"No tags",
|
|
176
|
+
'Create "{{value}}"',
|
|
177
|
+
"Suggestions",
|
|
178
|
+
"No available tags",
|
|
179
|
+
"Add Tag",
|
|
180
|
+
"Click + to add tags",
|
|
181
|
+
|
|
182
|
+
// Timeline
|
|
183
|
+
"just now",
|
|
184
|
+
"{{count}} minute ago",
|
|
185
|
+
"{{count}} minutes ago",
|
|
186
|
+
"{{count}} hour ago",
|
|
187
|
+
"{{count}} hours ago",
|
|
188
|
+
"{{count}} day ago",
|
|
189
|
+
"{{count}} days ago",
|
|
190
|
+
"{{count}} week ago",
|
|
191
|
+
"{{count}} weeks ago",
|
|
192
|
+
"{{count}} month ago",
|
|
193
|
+
"{{count}} months ago",
|
|
194
|
+
"No activity yet",
|
|
195
|
+
"+{{count}} more changes",
|
|
196
|
+
"made changes",
|
|
197
|
+
"added an attachment",
|
|
198
|
+
"assigned",
|
|
199
|
+
"Add a comment...",
|
|
200
|
+
"Add Comment",
|
|
201
|
+
"Sending...",
|
|
202
|
+
"Comment",
|
|
203
|
+
"Failed to add comment",
|
|
204
|
+
"{{count}} entry",
|
|
205
|
+
"{{count}} entries",
|
|
206
|
+
|
|
207
|
+
// ChildTable
|
|
208
|
+
"#",
|
|
209
|
+
"Add Row",
|
|
210
|
+
|
|
211
|
+
// SchemaForm
|
|
212
|
+
"Unsupported field type: {{fieldtype}}",
|
|
213
|
+
|
|
214
|
+
// Toolbar
|
|
215
|
+
"Loading...",
|
|
216
|
+
] as const;
|
|
217
|
+
|
|
218
|
+
describe("Group 8: Local Fallback Dictionaries", () => {
|
|
219
|
+
it("Test 1: All frappe-ui strings exist as keys in en/common.json", () => {
|
|
220
|
+
const enKeys = Object.keys(enCommon);
|
|
221
|
+
const missing: string[] = [];
|
|
222
|
+
|
|
223
|
+
for (const str of frappeUiStrings) {
|
|
224
|
+
if (!enKeys.includes(str)) {
|
|
225
|
+
missing.push(str);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
expect(missing, `Missing keys in en/common.json:\n ${missing.join("\n ")}`).toEqual([]);
|
|
230
|
+
|
|
231
|
+
// Also verify identity mapping: for English, key === value
|
|
232
|
+
for (const str of frappeUiStrings) {
|
|
233
|
+
expect((enCommon as Record<string, unknown>)[str]).toBe(str);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("Test 2: te/common.json has the same key set as en/common.json", () => {
|
|
238
|
+
const enKeys = Object.keys(enCommon).sort();
|
|
239
|
+
const teKeys = Object.keys(teCommon).sort();
|
|
240
|
+
|
|
241
|
+
// Every key in en must also appear in te
|
|
242
|
+
const missingInTe = enKeys.filter((k) => !teKeys.includes(k));
|
|
243
|
+
expect(
|
|
244
|
+
missingInTe,
|
|
245
|
+
`Keys present in en/common.json but missing in te/common.json:\n ${missingInTe.join("\n ")}`,
|
|
246
|
+
).toEqual([]);
|
|
247
|
+
|
|
248
|
+
// Every key in te must also appear in en (no stale keys)
|
|
249
|
+
const extraInTe = teKeys.filter((k) => !enKeys.includes(k));
|
|
250
|
+
expect(
|
|
251
|
+
extraInTe,
|
|
252
|
+
`Keys present in te/common.json but missing in en/common.json:\n ${extraInTe.join("\n ")}`,
|
|
253
|
+
).toEqual([]);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('Test 3: t("Filters") resolves correctly in English locale', async () => {
|
|
257
|
+
const instance = i18next.createInstance();
|
|
258
|
+
await instance.init({
|
|
259
|
+
lng: "en",
|
|
260
|
+
defaultNS: "common",
|
|
261
|
+
ns: ["common"],
|
|
262
|
+
resources: {
|
|
263
|
+
en: { common: enCommon },
|
|
264
|
+
te: { common: teCommon },
|
|
265
|
+
},
|
|
266
|
+
interpolation: { escapeValue: false },
|
|
267
|
+
returnNull: false,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Flat English string keys resolve via identity mapping
|
|
271
|
+
expect(instance.t("Filters")).toBe("Filters");
|
|
272
|
+
expect(instance.t("Save")).toBe("Save");
|
|
273
|
+
expect(instance.t("Loading...")).toBe("Loading...");
|
|
274
|
+
|
|
275
|
+
// Interpolated keys resolve with variable substitution
|
|
276
|
+
expect(instance.t("Loading {{doctype}}...", { doctype: "Pokemon" })).toBe("Loading Pokemon...");
|
|
277
|
+
expect(instance.t("{{n}} selected", { n: 5 })).toBe("5 selected");
|
|
278
|
+
|
|
279
|
+
// Namespaced keys still work
|
|
280
|
+
expect(instance.t("common.appName")).toBe("multiplatform.one");
|
|
281
|
+
expect(instance.t("screens.pokemon.title")).toBe("Pokemon");
|
|
282
|
+
|
|
283
|
+
// Switch to Telugu and verify translations differ from English
|
|
284
|
+
await instance.changeLanguage("te");
|
|
285
|
+
const teFilters = (teCommon as Record<string, unknown>)["Filters"];
|
|
286
|
+
const teSave = (teCommon as Record<string, unknown>)["Save"];
|
|
287
|
+
expect(instance.t("Filters")).toBe(teFilters);
|
|
288
|
+
expect(instance.t("Save")).toBe(teSave);
|
|
289
|
+
expect(instance.t("Filters")).not.toBe("Filters");
|
|
290
|
+
expect(instance.t("Save")).not.toBe("Save");
|
|
291
|
+
// appName stays the same across locales
|
|
292
|
+
expect(instance.t("common.appName")).toBe("multiplatform.one");
|
|
293
|
+
});
|
|
294
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { getPersistedLanguage, persistLanguage, parseCookieValue } from "../src/localePersistence";
|
|
3
|
+
|
|
4
|
+
describe("localePersistence", () => {
|
|
5
|
+
// Test 1: Cookie set/get roundtrip with preferred_language
|
|
6
|
+
describe("cookie roundtrip", () => {
|
|
7
|
+
let originalDocument: typeof globalThis.document;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
// Mock document.cookie for Node.js test environment
|
|
11
|
+
originalDocument = globalThis.document;
|
|
12
|
+
let cookieStore = "";
|
|
13
|
+
Object.defineProperty(globalThis, "document", {
|
|
14
|
+
value: {
|
|
15
|
+
get cookie() {
|
|
16
|
+
return cookieStore;
|
|
17
|
+
},
|
|
18
|
+
set cookie(val: string) {
|
|
19
|
+
// Simple cookie store: accumulate cookies separated by '; '
|
|
20
|
+
const name = val.split("=")[0];
|
|
21
|
+
const parts = cookieStore.split("; ").filter((c) => !c.startsWith(`${name}=`));
|
|
22
|
+
parts.push(val.split(";")[0]);
|
|
23
|
+
cookieStore = parts.filter(Boolean).join("; ");
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
writable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
Object.defineProperty(globalThis, "document", {
|
|
33
|
+
value: originalDocument,
|
|
34
|
+
writable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("persists and reads back a language cookie", () => {
|
|
40
|
+
expect(getPersistedLanguage()).toBeNull();
|
|
41
|
+
persistLanguage("es");
|
|
42
|
+
expect(getPersistedLanguage()).toBe("es");
|
|
43
|
+
persistLanguage("te");
|
|
44
|
+
expect(getPersistedLanguage()).toBe("te");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("parseCookieValue", () => {
|
|
49
|
+
it("extracts a named cookie from a header string", () => {
|
|
50
|
+
const header = "session_id=abc123; preferred_language=fr; theme=dark";
|
|
51
|
+
expect(parseCookieValue(header, "preferred_language")).toBe("fr");
|
|
52
|
+
expect(parseCookieValue(header, "session_id")).toBe("abc123");
|
|
53
|
+
expect(parseCookieValue(header, "theme")).toBe("dark");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("returns null for missing cookie", () => {
|
|
57
|
+
expect(parseCookieValue("session_id=abc", "preferred_language")).toBeNull();
|
|
58
|
+
expect(parseCookieValue("", "preferred_language")).toBeNull();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("decodes URI-encoded values", () => {
|
|
62
|
+
expect(parseCookieValue("preferred_language=zh-CN", "preferred_language")).toBe("zh-CN");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const rootdir = path.resolve(__dirname, "../../..");
|
|
6
|
+
|
|
7
|
+
describe("make.mk FRAPPE_APP_EXISTS flag", () => {
|
|
8
|
+
// Test 1: Verify the Makefile variable detects apps/frappe directory presence
|
|
9
|
+
it("sets VITE_FRAPPE_ENABLED=true when apps/frappe exists", () => {
|
|
10
|
+
// Use --eval to print the evaluated variable value
|
|
11
|
+
const result = execSync(
|
|
12
|
+
`make -f make.mk rootdir=${rootdir} --eval='print-flag: ; @echo $(VITE_FRAPPE_ENABLED)' print-flag 2>/dev/null`,
|
|
13
|
+
{ cwd: rootdir, encoding: "utf-8", env: { ...process.env, VITE_FRAPPE_ENABLED: undefined } },
|
|
14
|
+
).trim();
|
|
15
|
+
expect(result).toBe("true");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("FRAPPE_APP_EXISTS reflects directory presence", () => {
|
|
19
|
+
const result = execSync(
|
|
20
|
+
`make -f make.mk rootdir=${rootdir} --eval='print-flag: ; @echo $(FRAPPE_APP_EXISTS)' print-flag 2>/dev/null`,
|
|
21
|
+
{ cwd: rootdir, encoding: "utf-8" },
|
|
22
|
+
).trim();
|
|
23
|
+
expect(result).toBe("true");
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, afterEach } from "vitest";
|
|
2
|
+
|
|
3
|
+
// Test 4: useLanguage persists to cookie when language changes
|
|
4
|
+
// We test the integration by verifying that persistLanguage is called
|
|
5
|
+
// when the changeLanguage callback is invoked.
|
|
6
|
+
describe("useLanguage persistence", () => {
|
|
7
|
+
let cookieStore: string;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
cookieStore = "";
|
|
11
|
+
Object.defineProperty(globalThis, "document", {
|
|
12
|
+
value: {
|
|
13
|
+
get cookie() {
|
|
14
|
+
return cookieStore;
|
|
15
|
+
},
|
|
16
|
+
set cookie(val: string) {
|
|
17
|
+
const name = val.split("=")[0];
|
|
18
|
+
const parts = cookieStore.split("; ").filter((c) => c && !c.startsWith(`${name}=`));
|
|
19
|
+
parts.push(val.split(";")[0]);
|
|
20
|
+
cookieStore = parts.filter(Boolean).join("; ");
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
writable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
Object.defineProperty(globalThis, "document", {
|
|
30
|
+
value: undefined,
|
|
31
|
+
writable: true,
|
|
32
|
+
configurable: true,
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("persistLanguage sets the preferred_language cookie", async () => {
|
|
37
|
+
const { persistLanguage, getPersistedLanguage } = await import("../src/locale_persistence");
|
|
38
|
+
|
|
39
|
+
// Before: no cookie
|
|
40
|
+
expect(getPersistedLanguage()).toBeNull();
|
|
41
|
+
|
|
42
|
+
// Simulate what useLanguage does when setLanguage is called
|
|
43
|
+
persistLanguage("es");
|
|
44
|
+
expect(getPersistedLanguage()).toBe("es");
|
|
45
|
+
|
|
46
|
+
// Change again
|
|
47
|
+
persistLanguage("de");
|
|
48
|
+
expect(getPersistedLanguage()).toBe("de");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("cookie string contains correct attributes", async () => {
|
|
52
|
+
const { persistLanguage } = await import("../src/locale_persistence");
|
|
53
|
+
// Capture the raw cookie string set on document
|
|
54
|
+
let lastCookie = "";
|
|
55
|
+
Object.defineProperty(globalThis, "document", {
|
|
56
|
+
value: {
|
|
57
|
+
get cookie() {
|
|
58
|
+
return cookieStore;
|
|
59
|
+
},
|
|
60
|
+
set cookie(val: string) {
|
|
61
|
+
lastCookie = val;
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
writable: true,
|
|
65
|
+
configurable: true,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
persistLanguage("fr");
|
|
69
|
+
expect(lastCookie).toContain("preferred_language=fr");
|
|
70
|
+
expect(lastCookie).toContain("path=/");
|
|
71
|
+
expect(lastCookie).toContain("max-age=");
|
|
72
|
+
expect(lastCookie).toContain("samesite=lax");
|
|
73
|
+
});
|
|
74
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
|
|
3
|
+
// Ensure VITE_FRAPPE_ENABLED is "false" in test env by default
|
|
4
|
+
process.env.VITE_FRAPPE_ENABLED = "false";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
test: {
|
|
8
|
+
environment: "jsdom",
|
|
9
|
+
testTimeout: 10000,
|
|
10
|
+
include: ["tests/**/*.test.{ts,tsx}", "src/**/*.spec.{ts,tsx}"],
|
|
11
|
+
exclude: ["**/node_modules/**", "**/dist/**"],
|
|
12
|
+
},
|
|
13
|
+
});
|