import {fakeAsync, TestBed, tick} from "@angular/core/testing"; import {Observable, of, timer, zip, defer} from "rxjs"; import {mapTo, take, toArray, first} from 'rxjs/operators'; import {LangChangeEvent, TranslateLoader, TranslateModule, TranslateService, TranslationChangeEvent} from '../public-api'; let translations: any = {"TEST": "This is a test"}; class FakeLoader implements TranslateLoader { getTranslation(lang: string): Observable { return of(translations); } } describe('TranslateService', () => { let translate: TranslateService; beforeEach(() => { TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot({ loader: {provide: TranslateLoader, useClass: FakeLoader} }) ] }); translate = TestBed.inject(TranslateService); }); afterEach(() => { translations = {"TEST": "This is a test"}; }); it('is defined', () => { expect(TranslateService).toBeDefined(); expect(translate).toBeDefined(); expect(translate instanceof TranslateService).toBeTruthy(); }); it('should be able to get translations', () => { translations = {"TEST": "This is a test", "TEST2": "This is another test"}; translate.use('en'); // this will request the translation from the backend because we use a static files loader for TranslateService translate.get('TEST').subscribe((res: string) => { expect(res).toEqual('This is a test'); }); // this will request the translation from downloaded translations without making a request to the backend translate.get('TEST2').subscribe((res: string) => { expect(res).toEqual('This is another test'); }); }); it('should be able to get an array translations', () => { translations = {"TEST": "This is a test", "TEST2": "This is another test2"}; translate.use('en'); // this will request the translation from the backend because we use a static files loader for TranslateService translate.get(['TEST', 'TEST2']).subscribe((res: string) => { expect(res).toEqual(translations); }); }); it("should fallback to the default language", () => { translations = {}; translate.use('fr'); translate.get('TEST').subscribe((res: string) => { expect(res).toEqual('TEST'); translate.setDefaultLang('nl'); translate.setTranslation('nl', {"TEST": "Dit is een test"}); translate.get('TEST').subscribe((res2: string) => { expect(res2).toEqual('Dit is een test'); expect(translate.getDefaultLang()).toEqual('nl'); }); }); }); it("should use the default language by default", () => { translate.setDefaultLang('nl'); translate.setTranslation('nl', {"TEST": "Dit is een test"}); translate.get('TEST').subscribe((res: string) => { expect(res).toEqual('Dit is een test'); }); }); it("should return the key when it doesn't find a translation", () => { translate.use('en'); translate.get('TEST2').subscribe((res: string) => { expect(res).toEqual('TEST2'); }); }); it("should return the key when you haven't defined any translation", () => { translate.get('TEST').subscribe((res: string) => { expect(res).toEqual('TEST'); }); }); it('should return an empty value', () => { translate.setDefaultLang('en'); translate.setTranslation('en', {"TEST": ""}); translate.get('TEST').subscribe((res: string) => { expect(res).toEqual(''); }); }); it('should be able to get translations with params', () => { translations = {"TEST": "This is a test {{param}}"}; translate.use('en'); translate.get('TEST', {param: 'with param'}).subscribe((res: string) => { expect(res).toEqual('This is a test with param'); }); }); it('should be able to get translations with nested params', () => { translations = {"TEST": "This is a test {{param.value}}"}; translate.use('en'); translate.get('TEST', {param: {value: 'with param'}}).subscribe((res: string) => { expect(res).toEqual('This is a test with param'); }); }); it('should throw if you forget the key', () => { translate.use('en'); expect(() => { translate.get(undefined as any); }).toThrowError('Parameter "key" required'); expect(() => { translate.get(''); }).toThrowError('Parameter "key" required'); expect(() => { translate.get(null as any); }).toThrowError('Parameter "key" required'); expect(() => { translate.instant(undefined as any); }).toThrowError('Parameter "key" required'); }); it('should be able to get translations with nested keys', () => { translations = {"TEST": {"TEST": "This is a test"}, "TEST2": {"TEST2": {"TEST2": "This is another test"}}}; translate.use('en'); translate.get('TEST.TEST').subscribe((res: string) => { expect(res).toEqual('This is a test'); }); translate.get('TEST2.TEST2.TEST2').subscribe((res: string) => { expect(res).toEqual('This is another test'); }); }); it("should merge translations if option shouldMerge is true", (done: Function) => { translations = {}; translate.setTranslation('en', {"TEST": {"sub1": "value1"}}, true); translate.setTranslation('en', {"TEST": {"sub2": "value2"}}, true); translate.use('en'); translate.get('TEST').subscribe((res: any) => { expect(res).toEqual({"sub1": "value1", "sub2": "value2"}); expect(translations).toEqual({}); done(); }); }); it("should merge non-valid JSON translations if option shouldMerge is true", () => { translations = {}; translate.setTranslation('en', {"TEST": {"sub1": () => "value1"}}, true); translate.setTranslation('en', {"TEST": {"sub2": () => "value2"}}, true); translate.use('en'); translate.get('TEST.sub1').subscribe((res: string) => { expect(res).toEqual('value1'); }); translate.get('TEST.sub2').subscribe((res: string) => { expect(res).toEqual('value2'); }); }); it("shouldn't call the current loader if you set the translation yourself", (done: Function) => { translations = {}; translate.setTranslation('en', {"TEST": "This is a test"}); translate.use('en'); translate.get('TEST').subscribe((res: string) => { expect(res).toEqual('This is a test'); expect(translations).toEqual({}); done(); }); }); it('should be able to get a stream array', (done: Function) => { let tr = {"TEST": "This is a test", "TEST2": "This is a test2"}; translate.setTranslation('en', tr); translate.use('en'); translate.getStreamOnTranslationChange(['TEST', 'TEST2']).subscribe((res: any) => { expect(res).toEqual(tr); done(); }); }); it('should initially return the same value for getStreamOnTranslationChange and non-streaming get', (done: Function) => { translations = {"TEST": "This is a test"}; translate.use('en'); zip(translate.getStreamOnTranslationChange('TEST'), translate.get('TEST')).subscribe((value: [string, string]) => { const [streamed, nonStreamed] = value; expect(streamed).toEqual('This is a test'); expect(streamed).toEqual(nonStreamed); done(); }); }); it('should be able to stream a translation on translation change', (done: Function) => { translations = {"TEST": "This is a test"}; translate.use('en'); translate.getStreamOnTranslationChange('TEST').pipe(take(3), toArray()).subscribe((res: string[]) => { const expected = ['This is a test', 'I changed the test value!', 'I changed it again!']; expect(res).toEqual(expected); done(); }); translate.setTranslation('en', {"TEST": "I changed the test value!"}); translate.setTranslation('en', {"TEST": "I changed it again!"}); }); it('should interpolate the same param into each streamed value when get strean on translation change', (done: Function) => { translations = {"TEST": "This is a test {{param}}"}; translate.use('en'); translate.getStreamOnTranslationChange('TEST', {param: 'with param'}).subscribe((res: string[]) => { const expected = 'This is a test with param'; expect(res).toEqual(expected); done(); }); }); it('should be able to stream a translation for the current language', (done: Function) => { translations = {"TEST": "This is a test"}; translate.use('en'); translate.stream('TEST').subscribe((res: string) => { expect(res).toEqual('This is a test'); done(); }); }); it('should be able to stream a translation of an array for the current language', (done: Function) => { let tr = {"TEST": "This is a test", "TEST2": "This is a test2"}; translate.setTranslation('en', tr); translate.use('en'); translate.stream(['TEST', 'TEST2']).subscribe((res: any) => { expect(res).toEqual(tr); done(); }); }); it('should initially return the same value for streaming and non-streaming get', (done: Function) => { translations = {"TEST": "This is a test"}; translate.use('en'); zip(translate.stream('TEST'), translate.get('TEST')).subscribe((value: [string, string]) => { const [streamed, nonStreamed] = value; expect(streamed).toEqual('This is a test'); expect(streamed).toEqual(nonStreamed); done(); }); }); it('should update streaming translations on language change', (done: Function) => { translations = {"TEST": "This is a test"}; translate.use('en'); translate.stream('TEST').pipe(take(3), toArray()).subscribe((res: string[]) => { const expected = ['This is a test', 'Dit is een test', 'This is a test']; expect(res).toEqual(expected); done(); }); translate.setTranslation('nl', {"TEST": "Dit is een test"}); translate.use('nl'); translate.use('en'); }); it('should update lazy streaming translations on translation change', (done: Function) => { translations = {"TEST": "This is a test"}; translate.use('en'); const translation$ = translate.getStreamOnTranslationChange('TEST'); translate.setTranslation('en', {"TEST": "This is a test2"}); translation$.pipe(first()).subscribe((res: string[]) => { const expected = "This is a test2"; expect(res).toEqual(expected); done(); }); }); it('should update lazy streaming translations on language change', (done: Function) => { translations = {"TEST": "This is a test"}; translate.use('en'); const translation$ = translate.stream('TEST'); translate.setTranslation('nl', {"TEST": "Dit is een test"}); translate.use('nl'); translation$.pipe(first()).subscribe((res: string[]) => { const expected = 'Dit is een test'; expect(res).toEqual(expected); done(); }); }); it('should update streaming translations of an array on language change', (done: Function) => { const en = {"TEST": "This is a test", "TEST2": "This is a test2"}; const nl = {"TEST": "Dit is een test", "TEST2": "Dit is een test2"}; translate.setTranslation('en', en); translate.use('en'); translate.stream(['TEST', 'TEST2']).pipe(take(3), toArray()).subscribe((res: any[]) => { const expected = [en, nl, en]; expect(res).toEqual(expected); done(); }); translate.setTranslation('nl', nl); translate.use('nl'); translate.use('en'); }); it('should interpolate the same param into each streamed value', (done: Function) => { translations = {"TEST": "This is a test {{param}}"}; translate.use('en'); translate.stream('TEST', {param: 'with param'}).pipe(take(3), toArray()).subscribe((res: string[]) => { const expected = [ 'This is a test with param', 'Dit is een test with param', 'This is a test with param' ]; expect(res).toEqual(expected); done(); }); translate.setTranslation('nl', {"TEST": "Dit is een test {{param}}"}); translate.use('nl'); translate.use('en'); }); it('should be able to get instant translations', () => { translate.setTranslation('en', {"TEST": "This is a test"}); translate.use('en'); expect(translate.instant('TEST')).toEqual('This is a test'); }); it('should be able to get instant translations of an array', () => { let tr = {"TEST": "This is a test", "TEST2": "This is a test2"}; translate.setTranslation('en', tr); translate.use('en'); expect(translate.instant(['TEST', 'TEST2'])).toEqual(tr); }); it('should return the key if instant translations are not available', () => { translate.setTranslation('en', {"TEST": "This is a test"}); translate.use('en'); expect(translate.instant('TEST2')).toEqual('TEST2'); }); it('should trigger an event when the translation value changes', () => { translate.setTranslation('en', {}); translate.onTranslationChange.subscribe((event: TranslationChangeEvent) => { expect(event.translations).toBeDefined(); expect(event.translations["TEST"]).toEqual("This is a test"); expect(event.lang).toBe('en'); }); translate.set("TEST", "This is a test", 'en'); }); it('should trigger an event when the lang changes', () => { let tr = {"TEST": "This is a test"}; translate.setTranslation('en', tr); translate.onLangChange.subscribe((event: LangChangeEvent) => { expect(event.lang).toBe('en'); expect(event.translations).toEqual(tr); }); translate.use('en'); }); it('should be able to reset a lang', (done: Function) => { translate.use('en'); // this will request the translation from the backend because we use a static files loader for TranslateService translate.get('TEST').subscribe((res: string) => { expect(res).toEqual(translations['TEST']); // reset the lang as if it was never initiated translate.resetLang('en'); expect(translate.instant('TEST')).toEqual('TEST'); translate.get('TEST').subscribe((res2: string) => { expect(res2).toEqual('TEST'); // because the loader is "pristine" as if it was never called done(); }); }); }); it('should be able to reload a lang', () => { translations = {}; translate.use('en'); // this will request the translation from the loader translate.get('TEST').subscribe((res: string) => { expect(res).toEqual('TEST'); translations = {"TEST": "This is a test 2"}; // reset the lang as if it was never initiated translate.reloadLang('en').subscribe((res2: string) => { expect(translate.instant('TEST')).toEqual(translations['TEST']); }); }); }); it('should be able to add new langs', () => { translate.addLangs(['pl', 'es']); expect(translate.getLangs()).toEqual(['pl', 'es']); translate.addLangs(['fr']); translate.addLangs(['pl', 'fr']); expect(translate.getLangs()).toEqual(['pl', 'es', 'fr']); // this will request the translation from the backend because we use a static files loader for TranslateService translate.use('en').subscribe((res: string) => { expect(translate.getLangs()).toEqual(['pl', 'es', 'fr', 'en']); translate.addLangs(['de']); expect(translate.getLangs()).toEqual(['pl', 'es', 'fr', 'en', 'de']); }); }); it('should be able to get the browserLang', () => { let browserLang = translate.getBrowserLang(); expect(browserLang).toBeDefined(); expect(typeof browserLang === 'string').toBeTruthy(); }); it('should be able to get the browserCultureLang', () => { let browserCultureLand = translate.getBrowserCultureLang(); expect(browserCultureLand).toBeDefined(); expect(typeof browserCultureLand === 'string').toBeTruthy(); }); it('should not make duplicate getTranslation calls', fakeAsync(() => { let getTranslationCalls = 0; jest.spyOn(translate.currentLoader, 'getTranslation').mockImplementation(() => { getTranslationCalls += 1; return timer(1000).pipe(mapTo(of(translations))); }); translate.use('en'); translate.use('en'); tick(1001); expect(getTranslationCalls).toEqual(1); })); it('should subscribe to the loader just once', () => { let subscriptions = 0; jest.spyOn(translate.currentLoader, 'getTranslation').mockImplementation(() => { return defer(() => { subscriptions++; return of(translations); }); }); translate.use('en'); translate.use('en'); translate.use('en').subscribe(); translate.use('en').subscribe(); expect(subscriptions).toEqual(1); }); it('should compile translations only once, even when subscribing to translations while translations are loading', fakeAsync(() => { jest.spyOn(translate.currentLoader, 'getTranslation').mockImplementation(() => { return timer(1000).pipe(mapTo(of(translations))); }); let translateCompilerCallCount = 0; jest.spyOn(translate.compiler, 'compile').mockImplementation((value) => { ++translateCompilerCallCount; return value; }); jest.spyOn(translate.compiler, 'compileTranslations').mockImplementation((value) => { ++translateCompilerCallCount; return value; }); translate.setDefaultLang('en-US'); translate.get('TEST1').subscribe(); translate.get('TEST2').subscribe(); translate.get('TEST3').subscribe(); translate.get('TEST4').subscribe(); tick(1001); expect(translateCompilerCallCount).toBe(1); })); });