| OLD | NEW |
| 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, expectedText, opt_title)| asynchronous test | 8 // |spellcheck_test(sample, tester, expectedText, opt_title)| asynchronous test |
| 9 // to W3C test harness for easier writing of spellchecker test cases. | 9 // to W3C test harness for easier writing of spellchecker test cases. |
| 10 // | 10 // |
| 11 // |sample| is an HTML fragment text which is inserted as |innerHTML|. It should | 11 // |sample| is |
| 12 // have at least one focus boundary point marker "|" and at most one anchor | 12 // - Either an HTML fragment text which is inserted as |innerHTML|, in which |
| 13 // boundary point marker "^" indicating the initial selection. | 13 // case It should have at least one focus boundary point marker "|" and at |
| 14 // TODO(editing-dev): Make initial selection work with TEXTAREA and INPUT. | 14 // most one anchor boundary point marker "^" indicating the initial selection. |
| 15 // TODO(editing-dev): Make initial selection work with TEXTAREA and INPUT. |
| 16 // - Or a |Sample| object created by some previous test. |
| 15 // | 17 // |
| 16 // |tester| is either name with parameter of execCommand or function taking | 18 // |tester| is either name with parameter of execCommand or function taking |
| 17 // one parameter |Document|. | 19 // one parameter |Document|. |
| 18 // | 20 // |
| 19 // |expectedText| is an HTML fragment indicating the expected result, where text | 21 // |expectedText| is an HTML fragment indicating the expected result, where text |
| 20 // with spelling marker is surrounded by '_', and text with grammar marker is | 22 // with spelling marker is surrounded by '_', and text with grammar marker is |
| 21 // surrounded by '~'. | 23 // surrounded by '~'. |
| 22 // | 24 // |
| 23 // |opt_title| is an optional string giving the title of the test case. | 25 // |opt_args| is an optional object with the following optional fields: |
| 26 // - title: the title of the test case. |
| 27 // - callback: a callback function to be run after the test passes, which takes |
| 28 // one parameter -- the |Sample| at the end of the test |
| 29 // It is allowed to pass a string as |arg_args| to indicate the title only. |
| 24 // | 30 // |
| 25 // See spellcheck_test.html for sample usage. | 31 // See spellcheck_test.html for sample usage. |
| 26 | 32 |
| 27 (function() { | 33 (function() { |
| 28 const Sample = window.Sample; | 34 const Sample = window.Sample; |
| 29 | 35 |
| 30 // TODO(editing-dev): Once we can import JavaScript file from scripts, we should | 36 // TODO(editing-dev): Once we can import JavaScript file from scripts, we should |
| 31 // import "imported/wpt/html/resources/common.js", since |HTML5_VOID_ELEMENTS| | 37 // import "imported/wpt/html/resources/common.js", since |HTML5_VOID_ELEMENTS| |
| 32 // is defined in there. | 38 // is defined in there. |
| 33 /** | 39 /** |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 250 */ | 256 */ |
| 251 serializeInternal(node) { | 257 serializeInternal(node) { |
| 252 if (isElement(node)) | 258 if (isElement(node)) |
| 253 return this.handleElementNode(node); | 259 return this.handleElementNode(node); |
| 254 if (isCharacterData(node)) | 260 if (isCharacterData(node)) |
| 255 return this.handleCharacterData(node); | 261 return this.handleCharacterData(node); |
| 256 throw new Error(`Unexpected node ${node}`); | 262 throw new Error(`Unexpected node ${node}`); |
| 257 } | 263 } |
| 258 } | 264 } |
| 259 | 265 |
| 266 /** @type {string} */ |
| 267 const kTitle = 'title'; |
| 268 /** @type {string} */ |
| 269 const kCallback = 'callback'; |
| 270 /** @type {string} */ |
| 271 const kIsSpellcheckTest = 'isSpellcheckTest'; |
| 272 |
| 260 /** | 273 /** |
| 261 * @param {!Test} testObject | 274 * @param {!Test} testObject |
| 262 * @param {!Sample} sample | 275 * @param {!Sample} sample |
| 263 * @param {string} expectedText | 276 * @param {string} expectedText |
| 264 * @param {number} remainingRetry | 277 * @param {number} remainingRetry |
| 265 * @param {number} retryInterval | 278 * @param {number} retryInterval |
| 266 */ | 279 */ |
| 267 function verifyMarkers( | 280 function verifyMarkers( |
| 268 testObject, sample, expectedText, remainingRetry, retryInterval) { | 281 testObject, sample, expectedText, remainingRetry, retryInterval) { |
| 269 assert_not_equals( | 282 assert_not_equals( |
| 270 window.internals, undefined, | 283 window.internals, undefined, |
| 271 'window.internals is required for running automated spellcheck tests.'); | 284 'window.internals is required for running automated spellcheck tests.'); |
| 272 | 285 |
| 273 /** @type {!MarkerSerializer} */ | 286 /** @type {!MarkerSerializer} */ |
| 274 const serializer = new MarkerSerializer({ | 287 const serializer = new MarkerSerializer({ |
| 275 spelling: '_', | 288 spelling: '_', |
| 276 grammar: '~'}); | 289 grammar: '~'}); |
| 277 | 290 |
| 278 try { | 291 try { |
| 279 assert_equals(serializer.serialize(sample.document), expectedText); | 292 assert_equals(serializer.serialize(sample.document), expectedText); |
| 280 testObject.done(); | 293 testObject.done(); |
| 281 sample.remove(); | |
| 282 } catch (error) { | 294 } catch (error) { |
| 283 if (remainingRetry <= 0) | 295 if (remainingRetry <= 0) |
| 284 throw error; | 296 throw error; |
| 285 | 297 |
| 286 // Force invoking idle time spellchecker in case it has not been run yet. | 298 // Force invoking idle time spellchecker in case it has not been run yet. |
| 287 if (window.testRunner) | 299 if (window.testRunner) |
| 288 window.testRunner.runIdleTasks(() => {}); | 300 window.testRunner.runIdleTasks(() => {}); |
| 289 | 301 |
| 290 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger | 302 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger |
| 291 // something in JavaScript (e.g., a |Promise|), so that we can actively | 303 // something in JavaScript (e.g., a |Promise|), so that we can actively |
| (...skipping 13 matching lines...) Expand all Loading... |
| 305 // prevent interference among them. If we call spellcheck_test while another | 317 // prevent interference among them. If we call spellcheck_test while another |
| 306 // test is running, the new test will be added into testQueue waiting for the | 318 // test is running, the new test will be added into testQueue waiting for the |
| 307 // completion of the previous test. | 319 // completion of the previous test. |
| 308 | 320 |
| 309 /** @type {boolean} */ | 321 /** @type {boolean} */ |
| 310 var spellcheckTestRunning = false; | 322 var spellcheckTestRunning = false; |
| 311 /** @type {!Array<!Object>} */ | 323 /** @type {!Array<!Object>} */ |
| 312 const testQueue = []; | 324 const testQueue = []; |
| 313 | 325 |
| 314 /** | 326 /** |
| 315 * @param {string} inputText | 327 * @param {!Sample|string} input |
| 316 * @param {function(!Document)|string} tester | 328 * @param {function(!Document)|string} tester |
| 317 * @param {string} expectedText | 329 * @param {string} expectedText |
| 318 * @param {string=} opt_title | 330 * @param {Object} args |
| 319 */ | 331 */ |
| 320 function invokeSpellcheckTest(inputText, tester, expectedText, opt_title) { | 332 function invokeSpellcheckTest(input, tester, expectedText, args) { |
| 321 spellcheckTestRunning = true; | 333 spellcheckTestRunning = true; |
| 322 | 334 |
| 323 async_test(testObject => { | 335 async_test(testObject => { |
| 324 // TODO(xiaochengh): Merge the following part with |assert_selection|. | 336 // TODO(xiaochengh): Merge the following part with |assert_selection|. |
| 325 /** @type {!Sample} */ | 337 /** @type {!Sample} */ |
| 326 const sample = new Sample(inputText); | 338 const sample = typeof(input) === 'string' ? new Sample(input) : input; |
| 339 testObject.sample = sample; |
| 340 |
| 327 if (typeof(tester) === 'function') { | 341 if (typeof(tester) === 'function') { |
| 328 tester.call(window, sample.document); | 342 tester.call(window, sample.document); |
| 329 } else if (typeof(tester) === 'string') { | 343 } else if (typeof(tester) === 'string') { |
| 330 const strings = tester.split(/ (.+)/); | 344 const strings = tester.split(/ (.+)/); |
| 331 sample.document.execCommand(strings[0], false, strings[1]); | 345 sample.document.execCommand(strings[0], false, strings[1]); |
| 332 } else { | 346 } else { |
| 333 assert_unreached(`Invalid tester: ${tester}`); | 347 assert_unreached(`Invalid tester: ${tester}`); |
| 334 } | 348 } |
| 335 | 349 |
| 336 /** @type {number} */ | 350 /** @type {number} */ |
| 337 const kMaxRetry = 10; | 351 const kMaxRetry = 10; |
| 338 /** @type {number} */ | 352 /** @type {number} */ |
| 339 const kRetryInterval = 50; | 353 const kRetryInterval = 50; |
| 340 | 354 |
| 341 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger | 355 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger |
| 342 // something in JavaScript (e.g., a |Promise|), so that we can actively know | 356 // something in JavaScript (e.g., a |Promise|), so that we can actively know |
| 343 // the completion of spellchecking instead of passively waiting for markers | 357 // the completion of spellchecking instead of passively waiting for markers |
| 344 // to appear or disappear. | 358 // to appear or disappear. |
| 345 testObject.step_timeout( | 359 testObject.step_timeout( |
| 346 () => verifyMarkers(testObject, sample, expectedText, | 360 () => verifyMarkers(testObject, sample, expectedText, |
| 347 kMaxRetry, kRetryInterval), | 361 kMaxRetry, kRetryInterval), |
| 348 kRetryInterval); | 362 kRetryInterval); |
| 349 }, opt_title, {isSpellcheckTest: true}); | 363 }, args[kTitle], args); |
| 350 } | 364 } |
| 351 | 365 |
| 352 add_result_callback(testObj => { | 366 add_result_callback(testObj => { |
| 353 if (!testObj.properties.isSpellcheckTest) | 367 if (!testObj.properties[kIsSpellcheckTest]) |
| 354 return; | 368 return; |
| 355 spellcheckTestRunning = false; | 369 spellcheckTestRunning = false; |
| 370 |
| 371 if (testObj.status === testObj.PASS) { |
| 372 if (testObj.properties[kCallback]) { |
| 373 testObj.properties[kCallback](testObj.sample); |
| 374 } else { |
| 375 testObj.sample.remove(); |
| 376 } |
| 377 } |
| 378 |
| 356 /** @type {Object} */ | 379 /** @type {Object} */ |
| 357 const args = testQueue.shift(); | 380 const next = testQueue.shift(); |
| 358 if (args === undefined) | 381 if (next === undefined) |
| 359 return; | 382 return; |
| 360 invokeSpellcheckTest(args.inputText, args.tester, | 383 invokeSpellcheckTest(next.input, next.tester, |
| 361 args.expectedText, args.opt_title); | 384 next.expectedText, next.args); |
| 362 }); | 385 }); |
| 363 | 386 |
| 364 /** | 387 /** |
| 365 * @param {string} inputText | 388 * @param {Object=} passedArgs |
| 389 * @return {!Object} |
| 390 */ |
| 391 function getTestArguments(passedArgs) { |
| 392 const args = {}; |
| 393 args[kIsSpellcheckTest] = true; |
| 394 [kTitle, kCallback].forEach(key => args[key] = undefined); |
| 395 if (!passedArgs) |
| 396 return args; |
| 397 |
| 398 if (typeof(passedArgs) === 'string') { |
| 399 args[kTitle] = passedArgs; |
| 400 return args; |
| 401 } |
| 402 |
| 403 [kTitle, kCallback].forEach(key => args[key] = passedArgs[key]); |
| 404 return args; |
| 405 } |
| 406 |
| 407 /** |
| 408 * @param {!Sample|string} input |
| 366 * @param {function(!Document)|string} tester | 409 * @param {function(!Document)|string} tester |
| 367 * @param {string} expectedText | 410 * @param {string} expectedText |
| 368 * @param {string=} opt_title | 411 * @param {Object=} opt_args |
| 369 */ | 412 */ |
| 370 function spellcheckTest(inputText, tester, expectedText, opt_title) { | 413 function spellcheckTest(input, tester, expectedText, opt_args) { |
| 371 if (window.testRunner) | 414 if (window.testRunner) |
| 372 window.testRunner.setMockSpellCheckerEnabled(true); | 415 window.testRunner.setMockSpellCheckerEnabled(true); |
| 373 | 416 |
| 417 /** @type {!Object} */ |
| 418 const args = getTestArguments(opt_args); |
| 419 |
| 374 if (spellcheckTestRunning) { | 420 if (spellcheckTestRunning) { |
| 375 testQueue.push({ | 421 testQueue.push({ |
| 376 inputText: inputText, tester: tester, | 422 input: input, tester: tester, |
| 377 expectedText: expectedText, opt_title: opt_title}); | 423 expectedText: expectedText, args: args}); |
| 378 return; | 424 return; |
| 379 } | 425 } |
| 380 | 426 |
| 381 invokeSpellcheckTest(inputText, tester, expectedText, opt_title); | 427 invokeSpellcheckTest(input, tester, expectedText, args); |
| 382 } | 428 } |
| 383 | 429 |
| 384 // Export symbols | 430 // Export symbols |
| 385 window.spellcheck_test = spellcheckTest; | 431 window.spellcheck_test = spellcheckTest; |
| 386 })(); | 432 })(); |
| OLD | NEW |