11.2 KB385 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import type {HighlightedToken} from './textmate-lib/tokenize';
9
10import {
11 createTokenizedIntralineDiff,
12 MAX_INPUT_LENGTH_FOR_INTRALINE_DIFF,
13} from './createTokenizedIntralineDiff';
14
15describe('createTokenizedIntralineDiff', () => {
16 test('empty string', () => {
17 const beforeLine = '';
18 const beforeTokens: HighlightedToken[] = [];
19 const afterLine = '';
20 const afterTokens: HighlightedToken[] = [];
21 const [left, right] = createTokenizedIntralineDiff(
22 beforeLine,
23 beforeTokens,
24 afterLine,
25 afterTokens,
26 );
27 expect(left).toBe(null);
28 expect(right).toBe(null);
29 });
30
31 test('diff on words, not chars', () => {
32 const beforeLine = 'global x';
33 const beforeTokens = [
34 {start: 0, end: 6, color: 2},
35 {start: 6, end: 7, color: 1},
36 {start: 7, end: 8, color: 3},
37 ];
38 const afterLine = 'nonlocal xyz';
39 const afterTokens = [
40 {start: 0, end: 8, color: 2},
41 {start: 8, end: 9, color: 1},
42 {start: 9, end: 12, color: 3},
43 ];
44 const [left, right] = createTokenizedIntralineDiff(
45 beforeLine,
46 beforeTokens,
47 afterLine,
48 afterTokens,
49 );
50 expect(left).toEqual([
51 <span key={0} className="mtk2 patch-remove-word patch-word-begin patch-word-end">
52 global
53 </span>,
54 <span key={6} className="mtk1">
55 {' '}
56 </span>,
57 <span key={7} className="mtk3 patch-remove-word patch-word-begin patch-word-end">
58 x
59 </span>,
60 ]);
61 expect(right).toEqual([
62 <span key={0} className="mtk2 patch-add-word patch-word-begin patch-word-end">
63 nonlocal
64 </span>,
65 <span key={8} className="mtk1">
66 {' '}
67 </span>,
68 <span key={9} className="mtk3 patch-add-word patch-word-begin patch-word-end">
69 xyz
70 </span>,
71 ]);
72 });
73
74 test('renamed variable', () => {
75 const beforeLine = 'func _unhandled_input(event: InputEvent) -> void:';
76 const beforeTokens = [
77 {start: 0, end: 4, color: 4},
78 {start: 4, end: 5, color: 1},
79 {start: 5, end: 21, color: 10},
80 {start: 21, end: 29, color: 1},
81 {start: 29, end: 39, color: 9},
82 {start: 39, end: 44, color: 1},
83 {start: 44, end: 48, color: 9},
84 {start: 48, end: 49, color: 1},
85 ];
86 const afterLine = 'func _unhandled_input(input_event: InputEvent) -> void:';
87 const afterTokens = [
88 {start: 0, end: 4, color: 4},
89 {start: 4, end: 5, color: 1},
90 {start: 5, end: 21, color: 10},
91 {start: 21, end: 35, color: 1},
92 {start: 35, end: 45, color: 9},
93 {start: 45, end: 50, color: 1},
94 {start: 50, end: 54, color: 9},
95 {start: 54, end: 55, color: 1},
96 ];
97 const [left, right] = createTokenizedIntralineDiff(
98 beforeLine,
99 beforeTokens,
100 afterLine,
101 afterTokens,
102 );
103 expect(left).toEqual([
104 <span key={0} className="mtk4">
105 func
106 </span>,
107 <span key={4} className="mtk1">
108 {' '}
109 </span>,
110 <span key={5} className="mtk10">
111 _unhandled_input
112 </span>,
113 <span key={21} className="mtk1">
114 (
115 </span>,
116 <span key={22} className="mtk1 patch-remove-word patch-word-begin patch-word-end">
117 event
118 </span>,
119 <span key={27} className="mtk1">
120 {': '}
121 </span>,
122 <span key={29} className="mtk9">
123 InputEvent
124 </span>,
125 <span key={39} className="mtk1">
126 {') -> '}
127 </span>,
128 <span key={44} className="mtk9">
129 void
130 </span>,
131 <span key={48} className="mtk1">
132 :
133 </span>,
134 ]);
135 expect(right).toEqual([
136 <span key={0} className="mtk4">
137 func
138 </span>,
139 <span key={4} className="mtk1">
140 {' '}
141 </span>,
142 <span key={5} className="mtk10">
143 _unhandled_input
144 </span>,
145 <span key={21} className="mtk1">
146 (
147 </span>,
148 <span key={22} className="mtk1 patch-add-word patch-word-begin patch-word-end">
149 input_event
150 </span>,
151 <span key={33} className="mtk1">
152 {': '}
153 </span>,
154 <span key={35} className="mtk9">
155 InputEvent
156 </span>,
157 <span key={45} className="mtk1">
158 {') -> '}
159 </span>,
160 <span key={50} className="mtk9">
161 void
162 </span>,
163 <span key={54} className="mtk1">
164 :
165 </span>,
166 ]);
167 });
168
169 test('diffing unrelated lines', () => {
170 const beforeLine = '# - event: Triggered event';
171 const beforeTokens = [{start: 0, end: 26, color: 3}];
172 const afterLine = 'func _unhandled_input(input_event: InputEvent) -> void:';
173 const afterTokens = [
174 {start: 0, end: 4, color: 4},
175 {start: 4, end: 5, color: 1},
176 {start: 5, end: 21, color: 10},
177 {start: 21, end: 35, color: 1},
178 {start: 35, end: 45, color: 9},
179 {start: 45, end: 50, color: 1},
180 {start: 50, end: 54, color: 9},
181 {start: 54, end: 55, color: 1},
182 ];
183 const [left, right] = createTokenizedIntralineDiff(
184 beforeLine,
185 beforeTokens,
186 afterLine,
187 afterTokens,
188 );
189 expect(left).toEqual([
190 <span key={0} className="mtk3 patch-remove-word patch-word-begin patch-word-end">
191 {'#'}
192 </span>,
193 <span key={1} className="mtk3">
194 {' '}
195 </span>,
196 <span key={2} className="mtk3 patch-remove-word patch-word-begin patch-word-end">
197 {'- event'}
198 </span>,
199 <span key={9} className="mtk3">
200 {': '}
201 </span>,
202 <span key={11} className="mtk3 patch-remove-word patch-word-begin patch-word-end">
203 {'Triggered'}
204 </span>,
205 <span key={20} className="mtk3">
206 {' '}
207 </span>,
208 <span key={21} className="mtk3 patch-remove-word patch-word-begin patch-word-end">
209 {'event'}
210 </span>,
211 ]);
212 expect(right).toEqual([
213 <span key={0} className="mtk4 patch-add-word patch-word-begin patch-word-end">
214 func
215 </span>,
216 <span key={4} className="mtk1">
217 {' '}
218 </span>,
219 <span key={5} className="mtk10 patch-add-word patch-word-begin">
220 _unhandled_input
221 </span>,
222 <span key={21} className="mtk1 patch-add-word patch-word-end">
223 {'(input_event'}
224 </span>,
225 <span key={33} className="mtk1">
226 {': '}
227 </span>,
228 <span key={35} className="mtk9 patch-add-word patch-word-begin">
229 InputEvent
230 </span>,
231 <span key={45} className="mtk1 patch-add-word patch-word-end">
232 {')'}
233 </span>,
234 <span key={46} className="mtk1">
235 {' '}
236 </span>,
237 <span key={47} className="mtk1 patch-add-word patch-word-begin">
238 {'-> '}
239 </span>,
240 <span key={50} className="mtk9 patch-add-word">
241 void
242 </span>,
243 <span key={54} className="mtk1 patch-add-word patch-word-end">
244 :
245 </span>,
246 ]);
247 });
248
249 test('first patch-remove-work chunk has multiple tokens', () => {
250 const beforeLine = ' return true;';
251 const beforeTokens = [
252 {start: 0, end: 4, color: 1},
253 {start: 4, end: 10, color: 4},
254 {start: 10, end: 11, color: 1},
255 {start: 11, end: 15, color: 9},
256 {start: 15, end: 16, color: 1},
257 ];
258 const afterLine = '';
259 const afterTokens = [{start: 0, end: 0, color: 1}];
260 const [left, right] = createTokenizedIntralineDiff(
261 beforeLine,
262 beforeTokens,
263 afterLine,
264 afterTokens,
265 );
266 expect(left).toEqual([
267 <span key={0} className="mtk1 patch-remove-word patch-word-begin">
268 {' '}
269 </span>,
270 <span key={4} className="mtk4 patch-remove-word">
271 return
272 </span>,
273 <span key={10} className="mtk1 patch-remove-word">
274 {' '}
275 </span>,
276 <span key={11} className="mtk9 patch-remove-word">
277 true
278 </span>,
279 <span key={15} className="mtk1 patch-remove-word patch-word-end">
280 ;
281 </span>,
282 ]);
283 expect(right).toEqual([]);
284 });
285
286 test('creating an intraline diff for a line that is too long should bail out', () => {
287 // We repeat a piece of text with a space because createTokenizedIntralineDiff()
288 // uses diffWords() under the hood.
289 const beforeLine = 'reviewstack '.repeat(12);
290 const beforeTokens = [{start: 0, end: beforeLine.length, color: 1}];
291 const afterLineAtThreshold = 'reviewstack '.repeat(13);
292 expect(beforeLine.length + afterLineAtThreshold.length).toBe(
293 MAX_INPUT_LENGTH_FOR_INTRALINE_DIFF,
294 );
295 const afterTokensAtThreshold = [{start: 0, end: afterLineAtThreshold.length, color: 1}];
296 const [leftAtThreshold, rightAtThreshold] = createTokenizedIntralineDiff(
297 beforeLine,
298 beforeTokens,
299 afterLineAtThreshold,
300 afterTokensAtThreshold,
301 );
302 expect(leftAtThreshold).toEqual([
303 <span key={0} className="mtk1">
304 {beforeLine}
305 </span>,
306 ]);
307 expect(rightAtThreshold).toEqual([
308 <span key={0} className="mtk1">
309 {beforeLine}
310 </span>,
311 <span key={beforeLine.length} className="mtk1 patch-add-word patch-word-begin patch-word-end">
312 {'reviewstack '}
313 </span>,
314 ]);
315
316 // Verify that once the input exceeds the threshold, we no longer see CSS
317 // classes like patch-word-begin or patch-word-end in the output, indicating
318 // that the inline diff was not computed.
319 const afterLineOneLonger = afterLineAtThreshold + 'X';
320 expect(beforeLine.length + afterLineOneLonger.length).toBeGreaterThan(
321 MAX_INPUT_LENGTH_FOR_INTRALINE_DIFF,
322 );
323 const afterTokensOneLonger = [{start: 0, end: afterLineOneLonger.length, color: 1}];
324 const [leftOneLonger, rightOneLonger] = createTokenizedIntralineDiff(
325 beforeLine,
326 beforeTokens,
327 afterLineOneLonger,
328 afterTokensOneLonger,
329 );
330 expect(leftOneLonger).toEqual([
331 <span key={0} className="mtk1">
332 {beforeLine}
333 </span>,
334 ]);
335 expect(rightOneLonger).toEqual([
336 <span key={0} className="mtk1">
337 {afterLineOneLonger}
338 </span>,
339 ]);
340 });
341
342 test('diffWordsWithSpace() must be used instead of diffWords() or content will be lost', () => {
343 const beforeLine = ' <SplitDiffRow';
344 const afterLine = ' path,';
345 const beforeTokens = [
346 {start: 0, end: 8, color: 1},
347 {start: 8, end: 9, color: 13},
348 {start: 9, end: 21, color: 9},
349 ];
350 const afterTokens = [
351 {start: 0, end: 6, color: 1},
352 {start: 6, end: 10, color: 14},
353 {start: 10, end: 11, color: 1},
354 ];
355 const [left, right] = createTokenizedIntralineDiff(
356 beforeLine,
357 beforeTokens,
358 afterLine,
359 afterTokens,
360 );
361 expect(left).toEqual([
362 <span key={0} className="mtk1 patch-remove-word patch-word-begin">
363 {' '}
364 </span>,
365 <span key={8} className="mtk13 patch-remove-word">
366 {'<'}
367 </span>,
368 <span key={9} className="mtk9 patch-remove-word patch-word-end">
369 {'SplitDiffRow'}
370 </span>,
371 ]);
372 expect(right).toEqual([
373 <span key={0} className="mtk1 patch-add-word patch-word-begin">
374 {' '}
375 </span>,
376 <span key={6} className="mtk14 patch-add-word">
377 path
378 </span>,
379 <span key={10} className="mtk1 patch-add-word patch-word-end">
380 {','}
381 </span>,
382 ]);
383 });
384});
385