diff --git a/projects/http-loader/src/lib/http-loader.spec.ts b/projects/http-loader/src/lib/http-loader.spec.ts index 467307e..bdd38b6 100644 --- a/projects/http-loader/src/lib/http-loader.spec.ts +++ b/projects/http-loader/src/lib/http-loader.spec.ts @@ -1,7 +1,7 @@ import {HttpClient, provideHttpClient} from "@angular/common/http"; import {HttpTestingController, provideHttpClientTesting} from "@angular/common/http/testing"; import {TestBed} from "@angular/core/testing"; -import {TranslateLoader, TranslateModule, TranslateService, Translation} from "@codeandweb/ngx-translate"; +import {TranslateLoader, provideTranslateService, TranslateService, Translation} from "@codeandweb/ngx-translate"; import {TranslateHttpLoader} from "../public-api"; describe('TranslateLoader', () => { @@ -10,19 +10,18 @@ describe('TranslateLoader', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useFactory: (httpClient: HttpClient) => new TranslateHttpLoader(httpClient), - deps: [HttpClient] - } - }) - ], providers: [ TranslateService, provideHttpClient(), provideHttpClientTesting(), + provideTranslateService({ + loader: { + provide: TranslateLoader, + useFactory: (httpClient: HttpClient) => new TranslateHttpLoader(httpClient), + deps: [HttpClient] + } + } + ) ] }); translate = TestBed.inject(TranslateService); diff --git a/projects/ngx-translate/src/lib/missing-translation-handler.spec.ts b/projects/ngx-translate/src/lib/missing-translation-handler.spec.ts index 956a3a5..6e64bf8 100644 --- a/projects/ngx-translate/src/lib/missing-translation-handler.spec.ts +++ b/projects/ngx-translate/src/lib/missing-translation-handler.spec.ts @@ -2,12 +2,12 @@ import {TestBed} from "@angular/core/testing"; import {Observable, of} from "rxjs"; import { MissingTranslationHandler, - MissingTranslationHandlerParams, + MissingTranslationHandlerParams, provideTranslateService, TranslateLoader, - TranslateModule, TranslateService, Translation, TranslationObject } from "../public-api"; +import {Type} from "@angular/core"; let translations: TranslationObject = {"TEST": "This is a test"}; const fakeTranslation: TranslationObject = {"NOT_USED": "not used"}; @@ -39,16 +39,14 @@ describe('MissingTranslationHandler', () => { } } - const prepare = ((handlerClass: typeof MissingTranslationHandler, defaultLang = true) => { + const prepare = ((handlerClass: Type, defaultLang = true) => { TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot({ + providers: [ + provideTranslateService({ loader: {provide: TranslateLoader, useClass: FakeLoader}, + missingTranslationHandler: {provide: MissingTranslationHandler, useClass: handlerClass}, useDefaultLang: defaultLang }) - ], - providers: [ - {provide: MissingTranslationHandler, useClass: handlerClass} ] }); translate = TestBed.inject(TranslateService); diff --git a/projects/ngx-translate/src/lib/translate.compiler.spec.ts b/projects/ngx-translate/src/lib/translate.compiler.spec.ts index f01df5e..91bbb19 100644 --- a/projects/ngx-translate/src/lib/translate.compiler.spec.ts +++ b/projects/ngx-translate/src/lib/translate.compiler.spec.ts @@ -5,9 +5,10 @@ import { TranslateCompiler, TranslateFakeCompiler, TranslateLoader, - TranslateModule, TranslateService, - TranslationObject, Translation + TranslationObject, + Translation, + provideTranslateService } from "../public-api"; const translations: TranslationObject = {LOAD: 'This is a test'}; @@ -25,12 +26,12 @@ describe('TranslateCompiler', () => { describe('with default TranslateFakeCompiler', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot({ + providers: [ + provideTranslateService({ loader: {provide: TranslateLoader, useClass: FakeLoader}, compiler: {provide: TranslateCompiler, useClass: TranslateFakeCompiler} }) - ], + ] }); translate = TestBed.inject(TranslateService); @@ -78,8 +79,8 @@ describe('TranslateCompiler', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot({ + providers: [ + provideTranslateService({ loader: {provide: TranslateLoader, useClass: FakeLoader}, compiler: {provide: TranslateCompiler, useClass: CustomCompiler} }) diff --git a/projects/ngx-translate/src/lib/translate.directive.spec.ts b/projects/ngx-translate/src/lib/translate.directive-module.spec.ts similarity index 99% rename from projects/ngx-translate/src/lib/translate.directive.spec.ts rename to projects/ngx-translate/src/lib/translate.directive-module.spec.ts index af4305e..a97c832 100644 --- a/projects/ngx-translate/src/lib/translate.directive.spec.ts +++ b/projects/ngx-translate/src/lib/translate.directive-module.spec.ts @@ -40,7 +40,7 @@ class AppComponent { } } -describe('TranslateDirective', () => { +describe('TranslateDirective (modules)', () => { let translate: TranslateService; let fixture: ComponentFixture; diff --git a/projects/ngx-translate/src/lib/translate.directive-standalone.spec.ts b/projects/ngx-translate/src/lib/translate.directive-standalone.spec.ts new file mode 100644 index 0000000..c74c862 --- /dev/null +++ b/projects/ngx-translate/src/lib/translate.directive-standalone.spec.ts @@ -0,0 +1,268 @@ +import {ChangeDetectionStrategy, Component, ElementRef, Injectable, ViewChild, ViewContainerRef} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import { + provideTranslateService, TranslateDirective, TranslatePipe, + TranslateService +} from "../public-api"; + +@Injectable() +@Component({ + selector: 'lib-hmx-app', + standalone: true, + imports: [TranslateDirective, TranslatePipe], + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` +
TEST
+
TEST.VALUE
+
Some init content
+
+
TEST1 Hey TEST2
+
Some init content
+
TEST
+
TEST
+
TEST
+
+ TEST +
+ ` +}) +class AppComponent { + viewContainerRef: ViewContainerRef; + @ViewChild('noKey', {static: true}) noKey!: ElementRef; + @ViewChild('contentAsKey', {static: true}) contentAsKey!: ElementRef; + @ViewChild('withKey', {static: true}) withKey!: ElementRef; + @ViewChild('withOtherElements', {static: true}) withOtherElements!: ElementRef; + @ViewChild('withParams', {static: true}) withParams!: ElementRef; + @ViewChild('withParamsNoKey', {static: true}) withParamsNoKey!: ElementRef; + @ViewChild('noContent', {static: true}) noContent!: ElementRef; + @ViewChild('leadingSpaceNoKeyNoParams') leadingSpaceNoKeyNoParams!: ElementRef; + @ViewChild('trailingSpaceNoKeyNoParams') trailingSpaceNoKeyNoParams!: ElementRef; + @ViewChild('withSpaceAndLineBreakNoKeyNoParams') withSpaceAndLineBreakNoKeyNoParams!: ElementRef; + value = {value: 'ok'}; + + constructor(viewContainerRef: ViewContainerRef) { + this.viewContainerRef = viewContainerRef; + } +} + +describe('TranslateDirective (standalone)', () => { + let translate: TranslateService; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + provideTranslateService({}) + ] + }); + translate = TestBed.inject(TranslateService); + + fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + }); + + it('should translate a string using the container value', () => { + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('TEST'); + + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.use('en'); + + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('This is a test'); + }); + + it('should translate a string using the container value as a key', () => { + expect(fixture.componentInstance.contentAsKey.nativeElement.innerHTML).toEqual('TEST.VALUE'); + + translate.setTranslation('en', {"TEST": {"VALUE": "This is a test"}}); + translate.use('en'); + + expect(fixture.componentInstance.contentAsKey.nativeElement.innerHTML).toEqual('This is a test'); + }); + + it('should translate a string using the key value', () => { + // replace the content with the key + expect(fixture.componentInstance.withKey.nativeElement.innerHTML).toEqual('TEST'); + + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.use('en'); + + expect(fixture.componentInstance.withKey.nativeElement.innerHTML).toEqual('This is a test'); + }); + + it('should translate first child strings with elements in the middle', () => { + // replace the content with the key + expect(fixture.componentInstance.withOtherElements.nativeElement.innerHTML).toEqual('TEST1 Hey TEST2'); + + translate.setTranslation('en', {"TEST1": "Awesome", "TEST2": "it works"}); + translate.use('en'); + + expect(fixture.componentInstance.withOtherElements.nativeElement.innerHTML).toEqual('Awesome Hey it works'); + }); + + it('should translate first child strings without recursion', () => { + // replace the content with the key + expect(fixture.componentInstance.withOtherElements.nativeElement.innerHTML).toEqual('TEST1 Hey TEST2'); + + translate.setTranslation('en', {"TEST1": "TEST2", "TEST2": "it works"}); + translate.use('en'); + + expect(fixture.componentInstance.withOtherElements.nativeElement.innerHTML).toEqual('TEST2 Hey it works'); + }); + + it('should translate a string with params and a key', () => { + // replace the content with the key + expect(fixture.componentInstance.withParams.nativeElement.innerHTML).toEqual('TEST'); + + translate.setTranslation('en', {"TEST": "It is {{value}}"}); + translate.use('en'); + + expect(fixture.componentInstance.withParams.nativeElement.innerHTML).toEqual('It is ok'); + }); + + it('should translate a string with params and no key', () => { + // replace the content with the key + expect(fixture.componentInstance.withParamsNoKey.nativeElement.innerHTML).toEqual('TEST'); + + translate.setTranslation('en', {"TEST": "It is {{value}}"}); + translate.use('en'); + + expect(fixture.componentInstance.withParamsNoKey.nativeElement.innerHTML).toEqual('It is ok'); + }); + + it('should update the translation when params change', () => { + // replace the content with the key + expect(fixture.componentInstance.withParams.nativeElement.innerHTML).toEqual('TEST'); + expect(fixture.componentInstance.withParamsNoKey.nativeElement.innerHTML).toEqual('TEST'); + + translate.setTranslation('en', {"TEST": "It is {{value}}"}); + translate.use('en'); + + expect(fixture.componentInstance.withParams.nativeElement.innerHTML).toEqual('It is ok'); + expect(fixture.componentInstance.withParamsNoKey.nativeElement.innerHTML).toEqual('It is ok'); + fixture.componentInstance.value = {value: 'changed'}; + fixture.detectChanges(); + + expect(fixture.componentInstance.withParams.nativeElement.innerHTML).toEqual('It is changed'); + expect(fixture.componentInstance.withParamsNoKey.nativeElement.innerHTML).toEqual('It is changed'); + }); + + it('should update the DOM when the lang changes and the translation key starts with space', () => { + expect(fixture.componentInstance.leadingSpaceNoKeyNoParams.nativeElement.innerHTML).toEqual(' TEST'); + + const en = "This is a test - with leading spaces in translation key"; + const fr = "C'est un test - avec un espace de tête dans la clé de traduction"; + const leadingSpaceFromKey = ' '; + translate.setTranslation('en', {"TEST": en}); + translate.setTranslation('fr', {"TEST": fr}); + + translate.use('en'); + expect(fixture.componentInstance.leadingSpaceNoKeyNoParams.nativeElement.innerHTML).toEqual(leadingSpaceFromKey + en); + + translate.use('fr'); + expect(fixture.componentInstance.leadingSpaceNoKeyNoParams.nativeElement.innerHTML).toEqual(leadingSpaceFromKey + fr); + }); + + it('should update the DOM when the lang changes and the translation key has line breaks and spaces', () => { + expect(fixture.componentInstance.withSpaceAndLineBreakNoKeyNoParams.nativeElement.innerHTML).toEqual(' TEST '); + + const en = "This is a test - with trailing spaces in translation key"; + const fr = "C'est un test - avec un espace de fuite dans la clé de traduction"; + const whiteSpaceFromKey = ' '; + translate.setTranslation('en', {"TEST": en}); + translate.setTranslation('fr', {"TEST": fr}); + + translate.use('en'); + expect(fixture.componentInstance.withSpaceAndLineBreakNoKeyNoParams.nativeElement.innerHTML).toEqual(whiteSpaceFromKey + en + whiteSpaceFromKey); + + translate.use('fr'); + expect(fixture.componentInstance.withSpaceAndLineBreakNoKeyNoParams.nativeElement.innerHTML).toEqual(whiteSpaceFromKey + fr + whiteSpaceFromKey); + }); + + it('should update the DOM when the lang changes and the translation key ends with space', () => { + expect(fixture.componentInstance.trailingSpaceNoKeyNoParams.nativeElement.innerHTML).toEqual('TEST '); + + const en = "This is a test - with spaces and line breaks in translation key"; + const fr = "C'est un test - avec des espaces et sauts de lignes dans la clé de traduction"; + const trailingSpaceFromKey = ' '; + translate.setTranslation('en', {"TEST": en}); + translate.setTranslation('fr', {"TEST": fr}); + + translate.use('en'); + expect(fixture.componentInstance.trailingSpaceNoKeyNoParams.nativeElement.innerHTML).toEqual(en + trailingSpaceFromKey); + + translate.use('fr'); + expect(fixture.componentInstance.trailingSpaceNoKeyNoParams.nativeElement.innerHTML).toEqual(fr + trailingSpaceFromKey); + }); + + + it('should update the DOM when the lang changes and the translation ends with space', () => { + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('TEST'); + expect(fixture.componentInstance.withParams.nativeElement.innerHTML).toEqual('TEST'); + expect(fixture.componentInstance.noContent.nativeElement.innerHTML).toEqual('TEST'); + + const en = " This is a test - with spaces "; + const fr = " C'est un test - avec espaces "; + + translate.setTranslation('en', {"TEST": en}); + translate.setTranslation('fr', {"TEST": fr}); + + translate.use('en'); + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual(`${en}`); + expect(fixture.componentInstance.withParams.nativeElement.innerHTML).toEqual(en); + expect(fixture.componentInstance.noContent.nativeElement.innerHTML).toEqual(en); + + translate.use('fr'); + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual(`${fr}`); + expect(fixture.componentInstance.withParams.nativeElement.innerHTML).toEqual(fr); + expect(fixture.componentInstance.noContent.nativeElement.innerHTML).toEqual(fr); + }); + + it('should update the DOM when the default lang changes', () => { + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('TEST'); + + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.setTranslation('fr', {"TEST": "C'est un test"}); + translate.setDefaultLang('en'); + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('This is a test'); + + translate.setDefaultLang('fr'); + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual("C'est un test"); + }); + + it('should unsubscribe from lang change subscription on destroy', () => { + expect(fixture.componentInstance.withParamsNoKey.nativeElement.innerHTML).toEqual('TEST'); + + fixture.destroy(); + + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.use('en'); + + expect(fixture.componentInstance.withParamsNoKey.nativeElement.innerHTML).toEqual('TEST'); + }); + + it('should unsubscribe from default lang change subscription on destroy', () => { + expect(fixture.componentInstance.withParamsNoKey.nativeElement.innerHTML).toEqual('TEST'); + + fixture.destroy(); + + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.setDefaultLang('en'); + + expect(fixture.componentInstance.withParamsNoKey.nativeElement.innerHTML).toEqual('TEST'); + }); + + it('should update the DOM when the translation changes', () => { + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('TEST'); + expect(fixture.componentInstance.withParams.nativeElement.innerHTML).toEqual('TEST'); + expect(fixture.componentInstance.noContent.nativeElement.innerHTML).toEqual('TEST'); + + translate.setTranslation('en', {'TEST': 'first text'}); + translate.use('en'); + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('first text'); + + translate.setTranslation('en', {'TEST': 'updated text'}); + expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('updated text'); + }); + + +}); diff --git a/projects/ngx-translate/src/lib/translate.loader.spec.ts b/projects/ngx-translate/src/lib/translate.loader.spec.ts index 8c6e05a..3716f4a 100644 --- a/projects/ngx-translate/src/lib/translate.loader.spec.ts +++ b/projects/ngx-translate/src/lib/translate.loader.spec.ts @@ -3,9 +3,10 @@ import {Observable, of} from "rxjs"; import { TranslateFakeLoader, TranslateLoader, - TranslateModule, TranslateService, - TranslationObject, Translation + TranslationObject, + Translation, + provideTranslateService } from "../public-api"; const translations: TranslationObject = {"TEST": "This is a test"}; @@ -21,8 +22,8 @@ describe('TranslateLoader', () => { it('should be able to provide TranslateStaticLoader', () => { TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot({ + providers: [ + provideTranslateService({ loader: {provide: TranslateLoader, useClass: FakeLoader} }) ], @@ -50,8 +51,8 @@ describe('TranslateLoader', () => { } TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot({ + providers: [ + provideTranslateService({ loader: {provide: TranslateLoader, useClass: CustomLoader} }) ] @@ -73,8 +74,8 @@ describe('TranslateLoader', () => { it('TranslateFakeLoader should return empty object', () => { TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot({ + providers: [ + provideTranslateService({ loader: {provide: TranslateLoader, useClass: TranslateFakeLoader} }) ], diff --git a/projects/ngx-translate/src/lib/translate.pipe.spec.ts b/projects/ngx-translate/src/lib/translate.pipe-modules.spec.ts similarity index 99% rename from projects/ngx-translate/src/lib/translate.pipe.spec.ts rename to projects/ngx-translate/src/lib/translate.pipe-modules.spec.ts index 8434eea..652d79d 100644 --- a/projects/ngx-translate/src/lib/translate.pipe.spec.ts +++ b/projects/ngx-translate/src/lib/translate.pipe-modules.spec.ts @@ -56,7 +56,7 @@ class FakeLoader implements TranslateLoader { } } -describe('TranslatePipe', () => { +describe('TranslatePipe (modules)', () => { let translate: TranslateService; let translatePipe: TranslatePipe; let ref: FakeChangeDetectorRef; diff --git a/projects/ngx-translate/src/lib/translate.pipe-standalone.spec.ts b/projects/ngx-translate/src/lib/translate.pipe-standalone.spec.ts new file mode 100644 index 0000000..57aae2e --- /dev/null +++ b/projects/ngx-translate/src/lib/translate.pipe-standalone.spec.ts @@ -0,0 +1,289 @@ +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Injectable, ViewContainerRef} from "@angular/core"; +import {TestBed} from "@angular/core/testing"; +import {Observable, of} from "rxjs"; +import { + DefaultLangChangeEvent, + LangChangeEvent, provideTranslateService, + TranslateLoader, + TranslatePipe, + TranslateService, + TranslationObject +} from "../public-api"; + +class FakeChangeDetectorRef extends ChangeDetectorRef { + markForCheck(): void { + // Intentionally left empty + } + + detach(): void { + // Intentionally left empty + } + + detectChanges(): void { + // Intentionally left empty + } + + checkNoChanges(): void { + // Intentionally left empty + } + + reattach(): void { + // Intentionally left empty + } +} + +@Injectable() +@Component({ + selector: 'lib-hmx-app', + standalone: true, + imports: [TranslatePipe], + changeDetection: ChangeDetectionStrategy.OnPush, + template: `{{'TEST' | translate}}` +}) +class AppComponent { + viewContainerRef: ViewContainerRef; + + constructor(viewContainerRef: ViewContainerRef) { + this.viewContainerRef = viewContainerRef; + } +} + +let translations: TranslationObject = {"TEST": "This is a test"}; + +class FakeLoader implements TranslateLoader { + getTranslation(lang: string): Observable { + void lang; + return of(translations); + } +} + +describe('TranslatePipe (standalone)', () => { + let translate: TranslateService; + let translatePipe: TranslatePipe; + let ref: FakeChangeDetectorRef; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + provideTranslateService({ + loader: {provide: TranslateLoader, useClass: FakeLoader} + }) + ] + }); + translate = TestBed.inject(TranslateService); + ref = new FakeChangeDetectorRef(); + translatePipe = new TranslatePipe(translate, ref); + }); + + afterEach(() => { + translations = {"TEST": "This is a test"}; + }); + + it('is defined', () => { + expect(TranslatePipe).toBeDefined(); + expect(translatePipe).toBeDefined(); + expect(translatePipe instanceof TranslatePipe).toBeTruthy(); + }); + + it('should translate a string', () => { + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.use('en'); + + expect(translatePipe.transform('TEST')).toEqual("This is a test"); + }); + + it('should call markForChanges when it translates a string', () => { + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.use('en'); + spyOn(ref, 'markForCheck').and.callThrough(); + + translatePipe.transform('TEST'); + expect(ref.markForCheck).toHaveBeenCalled(); + }); + + it('should translate a string with object parameters', () => { + translate.setTranslation('en', {"TEST": "This is a test {{param}}"}); + translate.use('en'); + + expect(translatePipe.transform('TEST', {param: "with param"})).toEqual("This is a test with param"); + }); + + it('should translate a string with object as string parameters', () => { + translate.setTranslation('en', {"TEST": "This is a test {{param}}"}); + translate.use('en'); + + expect(translatePipe.transform('TEST', '{param: "with param"}')).toEqual("This is a test with param"); + expect(translatePipe.transform('TEST', '{"param": "with param"}')).toEqual("This is a test with param"); + expect(translatePipe.transform('TEST', "{param: 'with param'}")).toEqual("This is a test with param"); + expect(translatePipe.transform('TEST', "{'param' : 'with param'}")).toEqual("This is a test with param"); + }); + + it('should translate a string with object as multiple string parameters', () => { + translate.setTranslation('en', {"TEST": "This is a test {{param1}} {{param2}}"}); + translate.use('en'); + + expect(translatePipe.transform('TEST', '{param1: "with param-1", param2: "and param-2"}')) + .toEqual("This is a test with param-1 and param-2"); + expect(translatePipe.transform('TEST', '{"param1": "with param-1", "param2": "and param-2"}')) + .toEqual("This is a test with param-1 and param-2"); + expect(translatePipe.transform('TEST', "{param1: 'with param-1', param2: 'and param-2'}")) + .toEqual("This is a test with param-1 and param-2"); + expect(translatePipe.transform('TEST', "{'param1' : 'with param-1', 'param2': 'and param-2'}")) + .toEqual("This is a test with param-1 and param-2"); + }); + + it('should translate a string with object as nested string parameters', () => { + translate.setTranslation('en', {"TEST": "This is a test {{param.one}} {{param.two}}"}); + translate.use('en'); + + expect(translatePipe.transform('TEST', '{param: {one: "with param-1", two: "and param-2"}}')) + .toEqual("This is a test with param-1 and param-2"); + expect(translatePipe.transform('TEST', '{"param": {"one": "with param-1", "two": "and param-2"}}')) + .toEqual("This is a test with param-1 and param-2"); + expect(translatePipe.transform('TEST', "{param: {one: 'with param-1', two: 'and param-2'}}")) + .toEqual("This is a test with param-1 and param-2"); + expect(translatePipe.transform('TEST', "{'param' : {'one': 'with param-1', 'two': 'and param-2'}}")) + .toEqual("This is a test with param-1 and param-2"); + }); + + it('should update the value when the parameters change', () => { + translate.setTranslation('en', {"TEST": "This is a test {{param}}"}); + translate.use('en'); + + spyOn(translatePipe, 'updateValue').and.callThrough(); + spyOn(ref, 'markForCheck').and.callThrough(); + + expect(translatePipe.transform('TEST', {param: "with param"})).toEqual("This is a test with param"); + expect(translatePipe.updateValue).toHaveBeenCalledTimes(1); + + // same value, shouldn't call 'updateValue' again + expect(translatePipe.transform('TEST', {param: "with param"})).toEqual("This is a test with param"); + expect(translatePipe.updateValue).toHaveBeenCalledTimes(1); + + // different param, should call 'updateValue' + expect(translatePipe.transform('TEST', {param: "with param2"})).toEqual("This is a test with param2"); + expect(translatePipe.updateValue).toHaveBeenCalledTimes(2); + + expect(ref.markForCheck).toHaveBeenCalledTimes(2); + }); + + it("should throw if you don't give an object parameter", () => { + translate.setTranslation('en', {"TEST": "This is a test {{param}}"}); + translate.use('en'); + const param = 'param: "with param"'; + + expect(() => { + translatePipe.transform('TEST', param); + }).toThrowError(`Wrong parameter in TranslatePipe. Expected a valid Object, received: ${param}`); + }); + + it("should return given falsey or non length query", () => { + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.use('en'); + + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + expect(translatePipe.transform(null as any)).toBeNull(); + + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + expect(translatePipe.transform(undefined as any)).toBeUndefined(); + + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + expect(translatePipe.transform(1234 as any)).toBe(1234); + }); + + describe('should update translations on lang change', () => { + it('with fake loader', (done) => { + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.setTranslation('fr', {"TEST": "C'est un test"}); + translate.use('en'); + + expect(translatePipe.transform('TEST')).toEqual("This is a test"); + + // this will be resolved at the next lang change + const subscription = translate.onLangChange.subscribe((res: LangChangeEvent) => { + expect(res.lang).toEqual('fr'); + expect(translatePipe.transform('TEST')).toEqual("C'est un test"); + subscription.unsubscribe(); + done(); + }); + + translate.use('fr'); + }); + + it('with file loader', (done) => { + translate.use('en'); + expect(translatePipe.transform('TEST')).toEqual("This is a test"); + + // this will be resolved at the next lang change + const subscription = translate.onLangChange.subscribe((res: LangChangeEvent) => { + // let it update the translations + setTimeout(() => { + expect(res.lang).toEqual('fr'); + expect(translatePipe.transform('TEST')).toEqual("C'est un test"); + subscription.unsubscribe(); + done(); + }); + }); + + translations = {"TEST": "C'est un test"}; + translate.use('fr'); + }); + + it('should detect changes with OnPush', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML).toEqual("TEST"); + translate.use('en'); + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML).toEqual("This is a test"); + }); + }); + + describe('should update translations on default lang change', () => { + it('with fake loader', (done) => { + translate.setTranslation('en', {"TEST": "This is a test"}); + translate.setTranslation('fr', {"TEST": "C'est un test"}); + translate.setDefaultLang('en'); + + expect(translatePipe.transform('TEST')).toEqual("This is a test"); + + // this will be resolved at the next lang change + const subscription = translate.onDefaultLangChange.subscribe((res: DefaultLangChangeEvent) => { + expect(res.lang).toEqual('fr'); + expect(translatePipe.transform('TEST')).toEqual("C'est un test"); + subscription.unsubscribe(); + done(); + }); + + translate.setDefaultLang('fr'); + }); + + it('with file loader', (done) => { + translate.setDefaultLang('en'); + expect(translatePipe.transform('TEST')).toEqual("This is a test"); + + // this will be resolved at the next lang change + const subscription = translate.onDefaultLangChange.subscribe((res: DefaultLangChangeEvent) => { + // let it update the translations + setTimeout(() => { + expect(res.lang).toEqual('fr'); + expect(translatePipe.transform('TEST')).toEqual("C'est un test"); + subscription.unsubscribe(); + done(); + }); + }); + + translations = {"TEST": "C'est un test"}; + translate.setDefaultLang('fr'); + }); + + it('should detect changes with OnPush', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML).toEqual("TEST"); + translate.setDefaultLang('en'); + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML).toEqual("This is a test"); + }); + }); +}); diff --git a/projects/ngx-translate/src/lib/translate.service.spec.ts b/projects/ngx-translate/src/lib/translate.service.spec.ts index 69e57f7..b7be18e 100644 --- a/projects/ngx-translate/src/lib/translate.service.spec.ts +++ b/projects/ngx-translate/src/lib/translate.service.spec.ts @@ -4,9 +4,8 @@ import {take, toArray, first, map} from "rxjs/operators"; import { LangChangeEvent, TranslateLoader, - TranslateModule, TranslateService, - TranslationChangeEvent, TranslationObject, Translation + TranslationChangeEvent, TranslationObject, Translation, provideTranslateService } from "../public-api"; @@ -40,8 +39,8 @@ describe("TranslateService", () => beforeEach(() => { TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot({ + providers: [ + provideTranslateService({ loader: {provide: TranslateLoader, useClass: FakeLoader} }) ] diff --git a/projects/ngx-translate/src/lib/translate.service.ts b/projects/ngx-translate/src/lib/translate.service.ts index 7bd9ec8..fdb3bfa 100644 --- a/projects/ngx-translate/src/lib/translate.service.ts +++ b/projects/ngx-translate/src/lib/translate.service.ts @@ -84,7 +84,9 @@ const makeObservable = (value: T | Observable): Observable => { return isObservable(value) ? value : of(value); }; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class TranslateService { private loadingTranslations!: Observable; private pending = false; diff --git a/projects/ngx-translate/src/lib/translate.store.spec.ts b/projects/ngx-translate/src/lib/translate.store.spec.ts index 870fc15..04e104f 100644 --- a/projects/ngx-translate/src/lib/translate.store.spec.ts +++ b/projects/ngx-translate/src/lib/translate.store.spec.ts @@ -69,7 +69,7 @@ function createRoot(router: Router, type: Type): ComponentFixture { return f; } -describe("module", () => { +describe("TranslateStore", () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [ @@ -78,7 +78,7 @@ describe("module", () => { ], declarations: [RootComponent], providers: [ - provideRouter([]) + provideRouter([]), ], }); }); diff --git a/projects/ngx-translate/src/public-api.ts b/projects/ngx-translate/src/public-api.ts index 63ab8db..b6e8149 100644 --- a/projects/ngx-translate/src/public-api.ts +++ b/projects/ngx-translate/src/public-api.ts @@ -6,7 +6,13 @@ import {TranslateCompiler, TranslateFakeCompiler} from "./lib/translate.compiler import {TranslateDirective} from "./lib/translate.directive"; import {TranslatePipe} from "./lib/translate.pipe"; import {TranslateStore} from "./lib/translate.store"; -import {USE_DEFAULT_LANG, DEFAULT_LANGUAGE, USE_STORE, TranslateService, USE_EXTEND} from "./lib/translate.service"; +import { + USE_DEFAULT_LANG, + DEFAULT_LANGUAGE, + USE_EXTEND, + USE_STORE, + TranslateService +} from "./lib/translate.service"; export * from "./lib/translate.loader"; export * from "./lib/translate.service";