Skip to content

Commit

Permalink
Feature/cxspa 2132 v2 (#18873)
Browse files Browse the repository at this point in the history
Co-authored-by: Paweł Fraś <fras.pawel@yahoo.com>
Co-authored-by: Krzysztof Platis <platonn.git@gmail.com>
  • Loading branch information
3 people committed May 20, 2024
1 parent 11540c3 commit e2f79c3
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 169 deletions.
287 changes: 160 additions & 127 deletions projects/core/src/i18n/i18next/i18next-translation.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,26 @@ import { I18NEXT_INSTANCE } from './i18next-instance';
import { I18nextTranslationService } from './i18next-translation.service';

const testKey = 'testKey';
const testFallbackKey = 'testFallbackKey';
const testSecondFallbackKey = 'testSecondFallbackKey';
const testKeys = [
testKey,
[testKey, testFallbackKey],
[testKey, testFallbackKey, testSecondFallbackKey],
];

const testChunk = 'testChunk';
const testOptions = 'testOptions';
const nonBreakingSpace = String.fromCharCode(160);

const getDescription = (key: string | string[]) => {
return `${(Array.isArray(key) ? key : [key]).length} translation key(s) `;
};

const getNamespacedKeys = (key: string | string[]) => {
return (Array.isArray(key) ? key : [key]).map((k) => `${testChunk}:${k}`);
};

describe('I18nextTranslationService', () => {
let service: I18nextTranslationService;
let i18next: i18n;
Expand All @@ -19,9 +36,11 @@ describe('I18nextTranslationService', () => {
const mockTranslationChunk = {
getChunkNameForKey: jasmine
.createSpy('getChunkNameForKey')
.and.returnValue('testChunk'),
.and.returnValue(testChunk),
};

spyOn(console, 'warn');

TestBed.configureTestingModule({
providers: [
{ provide: I18nConfig, useValue: { production: false } },
Expand Down Expand Up @@ -49,142 +68,156 @@ describe('I18nextTranslationService', () => {
});

describe('translate', () => {
beforeEach(() => {
i18next.isInitialized = true;
});

describe(', when key exists,', () => {
beforeEach(() => {
spyOn(i18next, 'exists').and.returnValue(true);
});

it('should emit result of i18next.t', () => {
spyOn(i18next, 't').and.returnValue('value');
let result;
service
.translate(testKey, testOptions)
.pipe(first())
.subscribe((x) => (result = x));

expect(i18next.t).toHaveBeenCalledWith(
'testChunk:testKey',
testOptions
);
expect(result).toBe('value');
});
});

describe(', when key does NOT exist,', () => {
beforeEach(() => {
spyOn(i18next, 'exists').and.returnValue(false);
spyOn(i18next, 'loadNamespaces').and.returnValue(new Promise(() => {}));
});

it('should emit non-breaking space if whitespaceUntilLoaded is true', () => {
let result;
service
.translate(testKey, testOptions, true)
.pipe(first())
.subscribe((x) => (result = x));
expect(result).toBe(nonBreakingSpace);
});

it('should NOT emit any value if whitespaceUntilLoaded is false', () => {
let result = 'initial value';
service
.translate(testKey, testOptions, false)
.pipe(first())
.subscribe((x) => (result = x));
expect(result).toBe('initial value');
});

it('should load chunk of key', () => {
service.translate(testKey, testOptions).pipe(first()).subscribe();

expect(i18next.loadNamespaces).toHaveBeenCalledWith(
'testChunk',
jasmine.any(Function)
);
});
});

describe(', when key does NOT exist even after chunk was loaded,', () => {
beforeEach(() => {
spyOn(i18next, 'exists').and.returnValues(false, false);
spyOn(i18next, 'loadNamespaces').and.callFake(((
_namespaces,
onChunkLoad
) => onChunkLoad()) as any);
});

it('should emit key in brackets for non-production', () => {
let result;
service
.translate(testKey, testOptions)
.pipe(first())
.subscribe((x) => (result = x));
expect(result).toBe(`[testChunk:testKey]`);
});

it('should return non-breaking space for production', () => {
spyOnProperty(AngularCore, 'isDevMode').and.returnValue(() => false);
let result;
service
.translate(testKey, testOptions)
.pipe(first())
.subscribe((x) => (result = x));
expect(result).toBe(nonBreakingSpace);
});
});

describe(', when key does NOT exist firstly, but it comes with loaded chunk,', () => {
describe(' when i18next is NOT initialized', () => {
beforeEach(() => {
spyOn(i18next, 'exists').and.returnValues(false, true);
spyOn(i18next, 'loadNamespaces').and.callFake(((
_namespaces,
onChunkLoad
) => onChunkLoad()) as any);
i18next.isInitialized = false;
spyOn(i18next, 'loadNamespaces');
spyOn(i18next, 'exists');
spyOn(i18next, 't');
});

it('should emit result of i18next.t', () => {
spyOn(i18next, 't').and.returnValue('value');
it('should return and not call translate method', () => {
let result;
service
.translate(testKey, testOptions)
.translate(testKey)
.pipe(first())
.subscribe((x) => (result = x));
expect(i18next.t).toHaveBeenCalledWith(
'testChunk:testKey',
testOptions
);
expect(result).toBe('value');
expect(result).toBe(undefined);
expect(i18next.loadNamespaces).not.toHaveBeenCalled();
expect(i18next.exists).not.toHaveBeenCalled();
expect(i18next.t).not.toHaveBeenCalled();
});
});

describe(', when language changed,', () => {
it('should emit result of i18next.t in new language', () => {
let languageChangedCallback;
spyOn(i18next, 'off');
spyOn(i18next, 'on').and.callFake(
(_event, callback) => (languageChangedCallback = callback)
);
spyOn(i18next, 'exists').and.returnValue(true);
spyOn(i18next, 't').and.returnValues('value1', 'value2');

let result;
service
.translate(testKey, testOptions)
.pipe(take(2))
.subscribe((x) => (result = x));
expect(result).toBe('value1');

languageChangedCallback();
expect(result).toBe('value2');

expect(i18next.off).toHaveBeenCalledWith(
'languageChanged',
languageChangedCallback
);
testKeys.forEach((key) => {
describe(getDescription(key), () => {
const namespacedKeys = getNamespacedKeys(key);
beforeEach(() => {
i18next.isInitialized = true;
});

describe(', when key exists,', () => {
beforeEach(() => {
spyOn(i18next, 'exists').and.returnValue(true);
});

it('should emit result of i18next.t', () => {
spyOn(i18next, 't').and.returnValue('value');
let result;
service
.translate(key, testOptions)
.pipe(first())
.subscribe((x) => (result = x));

expect(i18next.t).toHaveBeenCalledWith(namespacedKeys, testOptions);
expect(result).toBe('value');
});
});

describe(', when key does NOT exist,', () => {
beforeEach(() => {
spyOn(i18next, 'exists').and.returnValue(false);
spyOn(i18next, 'loadNamespaces').and.returnValue(
new Promise(() => {})
);
});

it('should emit non-breaking space if whitespaceUntilLoaded is true', () => {
let result;
service
.translate(key, testOptions, true)
.pipe(first())
.subscribe((x) => (result = x));
expect(result).toBe(nonBreakingSpace);
});

it('should NOT emit any value if whitespaceUntilLoaded is false', () => {
let result = 'initial value';
service
.translate(key, testOptions, false)
.pipe(first())
.subscribe((x) => (result = x));
expect(result).toBe('initial value');
});

it('should load chunk of key', () => {
service.translate(key, testOptions).pipe(first()).subscribe();

expect(i18next.loadNamespaces).toHaveBeenCalledWith(
Array(namespacedKeys.length).fill(`${testChunk}`),
jasmine.any(Function)
);
});
});

describe(', when key does NOT exist even after chunk was loaded,', () => {
beforeEach(() => {
spyOn(i18next, 'exists').and.returnValue(false);
spyOn(i18next, 'loadNamespaces').and.callFake(((
_namespaces,
onChunkLoad
) => onChunkLoad()) as any);
});

it('should emit key in brackets for non-production', () => {
let result;
service
.translate(key, testOptions)
.pipe(first())
.subscribe((x) => (result = x));
expect(result).toEqual(`[${namespacedKeys.join(', ')}]`);
});

it('should return non-breaking space for production', () => {
spyOnProperty(AngularCore, 'isDevMode').and.returnValue(
() => false
);
let result;
service
.translate(key, testOptions)
.pipe(first())
.subscribe((x) => (result = x));
expect(result).toBe(nonBreakingSpace);
});

it('should log a warning with information which keys are missing', () => {
const keys = Array.isArray(key) ? key : [key];
const expected = `Translation keys missing [${keys.join(
', '
)}]. Attempted to load ${keys
.map((key) => `'${key}' from chunk 'testChunk'`)
.join(', ')}.`;
service.translate(key, testOptions).pipe(first()).subscribe();
expect(console.warn).toHaveBeenCalledWith(expected);
});
});

describe(', when language changed,', () => {
it('should emit result of i18next.t in new language', () => {
let languageChangedCallback;
spyOn(i18next, 'off');
spyOn(i18next, 'on').and.callFake(
(_event, callback) => (languageChangedCallback = callback)
);
spyOn(i18next, 'exists').and.returnValue(true);
spyOn(i18next, 't').and.returnValues('value1', 'value2');

let result;
service
.translate(key, testOptions)
.pipe(take(2))
.subscribe((x) => (result = x));
expect(result).toBe('value1');

languageChangedCallback();
expect(result).toBe('value2');

expect(i18next.off).toHaveBeenCalledWith(
'languageChanged',
languageChangedCallback
);
});
});
});
});
});
Expand Down

0 comments on commit e2f79c3

Please sign in to comment.