@pie-lib/text-select 2.1.1-next.0 → 2.2.0-next.0
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.
- package/lib/index.js +0 -7
- package/lib/index.js.map +1 -1
- package/lib/legend.js +0 -12
- package/lib/legend.js.map +1 -1
- package/lib/text-select.js +0 -14
- package/lib/text-select.js.map +1 -1
- package/lib/token-select/index.js +3 -24
- package/lib/token-select/index.js.map +1 -1
- package/lib/token-select/token.js +0 -17
- package/lib/token-select/token.js.map +1 -1
- package/lib/tokenizer/builder.js +3 -14
- package/lib/tokenizer/builder.js.map +1 -1
- package/lib/tokenizer/controls.js +0 -2
- package/lib/tokenizer/controls.js.map +1 -1
- package/lib/tokenizer/index.js +4 -19
- package/lib/tokenizer/index.js.map +1 -1
- package/lib/tokenizer/selection-utils.js +0 -4
- package/lib/tokenizer/selection-utils.js.map +1 -1
- package/lib/tokenizer/token-text.js +0 -11
- package/lib/tokenizer/token-text.js.map +1 -1
- package/lib/utils.js +0 -9
- package/lib/utils.js.map +1 -1
- package/package.json +11 -8
- package/src/__tests__/legend.test.jsx +211 -0
- package/src/token-select/__tests__/index.test.jsx +264 -2
- package/src/token-select/__tests__/token.test.jsx +207 -1
- package/src/token-select/index.jsx +1 -2
- package/src/token-select/token.jsx +1 -5
- package/src/tokenizer/__tests__/builder.test.js +1 -1
- package/src/tokenizer/__tests__/controls.test.jsx +1 -1
- package/src/tokenizer/__tests__/index.test.jsx +289 -7
- package/src/tokenizer/__tests__/selection-utils.test.js +122 -3
- package/src/tokenizer/__tests__/token-text.test.jsx +285 -9
- package/src/tokenizer/builder.js +2 -3
- package/src/tokenizer/controls.jsx +3 -18
- package/src/tokenizer/index.jsx +2 -4
- package/src/tokenizer/token-text.jsx +2 -2
- package/NEXT.CHANGELOG.json +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Tokenizer } from '../index';
|
|
2
1
|
import React from 'react';
|
|
3
|
-
import { render } from '@testing-library/react';
|
|
2
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
3
|
+
import { Tokenizer } from '../index';
|
|
4
4
|
|
|
5
5
|
const tokens = () => [
|
|
6
6
|
{
|
|
@@ -18,6 +18,10 @@ describe('tokenizer', () => {
|
|
|
18
18
|
tokens: tokens(),
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
jest.clearAllMocks();
|
|
23
|
+
});
|
|
24
|
+
|
|
21
25
|
describe('rendering', () => {
|
|
22
26
|
it('renders with default props', () => {
|
|
23
27
|
const { container } = render(<Tokenizer {...defaultProps} />);
|
|
@@ -37,11 +41,289 @@ describe('tokenizer', () => {
|
|
|
37
41
|
const { container } = render(<Tokenizer {...defaultProps} tokens={multipleTokens} />);
|
|
38
42
|
expect(container.firstChild).toBeInTheDocument();
|
|
39
43
|
});
|
|
44
|
+
|
|
45
|
+
it('renders Controls component', () => {
|
|
46
|
+
const { getByText } = render(<Tokenizer {...defaultProps} />);
|
|
47
|
+
expect(getByText(/clear/i)).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('renders TokenText with text content', () => {
|
|
51
|
+
const { container } = render(<Tokenizer {...defaultProps} text="Hello world" />);
|
|
52
|
+
expect(container.textContent).toContain('Hello world');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('control interactions', () => {
|
|
57
|
+
it('calls onChange when clear button is clicked', () => {
|
|
58
|
+
const onChange = jest.fn();
|
|
59
|
+
const { getByText } = render(<Tokenizer {...defaultProps} onChange={onChange} />);
|
|
60
|
+
|
|
61
|
+
const clearButton = getByText(/clear/i);
|
|
62
|
+
fireEvent.click(clearButton);
|
|
63
|
+
|
|
64
|
+
expect(onChange).toHaveBeenCalledWith([], '');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('calls onChange when words button is clicked', () => {
|
|
68
|
+
const onChange = jest.fn();
|
|
69
|
+
const { getByText } = render(<Tokenizer {...defaultProps} text="hello world" onChange={onChange} />);
|
|
70
|
+
|
|
71
|
+
const wordsButton = getByText(/words/i);
|
|
72
|
+
fireEvent.click(wordsButton);
|
|
73
|
+
|
|
74
|
+
expect(onChange).toHaveBeenCalled();
|
|
75
|
+
expect(onChange.mock.calls[0][1]).toBe('words');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('calls onChange when sentences button is clicked', () => {
|
|
79
|
+
const onChange = jest.fn();
|
|
80
|
+
const { getByText } = render(<Tokenizer {...defaultProps} text="Hello. World." onChange={onChange} />);
|
|
81
|
+
|
|
82
|
+
const sentencesButton = getByText(/sentences/i);
|
|
83
|
+
fireEvent.click(sentencesButton);
|
|
84
|
+
|
|
85
|
+
expect(onChange).toHaveBeenCalled();
|
|
86
|
+
expect(onChange.mock.calls[0][1]).toBe('sentence');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('calls onChange when paragraphs button is clicked', () => {
|
|
90
|
+
const onChange = jest.fn();
|
|
91
|
+
const { getByText } = render(<Tokenizer {...defaultProps} text="Para 1\n\nPara 2" onChange={onChange} />);
|
|
92
|
+
|
|
93
|
+
const paragraphsButton = getByText(/paragraphs/i);
|
|
94
|
+
fireEvent.click(paragraphsButton);
|
|
95
|
+
|
|
96
|
+
expect(onChange).toHaveBeenCalled();
|
|
97
|
+
expect(onChange.mock.calls[0][1]).toBe('paragraphs');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('correct mode toggle', () => {
|
|
102
|
+
it('toggles correct mode when toggle button is clicked', () => {
|
|
103
|
+
const { container } = render(<Tokenizer {...defaultProps} />);
|
|
104
|
+
const toggleButton = container.querySelector('[type="checkbox"]');
|
|
105
|
+
|
|
106
|
+
expect(toggleButton).not.toBeChecked();
|
|
107
|
+
|
|
108
|
+
fireEvent.click(toggleButton);
|
|
109
|
+
expect(toggleButton).toBeChecked();
|
|
110
|
+
|
|
111
|
+
fireEvent.click(toggleButton);
|
|
112
|
+
expect(toggleButton).not.toBeChecked();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('token handling', () => {
|
|
117
|
+
it('renders with tokens containing correct property', () => {
|
|
118
|
+
const tokensWithCorrect = [
|
|
119
|
+
{ start: 0, end: 3, text: 'foo', correct: true },
|
|
120
|
+
{ start: 4, end: 7, text: 'bar', correct: false },
|
|
121
|
+
];
|
|
122
|
+
const { container } = render(<Tokenizer {...defaultProps} tokens={tokensWithCorrect} />);
|
|
123
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('handles tokens with overlapping positions', () => {
|
|
127
|
+
const overlappingTokens = [
|
|
128
|
+
{ start: 0, end: 5, text: 'hello' },
|
|
129
|
+
{ start: 3, end: 8, text: 'low' },
|
|
130
|
+
];
|
|
131
|
+
const { container } = render(<Tokenizer {...defaultProps} tokens={overlappingTokens} />);
|
|
132
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
133
|
+
});
|
|
40
134
|
});
|
|
41
135
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
136
|
+
describe('text variations', () => {
|
|
137
|
+
it('handles multiline text', () => {
|
|
138
|
+
const multilineText = 'Line 1\nLine 2\nLine 3';
|
|
139
|
+
const { container } = render(<Tokenizer {...defaultProps} text={multilineText} />);
|
|
140
|
+
expect(container.textContent).toContain('Line 1');
|
|
141
|
+
expect(container.textContent).toContain('Line 2');
|
|
142
|
+
expect(container.textContent).toContain('Line 3');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('handles empty text', () => {
|
|
146
|
+
const { container } = render(<Tokenizer {...defaultProps} text="" tokens={[]} />);
|
|
147
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('handles text with special characters', () => {
|
|
151
|
+
const specialText = 'Hello! @#$ %^& *() world?';
|
|
152
|
+
const { container } = render(<Tokenizer {...defaultProps} text={specialText} />);
|
|
153
|
+
expect(container.textContent).toContain(specialText);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('token click interactions', () => {
|
|
158
|
+
it('removes token when clicked in normal mode', () => {
|
|
159
|
+
const onChange = jest.fn();
|
|
160
|
+
const tokensWithPredefined = [{ start: 0, end: 5, text: 'hello', predefined: true }];
|
|
161
|
+
const { container } = render(
|
|
162
|
+
<Tokenizer {...defaultProps} text="hello world" tokens={tokensWithPredefined} onChange={onChange} />,
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const predefinedToken = container.querySelector('.predefined');
|
|
166
|
+
if (predefinedToken) {
|
|
167
|
+
fireEvent.click(predefinedToken);
|
|
168
|
+
expect(onChange).toHaveBeenCalled();
|
|
169
|
+
expect(onChange.mock.calls[0][0]).toEqual([]);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('toggles correct property when token clicked in correct mode', () => {
|
|
174
|
+
const onChange = jest.fn();
|
|
175
|
+
const tokensWithPredefined = [{ start: 0, end: 5, text: 'hello', predefined: true, correct: false }];
|
|
176
|
+
const { container } = render(
|
|
177
|
+
<Tokenizer {...defaultProps} text="hello world" tokens={tokensWithPredefined} onChange={onChange} />,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const toggleButton = container.querySelector('[type="checkbox"]');
|
|
181
|
+
fireEvent.click(toggleButton);
|
|
182
|
+
onChange.mockClear();
|
|
183
|
+
|
|
184
|
+
const predefinedToken = container.querySelector('.predefined');
|
|
185
|
+
if (predefinedToken) {
|
|
186
|
+
fireEvent.click(predefinedToken);
|
|
187
|
+
expect(onChange).toHaveBeenCalled();
|
|
188
|
+
const updatedToken = onChange.mock.calls[0][0][0];
|
|
189
|
+
expect(updatedToken.correct).toBe(true);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('toggles correct from true to false when clicked in correct mode', () => {
|
|
194
|
+
const onChange = jest.fn();
|
|
195
|
+
const tokensWithPredefined = [{ start: 0, end: 5, text: 'hello', predefined: true, correct: true }];
|
|
196
|
+
const { container } = render(
|
|
197
|
+
<Tokenizer {...defaultProps} text="hello world" tokens={tokensWithPredefined} onChange={onChange} />,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const toggleButton = container.querySelector('[type="checkbox"]');
|
|
201
|
+
fireEvent.click(toggleButton);
|
|
202
|
+
onChange.mockClear();
|
|
203
|
+
|
|
204
|
+
const predefinedToken = container.querySelector('.predefined');
|
|
205
|
+
if (predefinedToken) {
|
|
206
|
+
fireEvent.click(predefinedToken);
|
|
207
|
+
expect(onChange).toHaveBeenCalled();
|
|
208
|
+
const updatedToken = onChange.mock.calls[0][0][0];
|
|
209
|
+
expect(updatedToken.correct).toBe(false);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('does not modify tokens when clicking non-existent token', () => {
|
|
214
|
+
const onChange = jest.fn();
|
|
215
|
+
const { container } = render(<Tokenizer {...defaultProps} text="hello world" tokens={[]} onChange={onChange} />);
|
|
216
|
+
|
|
217
|
+
fireEvent.click(container.querySelector('div > div'));
|
|
218
|
+
|
|
219
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('token removal', () => {
|
|
224
|
+
it('removes multiple different tokens', () => {
|
|
225
|
+
const onChange = jest.fn();
|
|
226
|
+
const multipleTokens = [
|
|
227
|
+
{ start: 0, end: 5, text: 'hello', predefined: true },
|
|
228
|
+
{ start: 6, end: 11, text: 'world', predefined: true },
|
|
229
|
+
];
|
|
230
|
+
const { container } = render(
|
|
231
|
+
<Tokenizer {...defaultProps} text="hello world" tokens={multipleTokens} onChange={onChange} />,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const predefinedTokens = container.querySelectorAll('.predefined');
|
|
235
|
+
expect(predefinedTokens.length).toBeGreaterThan(0);
|
|
236
|
+
|
|
237
|
+
fireEvent.click(predefinedTokens[0]);
|
|
238
|
+
expect(onChange).toHaveBeenCalled();
|
|
239
|
+
expect(onChange.mock.calls[0][0].length).toBe(1); // One token left
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('handles removing token by matching text, start, and end', () => {
|
|
243
|
+
const onChange = jest.fn();
|
|
244
|
+
const tokensWithSameText = [
|
|
245
|
+
{ start: 0, end: 5, text: 'hello', predefined: true },
|
|
246
|
+
{ start: 12, end: 17, text: 'hello', predefined: true },
|
|
247
|
+
];
|
|
248
|
+
const { container } = render(
|
|
249
|
+
<Tokenizer {...defaultProps} text="hello world hello" tokens={tokensWithSameText} onChange={onChange} />,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const predefinedTokens = container.querySelectorAll('.predefined');
|
|
253
|
+
if (predefinedTokens.length > 0) {
|
|
254
|
+
fireEvent.click(predefinedTokens[0]);
|
|
255
|
+
expect(onChange).toHaveBeenCalled();
|
|
256
|
+
expect(onChange.mock.calls[0][0].length).toBe(1);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('text selection and token creation', () => {
|
|
262
|
+
beforeEach(() => {
|
|
263
|
+
if (typeof global.window !== 'undefined') {
|
|
264
|
+
global.window.getSelection = jest.fn().mockReturnValue({
|
|
265
|
+
toString: () => '',
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('creates new token when text is selected', () => {
|
|
271
|
+
const onChange = jest.fn();
|
|
272
|
+
const { container } = render(<Tokenizer {...defaultProps} text="hello world" tokens={[]} onChange={onChange} />);
|
|
273
|
+
|
|
274
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('selectToken removes overlapping tokens when creating new token', () => {
|
|
278
|
+
const onChange = jest.fn();
|
|
279
|
+
const existingTokens = [{ start: 0, end: 5, text: 'hello' }];
|
|
280
|
+
const { container } = render(
|
|
281
|
+
<Tokenizer {...defaultProps} text="hello world" tokens={existingTokens} onChange={onChange} />,
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe('mode state management', () => {
|
|
289
|
+
it('maintains mode state after buildTokens', () => {
|
|
290
|
+
const onChange = jest.fn();
|
|
291
|
+
const { getByText } = render(<Tokenizer {...defaultProps} text="hello world test" onChange={onChange} />);
|
|
292
|
+
|
|
293
|
+
const wordsButton = getByText(/words/i);
|
|
294
|
+
fireEvent.click(wordsButton);
|
|
295
|
+
|
|
296
|
+
expect(onChange).toHaveBeenCalled();
|
|
297
|
+
expect(onChange.mock.calls[0][1]).toBe('words');
|
|
298
|
+
|
|
299
|
+
onChange.mockClear();
|
|
300
|
+
fireEvent.click(wordsButton);
|
|
301
|
+
expect(onChange).toHaveBeenCalled();
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('maintains mode state after clear', () => {
|
|
305
|
+
const onChange = jest.fn();
|
|
306
|
+
const { getByText } = render(<Tokenizer {...defaultProps} text="hello world" onChange={onChange} />);
|
|
307
|
+
|
|
308
|
+
fireEvent.click(getByText(/words/i));
|
|
309
|
+
onChange.mockClear();
|
|
310
|
+
|
|
311
|
+
fireEvent.click(getByText(/clear/i));
|
|
312
|
+
expect(onChange).toHaveBeenCalledWith([], '');
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe('disabled state styling', () => {
|
|
317
|
+
it('applies disabled styling when in correct mode', () => {
|
|
318
|
+
const { container } = render(<Tokenizer {...defaultProps} />);
|
|
319
|
+
|
|
320
|
+
const styledDiv = container.querySelector('div > div');
|
|
321
|
+
expect(styledDiv).toBeInTheDocument();
|
|
322
|
+
|
|
323
|
+
const toggleButton = container.querySelector('[type="checkbox"]');
|
|
324
|
+
fireEvent.click(toggleButton);
|
|
325
|
+
|
|
326
|
+
expect(styledDiv).toBeInTheDocument();
|
|
327
|
+
});
|
|
328
|
+
});
|
|
47
329
|
});
|
|
@@ -3,6 +3,7 @@ import { clearSelection, getCaretCharacterOffsetWithin } from '../selection-util
|
|
|
3
3
|
describe('selection-utils', () => {
|
|
4
4
|
let selection;
|
|
5
5
|
let range;
|
|
6
|
+
|
|
6
7
|
beforeEach(() => {
|
|
7
8
|
selection = {
|
|
8
9
|
removeAllRanges: jest.fn(),
|
|
@@ -14,13 +15,131 @@ describe('selection-utils', () => {
|
|
|
14
15
|
});
|
|
15
16
|
|
|
16
17
|
describe('clearSelection', () => {
|
|
17
|
-
it('calls removeAllRanges', () => {
|
|
18
|
+
it('calls removeAllRanges and addRange with document.getSelection', () => {
|
|
19
|
+
const mockRange = {};
|
|
20
|
+
global.document.createRange = jest.fn().mockReturnValue(mockRange);
|
|
21
|
+
|
|
18
22
|
clearSelection();
|
|
23
|
+
|
|
19
24
|
expect(selection.removeAllRanges).toBeCalled();
|
|
25
|
+
expect(selection.addRange).toHaveBeenCalledWith(mockRange);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('uses window.getSelection if document.getSelection is not available', () => {
|
|
29
|
+
global.document.getSelection = undefined;
|
|
30
|
+
const windowSelection = {
|
|
31
|
+
removeAllRanges: jest.fn(),
|
|
32
|
+
addRange: jest.fn(),
|
|
33
|
+
};
|
|
34
|
+
global.window.getSelection = jest.fn().mockReturnValue(windowSelection);
|
|
35
|
+
const mockRange = {};
|
|
36
|
+
global.document.createRange = jest.fn().mockReturnValue(mockRange);
|
|
37
|
+
|
|
38
|
+
clearSelection();
|
|
39
|
+
|
|
40
|
+
expect(windowSelection.removeAllRanges).toBeCalled();
|
|
41
|
+
expect(windowSelection.addRange).toHaveBeenCalledWith(mockRange);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('uses window.getSelection().empty() if removeAllRanges is not available', () => {
|
|
45
|
+
global.document.getSelection = undefined;
|
|
46
|
+
const windowSelection = {
|
|
47
|
+
empty: jest.fn(),
|
|
48
|
+
};
|
|
49
|
+
global.window.getSelection = jest.fn().mockReturnValue(windowSelection);
|
|
50
|
+
|
|
51
|
+
clearSelection();
|
|
52
|
+
|
|
53
|
+
expect(windowSelection.empty).toBeCalled();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('uses document.selection.empty() for IE8- compatibility', () => {
|
|
57
|
+
global.document.getSelection = undefined;
|
|
58
|
+
global.window.getSelection = undefined;
|
|
59
|
+
const docSelection = {
|
|
60
|
+
empty: jest.fn(),
|
|
61
|
+
};
|
|
62
|
+
global.document.selection = docSelection;
|
|
63
|
+
|
|
64
|
+
clearSelection();
|
|
65
|
+
|
|
66
|
+
expect(docSelection.empty).toBeCalled();
|
|
67
|
+
|
|
68
|
+
delete global.document.selection;
|
|
20
69
|
});
|
|
21
70
|
});
|
|
22
71
|
|
|
23
|
-
|
|
24
|
-
|
|
72
|
+
describe('getCaretCharacterOffsetWithin', () => {
|
|
73
|
+
let element;
|
|
74
|
+
let mockRange;
|
|
75
|
+
let mockSelection;
|
|
76
|
+
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
element = {
|
|
79
|
+
ownerDocument: global.document,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
mockRange = {
|
|
83
|
+
toString: jest.fn().mockReturnValue(''),
|
|
84
|
+
cloneRange: jest.fn(),
|
|
85
|
+
endContainer: {},
|
|
86
|
+
endOffset: 0,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const clonedRange = {
|
|
90
|
+
selectNodeContents: jest.fn(),
|
|
91
|
+
setEnd: jest.fn(),
|
|
92
|
+
toString: jest.fn().mockReturnValue('some text'),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
mockRange.cloneRange.mockReturnValue(clonedRange);
|
|
96
|
+
|
|
97
|
+
mockSelection = {
|
|
98
|
+
rangeCount: 1,
|
|
99
|
+
getRangeAt: jest.fn().mockReturnValue(mockRange),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
global.window.getSelection = jest.fn().mockReturnValue(mockSelection);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('returns 0 when rangeCount is 0', () => {
|
|
106
|
+
mockSelection.rangeCount = 0;
|
|
107
|
+
|
|
108
|
+
const offset = getCaretCharacterOffsetWithin(element);
|
|
109
|
+
|
|
110
|
+
expect(offset).toBe(0);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('returns caret offset for unselected text', () => {
|
|
114
|
+
mockRange.toString.mockReturnValue('');
|
|
115
|
+
const clonedRange = mockRange.cloneRange();
|
|
116
|
+
clonedRange.toString.mockReturnValue('hello');
|
|
117
|
+
|
|
118
|
+
const offset = getCaretCharacterOffsetWithin(element);
|
|
119
|
+
|
|
120
|
+
expect(offset).toBe(5);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('returns caret offset when text is selected', () => {
|
|
124
|
+
mockRange.toString.mockReturnValue('world');
|
|
125
|
+
const clonedRange = mockRange.cloneRange();
|
|
126
|
+
clonedRange.toString.mockReturnValue('hello world');
|
|
127
|
+
|
|
128
|
+
const offset = getCaretCharacterOffsetWithin(element);
|
|
129
|
+
|
|
130
|
+
expect(offset).toBe(6); // 11 - 5 = 6
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('returns 0 when selection type is Control in IE', () => {
|
|
134
|
+
global.window.getSelection = undefined;
|
|
135
|
+
const docSelection = {
|
|
136
|
+
type: 'Control',
|
|
137
|
+
};
|
|
138
|
+
element.ownerDocument.selection = docSelection;
|
|
139
|
+
|
|
140
|
+
const offset = getCaretCharacterOffsetWithin(element);
|
|
141
|
+
|
|
142
|
+
expect(offset).toBe(0);
|
|
143
|
+
});
|
|
25
144
|
});
|
|
26
145
|
});
|