@tolgee/core 4.7.0 → 4.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/tolgee.cjs.js +2 -4
  2. package/dist/tolgee.cjs.js.map +1 -1
  3. package/dist/tolgee.cjs.min.js +1 -1
  4. package/dist/tolgee.cjs.min.js.map +1 -1
  5. package/dist/{tolgee.esm.js → tolgee.esm.min.mjs} +2 -2
  6. package/dist/tolgee.esm.min.mjs.map +1 -0
  7. package/dist/tolgee.esm.mjs +5690 -0
  8. package/dist/tolgee.esm.mjs.map +1 -0
  9. package/dist/tolgee.umd.js +2 -4
  10. package/dist/tolgee.umd.js.map +1 -1
  11. package/dist/tolgee.umd.min.js +1 -1
  12. package/dist/tolgee.umd.min.js.map +1 -1
  13. package/package.json +10 -9
  14. package/src/Constants/Global.ts +9 -0
  15. package/src/Constants/ModifierKey.ts +6 -0
  16. package/src/Errors/ApiHttpError.ts +8 -0
  17. package/src/Observer.test.ts +119 -0
  18. package/src/Observer.ts +68 -0
  19. package/src/Properties.test.ts +150 -0
  20. package/src/Properties.ts +112 -0
  21. package/src/Tolgee.test.ts +473 -0
  22. package/src/Tolgee.ts +335 -0
  23. package/src/TolgeeConfig.test.ts +21 -0
  24. package/src/TolgeeConfig.ts +134 -0
  25. package/src/__integration/FormatterIcu.test.ts +80 -0
  26. package/src/__integration/FormatterMissing.ts +54 -0
  27. package/src/__integration/Tolgee.test.ts +90 -0
  28. package/src/__integration/TolgeeInvisible.test.ts +145 -0
  29. package/src/__integration/mockTranslations.ts +6 -0
  30. package/src/__integration/testConfig.ts +16 -0
  31. package/src/__testFixtures/classMock.ts +11 -0
  32. package/src/__testFixtures/createElement.ts +43 -0
  33. package/src/__testFixtures/createTestDom.ts +25 -0
  34. package/src/__testFixtures/mocked.ts +25 -0
  35. package/src/__testFixtures/setupAfterEnv.ts +34 -0
  36. package/src/helpers/NodeHelper.ts +90 -0
  37. package/src/helpers/TextHelper.test.ts +62 -0
  38. package/src/helpers/TextHelper.ts +58 -0
  39. package/src/helpers/commonTypes.ts +8 -0
  40. package/src/helpers/encoderPolyfill.ts +96 -0
  41. package/src/helpers/secret.test.ts +61 -0
  42. package/src/helpers/secret.ts +68 -0
  43. package/src/helpers/sleep.ts +2 -0
  44. package/src/highlighter/HighlightFunctionsInitializer.test.ts +40 -0
  45. package/src/highlighter/HighlightFunctionsInitializer.ts +61 -0
  46. package/src/highlighter/MouseEventHandler.test.ts +151 -0
  47. package/src/highlighter/MouseEventHandler.ts +191 -0
  48. package/src/highlighter/TranslationHighlighter.test.ts +177 -0
  49. package/src/highlighter/TranslationHighlighter.ts +113 -0
  50. package/src/index.ts +10 -0
  51. package/src/internal.ts +2 -0
  52. package/src/modules/IcuFormatter.ts +17 -0
  53. package/src/modules/index.ts +1 -0
  54. package/src/services/ApiHttpService.ts +85 -0
  55. package/src/services/CoreService.test.ts +142 -0
  56. package/src/services/CoreService.ts +76 -0
  57. package/src/services/DependencyService.test.ts +51 -0
  58. package/src/services/DependencyService.ts +116 -0
  59. package/src/services/ElementRegistrar.test.ts +131 -0
  60. package/src/services/ElementRegistrar.ts +108 -0
  61. package/src/services/EventEmitter.ts +52 -0
  62. package/src/services/EventService.ts +14 -0
  63. package/src/services/ModuleService.ts +14 -0
  64. package/src/services/ScreenshotService.ts +31 -0
  65. package/src/services/Subscription.ts +7 -0
  66. package/src/services/TextService.test.ts +88 -0
  67. package/src/services/TextService.ts +82 -0
  68. package/src/services/TranslationService.test.ts +358 -0
  69. package/src/services/TranslationService.ts +417 -0
  70. package/src/services/__mocks__/CoreService.ts +17 -0
  71. package/src/toolsManager/Messages.test.ts +79 -0
  72. package/src/toolsManager/Messages.ts +60 -0
  73. package/src/toolsManager/PluginManager.test.ts +108 -0
  74. package/src/toolsManager/PluginManager.ts +129 -0
  75. package/src/types/DTOs.ts +25 -0
  76. package/src/types/apiSchema.generated.ts +6208 -0
  77. package/src/types.ts +146 -0
  78. package/src/wrappers/AbstractWrapper.ts +14 -0
  79. package/src/wrappers/NodeHandler.ts +143 -0
  80. package/src/wrappers/WrappedHandler.ts +28 -0
  81. package/src/wrappers/invisible/AttributeHandler.ts +23 -0
  82. package/src/wrappers/invisible/Coder.ts +65 -0
  83. package/src/wrappers/invisible/ContentHandler.ts +15 -0
  84. package/src/wrappers/invisible/CoreHandler.ts +17 -0
  85. package/src/wrappers/invisible/InvisibleWrapper.ts +59 -0
  86. package/src/wrappers/invisible/ValueMemory.test.ts +25 -0
  87. package/src/wrappers/invisible/ValueMemory.ts +16 -0
  88. package/src/wrappers/text/AttributeHandler.test.ts +117 -0
  89. package/src/wrappers/text/AttributeHandler.ts +25 -0
  90. package/src/wrappers/text/Coder.test.ts +298 -0
  91. package/src/wrappers/text/Coder.ts +202 -0
  92. package/src/wrappers/text/ContentHandler.test.ts +185 -0
  93. package/src/wrappers/text/ContentHandler.ts +21 -0
  94. package/src/wrappers/text/CoreHandler.test.ts +106 -0
  95. package/src/wrappers/text/CoreHandler.ts +45 -0
  96. package/src/wrappers/text/TextWrapper.ts +69 -0
  97. package/dist/tolgee.esm.js.map +0 -1
@@ -0,0 +1,185 @@
1
+ jest.dontMock('./ContentHandler');
2
+ jest.dontMock('./TextWrapper');
3
+ jest.dontMock('../NodeHandler');
4
+ jest.dontMock('../../services/EventService');
5
+ jest.dontMock('../../helpers/NodeHelper.ts');
6
+ jest.dontMock('../../services/DependencyService');
7
+
8
+ import { ContentHandler } from './ContentHandler';
9
+ import '@testing-library/jest-dom/extend-expect';
10
+ import { ElementWithMeta, NodeWithMeta, Unwrapped } from '../../types';
11
+ import { NodeHelper } from '../../helpers/NodeHelper';
12
+ import { createTestDom } from '@testFixtures/createTestDom';
13
+ import { getMockedInstance } from '@testFixtures/mocked';
14
+ import { ElementRegistrar } from '../../services/ElementRegistrar';
15
+ import { Properties } from '../../Properties';
16
+ import { DependencyService } from '../../services/DependencyService';
17
+ import { Coder } from './Coder';
18
+ import { TextWrapper } from './TextWrapper';
19
+
20
+ describe('TextHandler', () => {
21
+ let textHandler: ContentHandler;
22
+
23
+ const mockedKeys = [
24
+ {
25
+ key: 'dummyKey',
26
+ params: { dummyParam: 'dummyValue' },
27
+ },
28
+ ];
29
+
30
+ const mockedTranslateInner = (text) => {
31
+ return {
32
+ text: text.replace(/{{(.*?)}}/gs, 'translated $1'),
33
+ keys: mockedKeys,
34
+ } as Unwrapped;
35
+ };
36
+
37
+ const gv = (key) => mockedTranslateInner(key).text;
38
+ const mockedTranslate = jest.fn(mockedTranslateInner);
39
+ let c: ReturnType<typeof createTestDom>;
40
+ beforeEach(() => {
41
+ const dependencyService = new DependencyService();
42
+ dependencyService.init({});
43
+
44
+ // @ts-ignore
45
+ textHandler = (dependencyService.wrapper as TextWrapper).textHandler;
46
+ getMockedInstance(Properties).config = {
47
+ inputPrefix: '{{',
48
+ inputSuffix: '}}',
49
+ restrictedElements: [],
50
+ tagAttributes: {
51
+ '*': ['aria-label'],
52
+ },
53
+ passToParent: ['option'],
54
+ };
55
+ c = createTestDom(document);
56
+ getMockedInstance(Coder).unwrap = (...args) => mockedTranslate(...args);
57
+ });
58
+
59
+ afterEach(async () => {
60
+ jest.clearAllMocks();
61
+ });
62
+
63
+ describe('in production mode', () => {
64
+ beforeEach(async () => {
65
+ await textHandler.handle(document.body);
66
+ });
67
+
68
+ test('Can be created', () => {
69
+ expect(textHandler).not.toBeUndefined();
70
+ });
71
+
72
+ test('will handle text in root', async () => {
73
+ expect(`./text()[contains(., ${gv(c.keyInRoot)})]`).toBeFoundIn(
74
+ document.body
75
+ );
76
+ });
77
+
78
+ test('will handle text in div', async () => {
79
+ expect(
80
+ `./div/text()[contains(., 'Some trash... translated ${gv(
81
+ c.keyInRootDiv
82
+ )}')]`
83
+ ).toBeFoundIn(document.body);
84
+ });
85
+
86
+ test('will handle text in div > div > span', async () => {
87
+ const xpath = `./div/div/span/text()[contains(., 'translated ${gv(
88
+ c.hereKey
89
+ )} and translated ${gv(c.hereTooKey)}')]`;
90
+ expect(xpath).toBeFoundIn(document.body);
91
+ });
92
+
93
+ describe("Node's _tolgee property", () => {
94
+ let node: NodeWithMeta;
95
+
96
+ beforeEach(() => {
97
+ node = NodeHelper.evaluateToSingle(
98
+ `./text()[contains(., ${gv(c.keyInRoot)})]`,
99
+ document.body
100
+ ) as NodeWithMeta;
101
+ });
102
+
103
+ test('will be defined', () => {
104
+ expect(node._tolgee).toBeDefined();
105
+ });
106
+
107
+ test('will have proper oldTextContent', () => {
108
+ expect(node._tolgee.oldTextContent).toContain(`{{${c.keyInRoot}}}`);
109
+ });
110
+
111
+ test('will have proper keys length', () => {
112
+ expect(node._tolgee.keys).toHaveLength(1);
113
+ });
114
+
115
+ test('will have proper first key', () => {
116
+ expect(node._tolgee.keys).toEqual(mockedKeys);
117
+ });
118
+ });
119
+
120
+ describe("Parent element's _tolgee property and attribute", () => {
121
+ let element: ElementWithMeta;
122
+ let node: NodeWithMeta;
123
+
124
+ beforeEach(() => {
125
+ node = NodeHelper.evaluateToSingle(
126
+ `./text()[contains(., ${gv(c.keyInRoot)})]`,
127
+ document.body
128
+ );
129
+ element = node.parentElement as any as ElementWithMeta;
130
+ });
131
+
132
+ test('property will be defined', () => {
133
+ expect(element._tolgee).toBeDefined();
134
+ });
135
+
136
+ test('will contain nodes array with correct node', () => {
137
+ expect(element._tolgee.nodes).toEqual(new Set([node]));
138
+ });
139
+
140
+ test('attribute will be set', () => {
141
+ expect(element.getAttribute('_tolgee')).toEqual('');
142
+ });
143
+ });
144
+
145
+ test("will pass option's text node to select element", () => {
146
+ const xPath = `//text()[contains(., 'translated option_key')]`;
147
+ const node = NodeHelper.evaluateToSingle(xPath, document.body);
148
+ expect(node.parentElement.parentElement).toHaveAttribute('_tolgee', '');
149
+ expect(node.parentElement).not.toHaveAttribute('_tolgee');
150
+ });
151
+ });
152
+
153
+ test('will pass recursively', async () => {
154
+ getMockedInstance(Properties).config.passToParent = ['option', 'select'];
155
+ await textHandler.handle(document.body);
156
+ const xPath = `//text()[contains(., 'translated option_key')]`;
157
+ const node = NodeHelper.evaluateToSingle(xPath, document.body);
158
+ expect(node.parentElement.parentElement.parentElement).toHaveAttribute(
159
+ '_tolgee',
160
+ ''
161
+ );
162
+ expect(node.parentElement.parentElement).not.toHaveAttribute('_tolgee');
163
+ });
164
+
165
+ test('will pass with function', async () => {
166
+ getMockedInstance(Properties).config.passToParent = (element) =>
167
+ element.tagName === 'OPTION';
168
+ await textHandler.handle(document.body);
169
+ const xPath = `//text()[contains(., 'translated option_key')]`;
170
+ const node = NodeHelper.evaluateToSingle(xPath, document.body);
171
+ expect(node.parentElement.parentElement).toHaveAttribute('_tolgee', '');
172
+ expect(node.parentElement).not.toHaveAttribute('_tolgee');
173
+ });
174
+
175
+ test('will register the node', async () => {
176
+ await textHandler.handle(document.body);
177
+ const node = NodeHelper.evaluateToSingle(
178
+ `./text()[contains(., ${gv(c.keyInRoot)})]`,
179
+ document.body
180
+ );
181
+ expect(getMockedInstance(ElementRegistrar).register).toBeCalledWith(
182
+ node.parentElement
183
+ );
184
+ });
185
+ });
@@ -0,0 +1,21 @@
1
+ import { NodeHelper } from '../../helpers/NodeHelper';
2
+ import { Properties } from '../../Properties';
3
+ import { NodeHandler } from '../NodeHandler';
4
+
5
+ export class ContentHandler {
6
+ constructor(
7
+ private properties: Properties,
8
+ private nodeHandler: NodeHandler
9
+ ) {}
10
+
11
+ async handle(node: Node): Promise<void> {
12
+ const inputPrefix = this.properties.config.inputPrefix;
13
+ const inputSuffix = this.properties.config.inputSuffix;
14
+
15
+ const xPath = `./descendant-or-self::text()[contains(., '${inputPrefix}') and contains(., '${inputSuffix}')]`;
16
+ const nodes = NodeHelper.evaluate(xPath, node);
17
+ const filtered: Text[] = this.nodeHandler.filterRestricted(nodes as Text[]);
18
+
19
+ await this.nodeHandler.handleNodes(filtered);
20
+ }
21
+ }
@@ -0,0 +1,106 @@
1
+ jest.dontMock('./CoreHandler');
2
+ jest.dontMock('./TextWrapper');
3
+ jest.dontMock('../../helpers/NodeHelper');
4
+ jest.dontMock('../../services/EventEmitter');
5
+ jest.dontMock('../../services/DependencyService');
6
+
7
+ import { CoreHandler } from './CoreHandler';
8
+ import { getMockedInstance } from '@testFixtures/mocked';
9
+ import { ContentHandler } from './ContentHandler';
10
+ import { AttributeHandler } from './AttributeHandler';
11
+ import { EventService } from '../../services/EventService';
12
+ import { EventEmitterImpl } from '../../services/EventEmitter';
13
+ import { mocked } from 'ts-jest/utils';
14
+ import { TranslationData } from '../../types/DTOs';
15
+ import { Properties } from '../../Properties';
16
+ import { ElementMeta, NodeMeta, NodeWithMeta, Unwrapped } from '../../types';
17
+ import { DependencyService } from '../../services/DependencyService';
18
+ import { Coder } from './Coder';
19
+
20
+ describe('CoreHandler', () => {
21
+ const mockedTranslationChanged = new EventEmitterImpl<TranslationData>();
22
+ const mockedLanguageChanged = new EventEmitterImpl<string>();
23
+ const mockedUnwrap: Unwrapped = {
24
+ keys: [{} as any],
25
+ text: 'This is refreshed',
26
+ };
27
+
28
+ mocked(EventService).mockImplementation(() => {
29
+ return {
30
+ TRANSLATION_CHANGED: mockedTranslationChanged,
31
+ LANGUAGE_CHANGED: mockedLanguageChanged,
32
+ LANGUAGE_LOADED: null,
33
+ ELEMENT_REGISTERED: null,
34
+ } as EventService;
35
+ });
36
+
37
+ mocked(Coder).mockImplementation(() => {
38
+ return {
39
+ unwrap: jest.fn(() => mockedUnwrap),
40
+ } as any as Coder;
41
+ });
42
+
43
+ let coreHandler: CoreHandler;
44
+
45
+ beforeEach(async () => {
46
+ const dependencyStore = new DependencyService();
47
+ dependencyStore.init({});
48
+
49
+ // @ts-ignore
50
+ coreHandler = dependencyStore.wrapper.coreHandler;
51
+ getMockedInstance(Properties).config.targetElement = document.body;
52
+ });
53
+
54
+ afterEach(async () => {
55
+ jest.clearAllMocks();
56
+ });
57
+
58
+ test('Can be created', () => {
59
+ expect(coreHandler).not.toBeUndefined();
60
+ });
61
+
62
+ describe('handleSubtree function', () => {
63
+ beforeEach(async () => {
64
+ await coreHandler.handleSubtree(document.body);
65
+ });
66
+
67
+ test('will call basic text handler', async () => {
68
+ expect(getMockedInstance(ContentHandler).handle).toBeCalledWith(
69
+ document.body
70
+ );
71
+ expect(getMockedInstance(ContentHandler).handle).toBeCalledTimes(1);
72
+ });
73
+
74
+ test('will call attribute handler', async () => {
75
+ expect(getMockedInstance(AttributeHandler).handle).toBeCalledWith(
76
+ document.body
77
+ );
78
+ expect(getMockedInstance(AttributeHandler).handle).toBeCalledTimes(1);
79
+ });
80
+ });
81
+
82
+ describe('refresh', () => {
83
+ const pgDiv = document.createElement('div');
84
+ pgDiv.setAttribute('_tolgee', '');
85
+ const textNode = document.createTextNode('Translated text');
86
+ textNode['_tolgee'] = {
87
+ oldTextContent: 'some_translation_key',
88
+ } as NodeMeta;
89
+ pgDiv['_tolgee'] = {
90
+ nodes: new Set([textNode as any as NodeWithMeta]),
91
+ } as ElementMeta;
92
+ pgDiv.append(textNode);
93
+
94
+ beforeEach(async () => {
95
+ document.body = document.createElement('body');
96
+ document.body.append(pgDiv);
97
+
98
+ await mockedLanguageChanged.emit('aaa');
99
+ await mockedTranslationChanged.emit(null);
100
+ });
101
+
102
+ test('will refresh the text', async () => {
103
+ expect(textNode.textContent).toEqual(mockedUnwrap.text);
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,45 @@
1
+ import { NodeHelper } from '../../helpers/NodeHelper';
2
+ import { ContentHandler } from './ContentHandler';
3
+ import { EventService } from '../../services/EventService';
4
+ import { Properties } from '../../Properties';
5
+ import { AttributeHandler } from './AttributeHandler';
6
+ import { ElementWithMeta } from '../../types';
7
+ import { WrappedHandler } from '../WrappedHandler';
8
+ import { Coder } from './Coder';
9
+
10
+ export class CoreHandler {
11
+ constructor(
12
+ private textHandler: ContentHandler,
13
+ private eventService: EventService,
14
+ private properties: Properties,
15
+ private attributeHandler: AttributeHandler,
16
+ private coder: Coder,
17
+ private wrappedHandler: WrappedHandler
18
+ ) {
19
+ if (typeof window !== 'undefined') {
20
+ eventService.LANGUAGE_CHANGED.subscribe(this.refresh.bind(this));
21
+ eventService.TRANSLATION_CHANGED.subscribe(this.refresh.bind(this));
22
+ }
23
+ }
24
+
25
+ public async handleSubtree(target: Element) {
26
+ await this.attributeHandler.handle(target);
27
+ await this.textHandler.handle(target);
28
+ await this.wrappedHandler.handle(target);
29
+ }
30
+
31
+ private async refresh() {
32
+ const nodes: ElementWithMeta[] = NodeHelper.evaluate(
33
+ `//*[@_tolgee]`,
34
+ this.properties.config.targetElement
35
+ );
36
+ for (const node of nodes) {
37
+ for (const textNode of node._tolgee.nodes) {
38
+ const result = await this.coder.unwrap(textNode._tolgee.oldTextContent);
39
+ if (result) {
40
+ NodeHelper.setNodeText(textNode, result.text);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,69 @@
1
+ import { Properties } from '../../Properties';
2
+ import { ElementRegistrar } from '../../services/ElementRegistrar';
3
+ import { EventService } from '../../services/EventService';
4
+ import { TextService } from '../../services/TextService';
5
+ import { TranslationParams } from '../../types';
6
+ import { NodeHandler } from '../NodeHandler';
7
+ import { AttributeHandler } from './AttributeHandler';
8
+ import { Coder } from './Coder';
9
+ import { CoreHandler } from './CoreHandler';
10
+ import { ContentHandler } from './ContentHandler';
11
+ import { WrappedHandler } from '../WrappedHandler';
12
+ import { AbstractWrapper } from '../AbstractWrapper';
13
+
14
+ export class TextWrapper implements AbstractWrapper {
15
+ private coder: Coder;
16
+ private coreHandler: CoreHandler;
17
+ private attributeHandler: AttributeHandler;
18
+ private wrappedHandler: WrappedHandler;
19
+ private textHandler: ContentHandler;
20
+ private nodeHandler: any;
21
+ constructor(
22
+ eventService: EventService,
23
+ properties: Properties,
24
+ textService: TextService,
25
+ elementRegistrar: ElementRegistrar
26
+ ) {
27
+ this.coder = new Coder(properties, textService);
28
+ this.nodeHandler = new NodeHandler(properties, elementRegistrar, this);
29
+ this.textHandler = new ContentHandler(properties, this.nodeHandler);
30
+ this.attributeHandler = new AttributeHandler(properties, this.nodeHandler);
31
+ this.wrappedHandler = new WrappedHandler(
32
+ elementRegistrar,
33
+ this.nodeHandler
34
+ );
35
+ this.coreHandler = new CoreHandler(
36
+ this.textHandler,
37
+ eventService,
38
+ properties,
39
+ this.attributeHandler,
40
+ this.coder,
41
+ this.wrappedHandler
42
+ );
43
+ }
44
+
45
+ public handleText(node: Element) {
46
+ return this.textHandler.handle(node);
47
+ }
48
+
49
+ public handleSubtree(node: Element) {
50
+ return this.coreHandler.handleSubtree(node);
51
+ }
52
+
53
+ public handleAttribute(node: Element) {
54
+ return this.attributeHandler.handle(node);
55
+ }
56
+
57
+ public wrap(
58
+ key: string,
59
+ params: TranslationParams = {},
60
+ defaultValue: string | undefined = undefined,
61
+ translation: string
62
+ ) {
63
+ return this.coder.wrap(key, params, defaultValue);
64
+ }
65
+
66
+ public unwrap(text: string) {
67
+ return this.coder.unwrap(text);
68
+ }
69
+ }