Skip to content

Commit 4e384bb

Browse files
committed
test: provideChildTranslateService
1 parent 5df7dda commit 4e384bb

File tree

4 files changed

+255
-12
lines changed

4 files changed

+255
-12
lines changed

projects/ngx-translate/src/lib/translate.providers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface TranslateProviders {
1919
compiler?: Provider;
2020
parser?: Provider;
2121
missingTranslationHandler?: Provider;
22+
translateServiceClass?: Type<TranslateService>;
2223
}
2324

2425
export interface ChildTranslateServiceConfig extends Partial<TranslateProviders> {
@@ -118,7 +119,7 @@ export function defaultProviders(
118119

119120
providers.push({
120121
provide: TranslateService,
121-
useClass: TranslateService,
122+
useClass: config.translateServiceClass ?? TranslateService,
122123
deps: [
123124
TranslateStore,
124125
TranslateLoader,

projects/ngx-translate/src/lib/translate.service.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ export class TranslateService implements ITranslateService, OnDestroy {
225225
}
226226

227227
constructor() {
228+
console.log("Constructor TranslateService");
229+
228230
const config: TranslateServiceConfig = {
229231
extend: false,
230232
fallbackLang: null,
@@ -234,6 +236,8 @@ export class TranslateService implements ITranslateService, OnDestroy {
234236
}),
235237
};
236238

239+
this.store.addLoader(this.currentLoader);
240+
237241
if (config.lang) {
238242
this.use(config.lang);
239243
}
@@ -243,10 +247,9 @@ export class TranslateService implements ITranslateService, OnDestroy {
243247
}
244248

245249
if (config.extend) {
250+
// we are a child service
246251
this.extend = true;
247252
}
248-
249-
this.store.addLoader(this.currentLoader);
250253
}
251254

252255
ngOnDestroy(): void {

projects/ngx-translate/src/tests/test-helpers.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { ClassProvider, Injectable, Provider } from "@angular/core";
1+
import { Injectable, Provider } from "@angular/core";
22
import { Observable, of, timer } from "rxjs";
33
import { map } from "rxjs/operators";
44
import {
55
provideTranslateService,
6+
provideChildTranslateService,
67
RootTranslateServiceConfig,
78
TranslateCompiler,
89
TranslateLoader,
@@ -47,12 +48,17 @@ export class TestableTranslateService extends TranslateService {
4748
export function provideTestableTranslateService(
4849
config: RootTranslateServiceConfig = {},
4950
): Provider[] {
50-
const providers: Provider[] = provideTranslateService(config);
51-
const translateServicedProvider: ClassProvider | undefined = providers.find(
52-
(provider) => (provider as ClassProvider).provide === TranslateService,
53-
) as ClassProvider | undefined;
54-
if (translateServicedProvider === undefined)
55-
throw new Error("Could not find TranslateService provider in provided providers");
56-
translateServicedProvider.useClass = TestableTranslateService;
57-
return providers;
51+
return provideTranslateService({
52+
translateServiceClass: TestableTranslateService,
53+
...config,
54+
});
55+
}
56+
57+
export function provideTestableChildTranslateService(
58+
config: RootTranslateServiceConfig = {},
59+
): Provider[] {
60+
return provideChildTranslateService({
61+
translateServiceClass: TestableTranslateService,
62+
...config,
63+
});
5864
}
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import { Component, inject } from "@angular/core";
2+
import { fakeAsync, TestBed } from "@angular/core/testing";
3+
import { Observable, of } from "rxjs";
4+
import {
5+
provideTranslateLoader,
6+
TranslateLoader,
7+
TranslatePipe,
8+
TranslateService,
9+
TranslationObject,
10+
} from "../public-api";
11+
import {
12+
provideTestableChildTranslateService,
13+
provideTestableTranslateService,
14+
TestableTranslateService,
15+
} from "./test-helpers";
16+
17+
export interface User {
18+
firstName: string;
19+
lastName?: string;
20+
}
21+
22+
export class FakeTranslateLoaderWithCounter implements TranslateLoader {
23+
public callCount = 0;
24+
25+
constructor(private translations: TranslationObject) {}
26+
27+
getTranslation(): Observable<TranslationObject> {
28+
this.callCount++;
29+
return of(this.translations);
30+
}
31+
}
32+
33+
export class FakeChildLoader extends FakeTranslateLoaderWithCounter {
34+
constructor() {
35+
super({ "value-from-child": "i'm from child" });
36+
}
37+
}
38+
39+
export class FakeRootLoader extends FakeTranslateLoaderWithCounter {
40+
constructor() {
41+
super({ "value-from-root": "i'm from root" });
42+
}
43+
}
44+
45+
describe("TranslateService (child, separate loaders)", () => {
46+
let rootTranslateService: TestableTranslateService;
47+
let childTranslateService: TestableTranslateService;
48+
49+
@Component({
50+
selector: "app-child-component",
51+
template: ` <div class="isolated-child">{{ "test" | translate }}</div> `,
52+
imports: [TranslatePipe],
53+
providers: [
54+
provideTestableChildTranslateService({
55+
loader: provideTranslateLoader(FakeChildLoader),
56+
}),
57+
],
58+
})
59+
class ChildComponent {
60+
constructor() {
61+
childTranslateService = inject(TranslateService) as TestableTranslateService;
62+
}
63+
}
64+
65+
@Component({
66+
imports: [ChildComponent],
67+
selector: "app-root-component",
68+
template: ` <app-child-component /> `,
69+
providers: [
70+
provideTestableTranslateService({ loader: provideTranslateLoader(FakeRootLoader) }),
71+
],
72+
})
73+
class RootComponent {
74+
constructor() {
75+
rootTranslateService = inject(TranslateService) as TestableTranslateService;
76+
}
77+
}
78+
79+
beforeEach(() => {
80+
TestBed.configureTestingModule({});
81+
const fixture = TestBed.createComponent(RootComponent);
82+
});
83+
84+
it("testSetup is ready", () => {
85+
expect(rootTranslateService).toBeDefined();
86+
expect(childTranslateService).toBeDefined();
87+
});
88+
89+
it("should trigger loading on language switch from child and parent", fakeAsync(() => {
90+
const childLoader: FakeChildLoader =
91+
childTranslateService.getCurrentLoader() as FakeChildLoader;
92+
const rootLoader: FakeChildLoader =
93+
rootTranslateService.getCurrentLoader() as FakeChildLoader;
94+
95+
childTranslateService.use("en");
96+
97+
expect(rootLoader.callCount).toEqual(1);
98+
expect(childLoader.callCount).toEqual(1);
99+
100+
expect(childTranslateService.instant("value-from-child")).toEqual("i'm from child");
101+
expect(childTranslateService.instant("value-from-root")).toEqual("i'm from root");
102+
expect(rootTranslateService.instant("value-from-child")).toEqual("i'm from child");
103+
expect(rootTranslateService.instant("value-from-root")).toEqual("i'm from root");
104+
105+
rootTranslateService.use("de");
106+
107+
expect(rootLoader.callCount).toEqual(2);
108+
expect(childLoader.callCount).toEqual(2);
109+
}));
110+
});
111+
112+
describe("TranslateService (child, separate loaders, preload with lang=)", () => {
113+
let rootTranslateService: TestableTranslateService;
114+
let childTranslateService: TestableTranslateService;
115+
116+
@Component({
117+
selector: "app-child-component",
118+
template: ` <div class="isolated-child">{{ "test" | translate }}</div> `,
119+
imports: [TranslatePipe],
120+
providers: [
121+
provideTestableChildTranslateService({
122+
loader: provideTranslateLoader(FakeChildLoader),
123+
}),
124+
],
125+
})
126+
class ChildComponent {
127+
constructor() {
128+
childTranslateService = inject(TranslateService) as TestableTranslateService;
129+
}
130+
}
131+
132+
@Component({
133+
imports: [ChildComponent],
134+
selector: "app-root-component",
135+
template: ` <app-child-component /> `,
136+
providers: [
137+
provideTestableTranslateService({
138+
loader: provideTranslateLoader(FakeRootLoader),
139+
lang: "en",
140+
}),
141+
],
142+
})
143+
class RootComponent {
144+
constructor() {
145+
rootTranslateService = inject(TranslateService) as TestableTranslateService;
146+
}
147+
}
148+
149+
beforeEach(() => {
150+
TestBed.configureTestingModule({});
151+
const fixture = TestBed.createComponent(RootComponent);
152+
});
153+
154+
it("testSetup is ready", () => {
155+
expect(rootTranslateService).toBeDefined();
156+
expect(childTranslateService).toBeDefined();
157+
});
158+
159+
xit("should load on start from child and parent", fakeAsync(() => {
160+
const childLoader: FakeChildLoader =
161+
childTranslateService.getCurrentLoader() as FakeChildLoader;
162+
const rootLoader: FakeChildLoader =
163+
rootTranslateService.getCurrentLoader() as FakeChildLoader;
164+
165+
expect(childLoader.callCount).toEqual(1);
166+
expect(rootLoader.callCount).toEqual(1);
167+
168+
expect(childTranslateService.instant("value-from-child")).toEqual("i'm from child");
169+
expect(childTranslateService.instant("value-from-root")).toEqual("i'm from root");
170+
expect(rootTranslateService.instant("value-from-child")).toEqual("i'm from child");
171+
expect(rootTranslateService.instant("value-from-root")).toEqual("i'm from root");
172+
}));
173+
});
174+
175+
describe("TranslateService (child, shared loader)", () => {
176+
let rootTranslateService: TestableTranslateService;
177+
let childTranslateService: TestableTranslateService;
178+
179+
@Component({
180+
selector: "app-child-component",
181+
template: ` <div class="isolated-child">{{ "test" | translate }}</div> `,
182+
imports: [TranslatePipe],
183+
providers: [provideTestableChildTranslateService()],
184+
})
185+
class ChildComponent {
186+
constructor() {
187+
childTranslateService = inject(TranslateService) as TestableTranslateService;
188+
}
189+
}
190+
191+
@Component({
192+
imports: [ChildComponent],
193+
selector: "app-root-component",
194+
template: ` <app-child-component /> `,
195+
providers: [
196+
provideTestableTranslateService({ loader: provideTranslateLoader(FakeRootLoader) }),
197+
],
198+
})
199+
class RootComponent {
200+
constructor() {
201+
rootTranslateService = inject(TranslateService) as TestableTranslateService;
202+
}
203+
}
204+
205+
beforeEach(() => {
206+
TestBed.configureTestingModule({});
207+
const fixture = TestBed.createComponent(RootComponent);
208+
});
209+
210+
it("testSetup is ready", () => {
211+
expect(rootTranslateService).toBeDefined();
212+
expect(childTranslateService).toBeDefined();
213+
expect(
214+
rootTranslateService.getCurrentLoader() === childTranslateService.getCurrentLoader(),
215+
).toBeTruthy();
216+
});
217+
218+
it("should trigger loading on language switch from child and parent", fakeAsync(() => {
219+
const rootLoader: FakeRootLoader =
220+
rootTranslateService.getCurrentLoader() as FakeRootLoader;
221+
222+
childTranslateService.use("en");
223+
224+
expect(rootLoader.callCount).toEqual(1);
225+
226+
expect(childTranslateService.instant("value-from-root")).toEqual("i'm from root");
227+
expect(rootTranslateService.instant("value-from-root")).toEqual("i'm from root");
228+
229+
rootTranslateService.use("de");
230+
231+
expect(rootLoader.callCount).toEqual(2);
232+
}));
233+
});

0 commit comments

Comments
 (0)