Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(251)

Side by Side Diff: third_party/WebKit/LayoutTests/editing/spelling/spellcheck_test.js

Issue 2450733002: Make spellcheck_test easier to use (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 'use strict'; 5 'use strict';
6 6
7 // This file provides 7 // This file provides
8 // |spellcheck_test(sample, tester, expectedMarkers, opt_title)| asynchronous 8 // |spellcheck_test(sample, tester, expectedMarkers, opt_title)| asynchronous
9 // test to W3C test harness for easier writing of editing test cases. 9 // test to W3C test harness for easier writing of editing test cases.
10 // 10 //
(...skipping 25 matching lines...) Expand all
36 // 36 //
37 // spellcheck_test( 37 // spellcheck_test(
38 // '<div contentEditable>|</div>', 38 // '<div contentEditable>|</div>',
39 // 'insertText You has the right.', 39 // 'insertText You has the right.',
40 // grammarMarker(4, 3), // 'has' 40 // grammarMarker(4, 3), // 'has'
41 // 'Mark ungrammatical phrases after typing.'); 41 // 'Mark ungrammatical phrases after typing.');
42 42
43 (function() { 43 (function() {
44 const Sample = window.Sample; 44 const Sample = window.Sample;
45 45
46 // TODO(editing-dev): Once we can import JavaScript file from scripts, we should
47 // import "imported/wpt/html/resources/common.js", since |HTML5_VOID_ELEMENTS|
48 // is defined in there.
49 /**
50 * @const @type {!Set<string>}
51 * only void (without end tag) HTML5 elements
52 */
53 const HTML5_VOID_ELEMENTS = new Set([
54 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
55 'keygen', 'link', 'meta', 'param', 'source','track', 'wbr' ]);
56
57 // TODO(editing-dev): Reduce code duplication with assert_selection's Serializer
58 // once we can import and export Javascript modules.
59
60 /**
61 * @param {!Node} node
62 * @return {boolean}
63 */
64 function isCharacterData(node) {
65 return node.nodeType === Node.TEXT_NODE ||
66 node.nodeType === Node.COMMENT_NODE;
67 }
68
69 /**
70 * @param {!Node} node
71 * @return {boolean}
72 */
73 function isElement(node) {
74 return node.nodeType === Node.ELEMENT_NODE;
75 }
76
77 /**
78 * @param {!Node} node
79 * @return {boolean}
80 */
81 function isHTMLInputElement(node) {
82 if (!isElement(node))
yosin_UTC9 2016/10/25 09:41:11 |node.nodeName === 'INPUT'| is enough.
Xiaocheng 2016/10/25 13:19:17 Done.
83 return false;
84 const window = node.ownerDocument.defaultView;
85 return node instanceof window.HTMLInputElement;
86 }
87
88 /**
89 * @param {!Node} node
90 * @return {boolean}
91 */
92 function isHTMLTextAreaElement(node) {
93 if (!isElement(node))
yosin_UTC9 2016/10/25 09:41:11 |node.nodeName === 'TEXTAREA'| is enough.
Xiaocheng 2016/10/25 13:19:17 Done.
94 return false;
95 const window = node.ownerDocument.defaultView;
96 return node instanceof window.HTMLTextAreaElement;
97 }
98
99 /**
100 * @param {?Range} range
101 * @param {!Node} node
102 * @param {number} offset
103 */
104 function isAtRangeEnd(range, node, offset) {
105 return range && node === range.endContainer && offset === range.endOffset;
106 }
107
108 class MarkerSerializer {
109 /**
110 * @public
111 * @param {!Array<string>} markerTypes
112 * @param {string} markerMasks
113 */
114 constructor(markerTypes, markerMasks) {
115 assert_equals(markerMasks.length + 1, 1 << markerTypes.length,
116 'Maker types and masks do not match each other.');
117 /** @type {!Array<string>} */
118 this.strings_ = [];
119 /** @type {!Array<string>} */
120 this.markerTypes_ = markerTypes;
121 /** @type {string} */
122 this.markerMasks_ = markerMasks;
123 /** @type {!Object} */
124 this.activeMarkerRanges_ = {};
125 markerTypes.forEach(type => this.activeMarkerRanges_[type] = null);
126 }
127
128 /**
129 * @private
130 * @return {number}
131 */
132 get activeMarkerTypes() {
yosin_UTC9 2016/10/25 09:41:11 This should be a method instead of a getter, since
Xiaocheng 2016/10/25 13:19:17 Done.
133 return this.markerTypes_.reduce(
134 (result, type, index) =>
135 this.activeMarkerRanges_[type] ? (result | (1 << index)) : result,
136 0);
137 }
138
139 /**
140 * @private
141 * @param {string} string
142 */
143 emit(string) { this.strings_.push(string); }
144
145 /**
146 * @private
147 * @param {!Node} node
148 * @param {number} offset
149 */
150 advancedTo(node, offset) {
151 this.markerTypes_.forEach(type => {
152 if (isAtRangeEnd(this.activeMarkerRanges_[type], node, offset))
153 this.activeMarkerRanges_[type] = null;
154 if (this.activeMarkerRanges_[type])
155 return;
156 const markerCount = window.internals.markerCountForNode(node, type);
157 for (let i = 0; i < markerCount; ++i) {
158 const marker = window.internals.markerRangeForNode(node, type, i);
159 assert_equals(marker.startContainer, node,
yosin_UTC9 2016/10/25 09:41:10 We don't need to verity marker. It should work as
Xiaocheng 2016/10/25 13:19:17 I prefer keeping them in case anything goes wrong.
160 'Marker range does not start in the annotated node.');
161 assert_equals(marker.endContainer, node,
162 'Marker range does not end in the annotated node.');
163 if (marker.startOffset == offset) {
yosin_UTC9 2016/10/25 09:41:11 nit: s/==/===/
Xiaocheng 2016/10/25 13:19:16 Done.
164 assert_greater_than(marker.endOffset, offset,
165 'Marker range is collapsed.');
166 this.activeMarkerRanges_[type] = marker;
167 break;
168 }
169 }
170 });
171 }
172
173 /**
174 * @private
175 * @param {!CharacterData} node
176 */
177 handleCharacterData(node) {
178 /** @type {string} */
179 const text = node.nodeValue;
180 /** @type {number} */
181 const length = text.length;
182 for (let offset = 0; offset < length; ++offset) {
183 this.advancedTo(node, offset);
184 /** @type {number} */
185 const activeTypes = this.activeMarkerTypes;
186 /** @type {string} */
187 const charToEmit = activeTypes ? this.markerMasks_[activeTypes - 1] : text [offset];
188 this.emit(charToEmit);
189 }
190 this.advancedTo(node, length);
191 }
192
193 /**
194 * @private
195 * @param {!HTMLElement} element
196 */
197 handleInnerEditorOf(element) {
198 /** @type {!ShadowRoot} */
199 const shadowRoot = window.internals.shadowRoot(element);
200 /** @type {!HTMLDivElement} */
201 const innerEditor = shadowRoot.firstChild;
202 assert_equals(innerEditor.tagName, 'DIV');
203 innerEditor.childNodes.forEach((child, index) => {
204 assert_true(isCharacterData(child));
205 this.advancedTo(innerEditor, index);
206 this.handleCharacterData(child);
207 });
208 this.advancedTo(innerEditor, innerEditor.childNodes.length);
209 }
210
211 /**
212 * @private
213 * @param {!HTMLTextAreaElement} element
214 */
215 handleTextAreaNode(element) {
216 this.handleInnerEditorOf(element);
217 }
218
219 /**
220 * @private
221 * @param {!HTMLInputElement} element
222 */
223 handleInputNode(element) {
224 this.emit(' value="');
225 this.handleInnerEditorOf(element);
226 this.emit('"');
227 }
228
229 /**
230 * @private
231 * @param {!HTMLElement} element
232 */
233 handleElementNode(element) {
234 /** @type {string} */
235 const tagName = element.tagName.toLowerCase();
236 this.emit(`<${tagName}`);
237 Array.from(element.attributes)
238 .sort((attr1, attr2) => attr1.name.localeCompare(attr2.name))
239 .forEach(attr => {
240 if (attr.value === '')
241 return this.emit(` ${attr.name}`);
242 const value = attr.value.replace(/&/g, '&amp;')
243 .replace(/\u0022/g, '&quot;')
244 .replace(/\u0027/g, '&apos;');
245 this.emit(` ${attr.name}="${value}"`);
246 });
247 if (isHTMLInputElement(element) && element.value)
248 this.handleInputNode(element);
249 this.emit('>');
250
251 if (isHTMLTextAreaElement(element) && element.value)
252 this.handleTextAreaNode(element);
253
254 if (element.childNodes.length === 0 &&
255 HTML5_VOID_ELEMENTS.has(tagName)) {
256 return;
257 }
258 this.serializeChildren(element);
259 this.emit(`</${tagName}>`);
260 }
261
262 /**
263 * @public
264 * @param {!HTMLDocument} document
265 */
266 serialize(document) {
267 if (document.body)
268 this.serializeChildren(document.body);
269 else
270 this.serializeInternal(document.documentElement);
271 return this.strings_.join('');
272 }
273
274 /**
275 * @private
276 * @param {!HTMLElement} element
277 */
278 serializeChildren(element) {
279 /** @type {!Array<!Node>} */
280 const childNodes = Array.from(element.childNodes);
yosin_UTC9 2016/10/25 09:41:11 Since Node#childNodes works with for-of, we don't
Xiaocheng 2016/10/25 13:19:17 Switched to a for loop.
281 if (childNodes.length === 0) {
yosin_UTC9 2016/10/25 09:41:11 if (element.childNodes.length === 0)
Xiaocheng 2016/10/25 13:19:16 Switched to a for loop and removed this check. Pe
282 this.advancedTo(element, 0);
283 return;
284 }
285
286 /** @type {number} */
287 let childIndex = 0;
288 for (const child of childNodes) {
289 this.advancedTo(element, childIndex);
290 this.serializeInternal(child, childIndex);
291 ++childIndex;
292 }
293 this.advancedTo(element, childIndex);
294 }
295
296 /**
297 * @private
298 * @param {!Node} node
299 */
300 serializeInternal(node) {
301 if (isElement(node))
302 return this.handleElementNode(node);
303 if (isCharacterData(node))
304 return this.handleCharacterData(node);
305 throw new Error(`Unexpected node ${node}`);
306 }
307 }
308
46 /** @type {string} */ 309 /** @type {string} */
47 const kSpelling = 'spelling'; 310 const kSpelling = 'spelling';
48 /** @type {string} */ 311 /** @type {string} */
49 const kGrammar = 'grammar'; 312 const kGrammar = 'grammar';
50 313 /** @type {string} */
51 class Marker { 314 const kSpellingMarker = '_';
52 /** 315 /** @type {string} */
53 * @public 316 const kGrammarMarker = '~';
54 * @param {number} location 317 /** @type {string} */
55 * @param {number} length 318 const kBothMarkers = '#';
56 * @param {string=} opt_type
57 * @param {string=} opt_description
58 */
59 constructor(location, length, opt_type, opt_description) {
60 /** @type {number} */
61 this.location_ = location;
62 /** @type {number} */
63 this.length_ = length;
64 /** @type {string} */
65 this.type_ = opt_type || 'spelling';
66 /** @type {boolean} */
67 this.ignoreDescription_ = opt_description === undefined;
68 /** @type {string} */
69 this.description_ = opt_description || '';
70 }
71
72 /** @return {number} */
73 get location() { return this.location_; }
74
75 /** @return {number} */
76 get length() { return this.length_; }
77
78 /** @return {string} */
79 get type() { return this.type_; }
80
81 /** @return {boolean} */
82 get ignoreDescription() { return this.ignoreDescription_; }
83
84 /** @return {string} */
85 get description() { return this.description_; }
86
87 /**
88 * @public
89 */
90 assertValid() {
91 // TODO(xiaochengh): Add proper assert descriptions when needed.
92 assert_true(Number.isInteger(this.location_));
93 assert_greater_than_equal(this.location_, 0);
94 assert_true(Number.isInteger(this.length_));
95 assert_greater_than(this.length_, 0);
96 assert_true(this.type_ === kSpelling || this.type_ === kGrammar);
97 assert_true(typeof this.description_ === 'string');
98 }
99
100 /**
101 * @public
102 * @param {!Marker} expected
103 */
104 assertMatch(expected) {
105 try {
106 assert_equals(this.location, expected.location);
107 assert_equals(this.length, expected.length);
108 assert_equals(this.type, expected.type);
109 if (expected.ignoreDescription)
110 return;
111 assert_equals(this.description, expected.description);
112 } catch (error) {
113 throw new Error(`Expected ${expected} but got ${this}.`);
114 }
115 }
116
117 /** @override */
118 toString() {
119 return `${this.type_} marker at ` +
120 `[${this.location_}, ${this.location_ + this.length_}]` +
121 (this.description_ ? ` with description "${this.description_}"` : ``);
122 }
123 }
124
125 /**
126 * @param {number} location
127 * @param {number} length
128 * @param {string=} opt_description
129 * @return {!Marker}
130 */
131 function spellingMarker(location, length, opt_description) {
132 return new Marker(location, length, kSpelling, opt_description);
133 }
134
135 /**
136 * @param {number} location
137 * @param {number} length
138 * @param {string=} opt_description
139 * @return {!Marker}
140 */
141 function grammarMarker(location, length, opt_description) {
142 return new Marker(location, length, kGrammar, opt_description);
143 }
144
145 /**
146 * @param {!Marker} marker1
147 * @param {!Marker} marker2
148 * @return {number}
149 */
150 function markerComparison(marker1, marker2) {
151 return marker1.location - marker2.location;
152 }
153
154 /**
155 * @param {!Array<!Marker>} expectedMarkers
156 */
157 function checkExpectedMarkers(expectedMarkers) {
158 if (expectedMarkers.length === 0)
159 return;
160 expectedMarkers.forEach(marker => marker.assertValid());
161 expectedMarkers.sort(markerComparison);
162 expectedMarkers.reduce((lastMarker, currentMarker) => {
163 assert_less_than(
164 lastMarker.location + lastMarker.length, currentMarker.location,
165 'Marker ranges should be disjoint.');
166 return currentMarker;
167 });
168 }
169
170 /**
171 * @param {!Node} node
172 * @param {string} type
173 * @param {!Array<!Marker>} markers
174 */
175 function extractMarkersOfType(node, type, markers) {
176 /** @type {!HTMLBodyElement} */
177 const body = node.ownerDocument.body;
178 /** @type {number} */
179 const markerCount = window.internals.markerCountForNode(node, type);
180 for (let i = 0; i < markerCount; ++i) {
181 /** @type {!Range} */
182 const markerRange = window.internals.markerRangeForNode(node, type, i);
183 /** @type {string} */
184 const description = window.internals.markerDescriptionForNode(node, type, i) ;
185 /** @type {number} */
186 const location = window.internals.locationFromRange(body, markerRange);
187 /** @type {number} */
188 const length = window.internals.lengthFromRange(body, markerRange);
189
190 markers.push(new Marker(location, length, type, description));
191 }
192 }
193
194 /**
195 * @param {!Node} node
196 * @param {!Array<!Marker>} markers
197 */
198 function extractAllMarkersRecursivelyTo(node, markers) {
199 extractMarkersOfType(node, kSpelling, markers);
200 extractMarkersOfType(node, kGrammar, markers);
201 node.childNodes.forEach(
202 child => extractAllMarkersRecursivelyTo(child, markers));
203 }
204
205 /**
206 * @param {!Document} doc
207 * @return {!Array<!Marker>}
208 */
209 function extractAllMarkers(doc) {
210 /** @type {!Array<!Marker>} */
211 const markers = [];
212 extractAllMarkersRecursivelyTo(doc.body, markers);
213 markers.sort(markerComparison);
214 return markers;
215 }
216 319
217 /** 320 /**
218 * @param {!Test} testObject 321 * @param {!Test} testObject
219 * @param {!Sample} sample, 322 * @param {!Sample} sample
220 * @param {!Array<!Marker>} expectedMarkers 323 * @param {string} expectedText
221 * @param {number} remainingRetry 324 * @param {number} remainingRetry
222 * @param {number} retryInterval 325 * @param {number} retryInterval
223 */ 326 */
224 function verifyMarkers( 327 function verifyMarkers(
225 testObject, sample, expectedMarkers, remainingRetry, retryInterval) { 328 testObject, sample, expectedText, remainingRetry, retryInterval) {
226 assert_not_equals( 329 assert_not_equals(
227 window.internals, undefined, 330 window.internals, undefined,
228 'window.internals is required for running automated spellcheck tests.'); 331 'window.internals is required for running automated spellcheck tests.');
229 332
230 /** @type {!Array<!Marker>} */ 333 /** @type {!MarkerSerializer} */
231 const actualMarkers = extractAllMarkers(sample.document); 334 const serializer = new MarkerSerializer(
335 [kSpelling, kGrammar],
336 [kSpellingMarker, kGrammarMarker, kBothMarkers].join(''));
337
232 try { 338 try {
233 assert_equals(actualMarkers.length, expectedMarkers.length, 339 assert_equals(serializer.serialize(sample.document), expectedText);
234 'Number of markers mismatch.');
235 actualMarkers.forEach(
236 (marker, index) => marker.assertMatch(expectedMarkers[index]));
237 testObject.done(); 340 testObject.done();
238 sample.remove(); 341 sample.remove();
239 } catch (error) { 342 } catch (error) {
240 if (remainingRetry <= 0) 343 if (remainingRetry <= 0)
241 throw error; 344 throw error;
242 345
243 // Force invoking idle time spellchecker in case it has not been run yet. 346 // Force invoking idle time spellchecker in case it has not been run yet.
244 if (window.testRunner) 347 if (window.testRunner)
245 window.testRunner.runIdleTasks(() => {}); 348 window.testRunner.runIdleTasks(() => {});
246 349
247 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger 350 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger
248 // something in JavaScript (e.g., a |Promise|), so that we can actively 351 // something in JavaScript (e.g., a |Promise|), so that we can actively
249 // know the completion of spellchecking instead of passively waiting for 352 // know the completion of spellchecking instead of passively waiting for
250 // markers to appear or disappear. 353 // markers to appear or disappear.
251 testObject.step_timeout( 354 testObject.step_timeout(
252 () => verifyMarkers(testObject, sample, expectedMarkers, 355 () => verifyMarkers(testObject, sample, expectedText,
253 remainingRetry - 1, retryInterval), 356 remainingRetry - 1, retryInterval),
254 retryInterval); 357 retryInterval);
255 } 358 }
256 } 359 }
257 360
258 // Spellchecker gets triggered not only by text and selection change, but also 361 // Spellchecker gets triggered not only by text and selection change, but also
259 // by focus change. For example, misspelling markers in <INPUT> disappear when 362 // by focus change. For example, misspelling markers in <INPUT> disappear when
260 // the window loses focus, even though the selection does not change. 363 // the window loses focus, even though the selection does not change.
261 // Therefore, we disallow spellcheck tests from running simultaneously to 364 // Therefore, we disallow spellcheck tests from running simultaneously to
262 // prevent interference among them. If we call spellcheck_test while another 365 // prevent interference among them. If we call spellcheck_test while another
263 // test is running, the new test will be added into testQueue waiting for the 366 // test is running, the new test will be added into testQueue waiting for the
264 // completion of the previous test. 367 // completion of the previous test.
265 368
266 /** @type {boolean} */ 369 /** @type {boolean} */
267 var spellcheckTestRunning = false; 370 var spellcheckTestRunning = false;
268 /** @type {!Array<!Object>} */ 371 /** @type {!Array<!Object>} */
269 const testQueue = []; 372 const testQueue = [];
270 373
271 /** 374 /**
272 * @param {string} inputText 375 * @param {string} inputText
273 * @param {function(!Document)|string} tester 376 * @param {function(!Document)|string} tester
274 * @param {!Marker|!Array<!Marker>} expectedMarkers 377 * @param {string} expectedText
275 * @param {string=} opt_title 378 * @param {string=} opt_title
276 */ 379 */
277 function invokeSpellcheckTest(inputText, tester, expectedMarkers, opt_title) { 380 function invokeSpellcheckTest(inputText, tester, expectedText, opt_title) {
278 spellcheckTestRunning = true; 381 spellcheckTestRunning = true;
279 382
280 /** @type {!Test} */ 383 async_test(testObject => {
281 const testObject = async_test(opt_title, {isSpellcheckTest: true}); 384 // TODO(xiaochengh): Merge the following part with |assert_selection|.
385 /** @type {!Sample} */
386 const sample = new Sample(inputText);
387 if (typeof(tester) === 'function') {
388 tester.call(window, sample.document);
389 } else if (typeof(tester) === 'string') {
390 const strings = tester.split(/ (.+)/);
391 sample.document.execCommand(strings[0], false, strings[1]);
392 } else {
393 assert_unreached(`Invalid tester: ${tester}`);
394 }
282 395
283 if (!(expectedMarkers instanceof Array)) 396 /** @type {number} */
284 expectedMarkers = [expectedMarkers] 397 const kMaxRetry = 10;
285 testObject.step(() => checkExpectedMarkers(expectedMarkers)); 398 /** @type {number} */
399 const kRetryInterval = 50;
286 400
287 if (window.testRunner) 401 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger
288 window.testRunner.setMockSpellCheckerEnabled(true); 402 // something in JavaScript (e.g., a |Promise|), so that we can actively know
289 403 // the completion of spellchecking instead of passively waiting for markers to
290 // TODO(xiaochengh): Merge the following part with |assert_selection|. 404 // appear or disappear.
291 /** @type {!Sample} */ 405 testObject.step_timeout(
292 const sample = new Sample(inputText); 406 () => verifyMarkers(testObject, sample, expectedText,
293 if (typeof(tester) === 'function') { 407 kMaxRetry, kRetryInterval),
294 tester.call(window, sample.document); 408 kRetryInterval);
295 } else if (typeof(tester) === 'string') { 409 }, opt_title, {isSpellcheckTest: true});
296 const strings = tester.split(/ (.+)/);
297 sample.document.execCommand(strings[0], false, strings[1]);
298 } else {
299 testObject.step(() => assert_unreached(`Invalid tester: ${tester}`));
300 }
301
302 /** @type {number} */
303 const kMaxRetry = 10;
304 /** @type {number} */
305 const kRetryInterval = 50;
306
307 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger
308 // something in JavaScript (e.g., a |Promise|), so that we can actively know
309 // the completion of spellchecking instead of passively waiting for markers to
310 // appear or disappear.
311 testObject.step_timeout(
312 () => verifyMarkers(testObject, sample, expectedMarkers,
313 kMaxRetry, kRetryInterval),
314 kRetryInterval);
315 } 410 }
316 411
317 add_result_callback(testObj => { 412 add_result_callback(testObj => {
318 if (!testObj.properties.isSpellcheckTest) 413 if (!testObj.properties.isSpellcheckTest)
319 return; 414 return;
320 spellcheckTestRunning = false; 415 spellcheckTestRunning = false;
321 /** @type {Object} */ 416 /** @type {Object} */
322 const args = testQueue.shift(); 417 const args = testQueue.shift();
323 if (args === undefined) 418 if (args === undefined)
324 return; 419 return;
325 invokeSpellcheckTest(args.inputText, args.tester, 420 invokeSpellcheckTest(args.inputText, args.tester,
326 args.expectedMarkers, args.opt_title); 421 args.expectedText, args.opt_title);
327 }); 422 });
328 423
424 // TODO(xiaochengh): Add support for checking marker descriptions.
425
329 /** 426 /**
330 * @param {string} inputText 427 * @param {string} inputText
331 * @param {function(!Document)|string} tester 428 * @param {function(!Document)|string} tester
332 * @param {!Marker|!Array<!Marker>} expectedMarkers 429 * @param {string} expectedText
333 * @param {string=} opt_title 430 * @param {string=} opt_title
334 */ 431 */
335 function spellcheckTest(inputText, tester, expectedMarkers, opt_title) { 432 function spellcheckTest(inputText, tester, expectedText, opt_title) {
433 if (window.testRunner)
yosin_UTC9 2016/10/25 09:41:11 We should abort if window.internals is unavailable
Xiaocheng 2016/10/25 13:19:17 There's a reason not to abort here. |internals| i
434 window.testRunner.setMockSpellCheckerEnabled(true);
435
336 if (spellcheckTestRunning) { 436 if (spellcheckTestRunning) {
337 testQueue.push({ 437 testQueue.push({
338 inputText: inputText, tester: tester, 438 inputText: inputText, tester: tester,
339 expectedMarkers: expectedMarkers, opt_title: opt_title}); 439 expectedText: expectedText, opt_title: opt_title});
340 return; 440 return;
341 } 441 }
342 442
343 invokeSpellcheckTest(inputText, tester, expectedMarkers, opt_title); 443 invokeSpellcheckTest(inputText, tester, expectedText, opt_title);
344 } 444 }
345 445
346 // Export symbols 446 // Export symbols
347 window.Marker = Marker;
348 window.spellingMarker = spellingMarker;
349 window.grammarMarker = grammarMarker;
350 window.spellcheck_test = spellcheckTest; 447 window.spellcheck_test = spellcheckTest;
351 })(); 448 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698