Skip to content

Commit b1a178f

Browse files
authored
Merge pull request #1570 from ngx-translate/feat/http-loader/ressources
feat(http-loader): ressources
2 parents 22c2162 + d53e398 commit b1a178f

File tree

7 files changed

+295
-26
lines changed

7 files changed

+295
-26
lines changed

projects/http-loader/src/lib/http-loader.spec.ts

Lines changed: 200 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,38 @@ import { TestBed } from "@angular/core/testing";
44
import { provideTranslateService, TranslateService, Translation } from "@ngx-translate/core";
55
import {
66
provideTranslateHttpLoader,
7+
provideTranslateMultiHttpLoader,
78
TranslateHttpLoader,
89
TranslateHttpLoaderConfig,
10+
TranslateMultiHttpLoaderConfig,
911
} from "../public-api";
1012
import { MarkerInterceptor } from "../test-helper/marker-interceptor";
1113

1214
describe("TranslateHttpLoader (HttpClient)", () => {
1315
let translate: TranslateService;
1416
let http: HttpTestingController;
1517

16-
const prepare = (config: Partial<TranslateHttpLoaderConfig> = {}) => {
18+
const prepareMulti = (config: Partial<TranslateMultiHttpLoaderConfig> = {}) => {
19+
TestBed.configureTestingModule({
20+
providers: [
21+
MarkerInterceptor,
22+
{
23+
provide: HTTP_INTERCEPTORS,
24+
useExisting: MarkerInterceptor,
25+
multi: true,
26+
},
27+
provideHttpClient(withInterceptorsFromDi()),
28+
provideHttpClientTesting(),
29+
provideTranslateService(),
30+
provideTranslateMultiHttpLoader(config),
31+
],
32+
});
33+
34+
translate = TestBed.inject(TranslateService);
35+
http = TestBed.inject(HttpTestingController);
36+
};
37+
38+
const prepareSingle = (config: Partial<TranslateHttpLoaderConfig> = {}) => {
1739
TestBed.configureTestingModule({
1840
providers: [
1941
MarkerInterceptor,
@@ -38,29 +60,44 @@ describe("TranslateHttpLoader (HttpClient)", () => {
3860
});
3961

4062
it("should be able to provide TranslateHttpLoader", () => {
41-
prepare();
63+
prepareSingle();
4264
expect(TranslateHttpLoader).toBeDefined();
4365
expect(translate.currentLoader).toBeDefined();
4466
expect(translate.currentLoader instanceof TranslateHttpLoader).toBeTruthy();
4567
});
4668

4769
describe("Config", () => {
4870
it("uses prefix", () => {
49-
prepare({ prefix: "XXXX/" });
71+
prepareSingle({ prefix: "XXXX/" });
72+
translate.use("en");
73+
http.expectOne("XXXX/en.json").flush({});
74+
expect(true).toBeTruthy(); // http.expectOne() is not detected by jasmine...
75+
});
76+
77+
it("uses multipleHttpLoader prefix", () => {
78+
prepareMulti({ resources: ["XXXX/", "YYYY/"] });
5079
translate.use("en");
5180
http.expectOne("XXXX/en.json").flush({});
81+
http.expectOne("YYYY/en.json").flush({});
5282
expect(true).toBeTruthy(); // http.expectOne() is not detected by jasmine...
5383
});
5484

5585
it("uses suffix", () => {
56-
prepare({ suffix: ".XXXX" });
86+
prepareSingle({ suffix: ".XXXX" });
87+
translate.use("en");
88+
http.expectOne("/assets/i18n/en.XXXX").flush({});
89+
expect(true).toBeTruthy(); // http.expectOne() is not detected by jasmine...
90+
});
91+
92+
it("uses multipleHttpLoader suffix", () => {
93+
prepareMulti({ resources: [{ prefix: "/assets/i18n/", suffix: ".XXXX" }] });
5794
translate.use("en");
5895
http.expectOne("/assets/i18n/en.XXXX").flush({});
5996
expect(true).toBeTruthy(); // http.expectOne() is not detected by jasmine...
6097
});
6198

6299
it("uses cache buster", () => {
63-
prepare({ enforceLoading: true });
100+
prepareSingle({ enforceLoading: true });
64101
translate.use("en");
65102
http.expectOne((req) =>
66103
req.url.startsWith("/assets/i18n/en.json?enforceLoading="),
@@ -70,7 +107,29 @@ describe("TranslateHttpLoader (HttpClient)", () => {
70107
});
71108

72109
it("should be able to get translations", () => {
73-
prepare();
110+
prepareSingle();
111+
112+
translate.use("en");
113+
114+
// this will request the translation from the backend because we use a static files loader for TranslateService
115+
translate.get("TEST").subscribe((res: Translation) => {
116+
expect(res as string).toEqual("This is a test");
117+
});
118+
119+
// mock response after the xhr request, otherwise it will be undefined
120+
http.expectOne("/assets/i18n/en.json").flush({
121+
TEST: "This is a test",
122+
TEST2: "This is another test",
123+
});
124+
125+
// this will request the translation from downloaded translations without making a request to the backend
126+
translate.get("TEST2").subscribe((res: Translation) => {
127+
expect(res as string).toEqual("This is another test");
128+
});
129+
});
130+
131+
it("should be able to get translations from multipleHttpLoader", () => {
132+
prepareMulti();
74133

75134
translate.use("en");
76135

@@ -92,7 +151,18 @@ describe("TranslateHttpLoader (HttpClient)", () => {
92151
});
93152

94153
it("should trigger MarkerInterceptor when loading translations", () => {
95-
prepare();
154+
prepareSingle();
155+
156+
translate.use("en").subscribe();
157+
158+
const req = http.expectOne("/assets/i18n/en.json");
159+
req.flush({ HELLO: "Hello" });
160+
161+
expect(req.request.headers.get("X-Test-Header")).toBe("marker");
162+
});
163+
164+
it("should trigger MarkerInterceptor when loading translations with multipleHttpLoader", () => {
165+
prepareMulti();
96166

97167
translate.use("en").subscribe();
98168

@@ -103,7 +173,28 @@ describe("TranslateHttpLoader (HttpClient)", () => {
103173
});
104174

105175
it("should be able to reload a lang", () => {
106-
prepare();
176+
prepareSingle();
177+
178+
translate.use("en");
179+
180+
// this will request the translation from the backend because we use a static files loader for TranslateService
181+
translate.get("TEST").subscribe((res: Translation) => {
182+
expect(res as string).toEqual("This is a test");
183+
184+
// reset the lang as if it was never initiated
185+
translate.reloadLang("en").subscribe(() => {
186+
expect(translate.instant("TEST") as string).toEqual("This is a test 2");
187+
});
188+
189+
http.expectOne("/assets/i18n/en.json").flush({ TEST: "This is a test 2" });
190+
});
191+
192+
// mock response after the xhr request, otherwise it will be undefined
193+
http.expectOne("/assets/i18n/en.json").flush({ TEST: "This is a test" });
194+
});
195+
196+
it("should be able to reload a lang with multipleHttpLoader", () => {
197+
prepareMulti();
107198

108199
translate.use("en");
109200

@@ -124,7 +215,37 @@ describe("TranslateHttpLoader (HttpClient)", () => {
124215
});
125216

126217
it("should be able to reset a lang", (done: DoneFn) => {
127-
prepare();
218+
prepareSingle();
219+
220+
translate.use("en");
221+
spyOn(http, "expectOne").and.callThrough();
222+
223+
// this will request the translation from the backend because we use a static files loader for TranslateService
224+
translate.get("TEST").subscribe((res: Translation) => {
225+
expect(res as string).toEqual("This is a test");
226+
expect(http.expectOne).toHaveBeenCalledTimes(1);
227+
228+
// reset the lang as if it was never initiated
229+
translate.resetLang("en");
230+
231+
expect(translate.instant("TEST") as string).toEqual("TEST");
232+
233+
// use set timeout because no request is really made and we need to trigger zone to resolve the observable
234+
setTimeout(() => {
235+
translate.get("TEST").subscribe((res2: Translation) => {
236+
expect(res2 as string).toEqual("TEST"); // because the loader is "pristine" as if it was never called
237+
expect(http.expectOne).toHaveBeenCalledTimes(1);
238+
done();
239+
});
240+
}, 10);
241+
});
242+
243+
// mock response after the xhr request, otherwise it will be undefined
244+
http.expectOne("/assets/i18n/en.json").flush({ TEST: "This is a test" });
245+
});
246+
247+
it("should be able to reset a lang from MultiHttpLoader", (done: DoneFn) => {
248+
prepareMulti();
128249

129250
translate.use("en");
130251
spyOn(http, "expectOne").and.callThrough();
@@ -152,4 +273,74 @@ describe("TranslateHttpLoader (HttpClient)", () => {
152273
// mock response after the xhr request, otherwise it will be undefined
153274
http.expectOne("/assets/i18n/en.json").flush({ TEST: "This is a test" });
154275
});
276+
277+
it("should merge translations from multiple resources", () => {
278+
prepareMulti({
279+
resources: ["/assets/i18n/", { prefix: "/custom/", suffix: ".lang.json" }],
280+
});
281+
translate.use("en").subscribe();
282+
http.expectOne("/assets/i18n/en.json").flush({ TEST: "A", ONLY1: "X" });
283+
http.expectOne("/custom/en.lang.json").flush({ TEST: "B", ONLY2: "Y" });
284+
// TEST should be overwritten by the second resource
285+
expect(translate.instant("TEST")).toBe("B");
286+
expect(translate.instant("ONLY1")).toBe("X");
287+
expect(translate.instant("ONLY2")).toBe("Y");
288+
});
289+
290+
it("should handle error in one resource and still merge others", () => {
291+
prepareMulti({
292+
resources: ["/assets/i18n/", { prefix: "/custom/", suffix: ".lang.json" }],
293+
});
294+
translate.use("en").subscribe();
295+
http.expectOne("/assets/i18n/en.json").flush({ TEST: "A" });
296+
http.expectOne("/custom/en.lang.json").flush(null, {
297+
status: 500,
298+
statusText: "Server Error",
299+
});
300+
expect(translate.instant("TEST")).toBe("A");
301+
});
302+
303+
it("should log error if showLog is true", () => {
304+
prepareMulti({
305+
resources: [
306+
"/assets/i18n/",
307+
{ prefix: "/custom/", suffix: ".lang.json", showLog: true },
308+
],
309+
showLog: true,
310+
});
311+
spyOn(console, "error");
312+
translate.use("en").subscribe();
313+
http.expectOne("/assets/i18n/en.json").flush({ TEST: "A" });
314+
http.expectOne("/custom/en.lang.json").flush(null, {
315+
status: 500,
316+
statusText: "Server Error",
317+
});
318+
expect(console.error).toHaveBeenCalled();
319+
});
320+
321+
it("should fallback to empty object if all resources fail", () => {
322+
prepareMulti({
323+
resources: ["/assets/i18n/", { prefix: "/custom/", suffix: ".lang.json" }],
324+
});
325+
translate.use("en").subscribe();
326+
http.expectOne("/assets/i18n/en.json").flush(null, {
327+
status: 500,
328+
statusText: "Server Error",
329+
});
330+
http.expectOne("/custom/en.lang.json").flush(null, {
331+
status: 500,
332+
statusText: "Server Error",
333+
});
334+
expect(translate.instant("ANY")).toBe("ANY");
335+
});
336+
337+
it("should support resource as string and object with suffix", () => {
338+
prepareMulti({
339+
resources: ["/assets/i18n/", { prefix: "/custom/", suffix: ".lang.json" }],
340+
});
341+
translate.use("en").subscribe();
342+
http.expectOne("/assets/i18n/en.json").flush({ TEST: "A" });
343+
http.expectOne("/custom/en.lang.json").flush({ TEST: "B" });
344+
expect(translate.instant("TEST")).toBe("B");
345+
});
155346
});

0 commit comments

Comments
 (0)