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

Side by Side Diff: third_party/WebKit/LayoutTests/editing/assert_selection.js

Issue 2016173002: Convert execCommand/unlink.html to use w3c test harness (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: 2016-05-27T18:50:31 Created 4 years, 6 months 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
« no previous file with comments | « no previous file | third_party/WebKit/LayoutTests/editing/execCommand/unlink.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 |assert_selection(sample, tester, expectedText)| assertion 7 // This file provides |assert_selection(sample, tester, expectedText)| assertion
8 // to W3C test harness to write editing test cases easier. 8 // to W3C test harness to write editing test cases easier.
9 // 9 //
10 // |sample| is an HTML fragment text which is inserted as |innerHTML|. It should 10 // |sample| is an HTML fragment text which is inserted as |innerHTML|. It should
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after
339 // import "imported/wpt/html/resources/common.js", since |HTML5_VOID_ELEMENTS| 339 // import "imported/wpt/html/resources/common.js", since |HTML5_VOID_ELEMENTS|
340 // is defined in there. 340 // is defined in there.
341 /** 341 /**
342 * @const @type {!Set<string>} 342 * @const @type {!Set<string>}
343 * only void (without end tag) HTML5 elements 343 * only void (without end tag) HTML5 elements
344 */ 344 */
345 const HTML5_VOID_ELEMENTS = new Set([ 345 const HTML5_VOID_ELEMENTS = new Set([
346 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 346 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
347 'keygen', 'link', 'meta', 'param', 'source','track', 'wbr' ]); 347 'keygen', 'link', 'meta', 'param', 'source','track', 'wbr' ]);
348 348
349 class Serializer { 349 class Serializer {
yoichio 2016/05/30 02:08:36 Add comment describing simple example of sample =
350 /** 350 /**
351 * @public 351 * @public
352 * @param {!SampleSelection} selection 352 * @param {!SampleSelection} selection
353 */ 353 */
354 constructor(selection) { 354 constructor(selection) {
355 /** @type {!SampleSelection} */ 355 /** @type {!SampleSelection} */
356 this.selection_ = selection; 356 this.selection_ = selection;
357 /** @type {!Array<strings>} */ 357 /** @type {!Array<strings>} */
358 this.strings_ = []; 358 this.strings_ = [];
359 } 359 }
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
412 } 412 }
413 this.emit(text); 413 this.emit(text);
414 } 414 }
415 415
416 /** 416 /**
417 * @private 417 * @private
418 * @param {!HTMLElement} element 418 * @param {!HTMLElement} element
419 * @param {number} nodeIndex 419 * @param {number} nodeIndex
420 */ 420 */
421 handleElementNode(element, nodeIndex) { 421 handleElementNode(element, nodeIndex) {
422 if (element.parenNode === this.selection_.focusNode && 422 if (element.parentNode === this.selection_.focusNode &&
yoichio 2016/05/30 02:08:36 The strict mode can't check this? Sad,,,
423 nodeIndex === this.selection_.focusOffset) { 423 nodeIndex === this.selection_.focusOffset) {
424 this.emit('|'); 424 this.emit('|');
425 } else if ( 425 } else if (element.parentNode === this.selection_.anchorNode &&
426 element === this.selection_.anchorNode && 426 nodeIndex === this.selection_.anchorOffset) {
427 nodeIndex === this.selection_.anchorOffset) {
428 this.emit('^'); 427 this.emit('^');
429 } 428 }
430 /** @type {string} */ 429 /** @type {string} */
431 const tagName = element.tagName.toLowerCase(); 430 const tagName = element.tagName.toLowerCase();
432 this.emit(`<${tagName}`); 431 this.emit(`<${tagName}`);
433 Array.from(element.attributes) 432 Array.from(element.attributes)
434 .sort((attr1, attr2) => attr1.name.localeCompare(attr2.name)) 433 .sort((attr1, attr2) => attr1.name.localeCompare(attr2.name))
435 .forEach(attr => { 434 .forEach(attr => {
436 if (attr.value === '') 435 if (attr.value === '')
437 return this.emit(` ${attr.name}`); 436 return this.emit(` ${attr.name}`);
438 const value = attr.value.replace(/&/g, '&amp;') 437 const value = attr.value.replace(/&/g, '&amp;')
439 .replace(/\u0022/g, '&quot;') 438 .replace(/\u0022/g, '&quot;')
440 .replace(/\u0027/g, '&apos;'); 439 .replace(/\u0027/g, '&apos;');
441 this.emit(` ${attr.name}="${value}"`); 440 this.emit(` ${attr.name}="${value}"`);
442 }); 441 });
443 this.emit('>'); 442 this.emit('>');
444 if (element.childNodes.length === 0 && 443 if (element.childNodes.length === 0 &&
445 HTML5_VOID_ELEMENTS.has(tagName)) { 444 HTML5_VOID_ELEMENTS.has(tagName)) {
446 return; 445 return;
447 } 446 }
448 /** @type {number} */ 447 /** @type {number} */
449 let childIndex = 0; 448 let childIndex = 0;
450 for (const child of Array.from(element.childNodes)) { 449 for (const child of Array.from(element.childNodes)) {
451 this.serializeInternal(child, childIndex); 450 this.serializeInternal(child, childIndex);
452 ++childIndex; 451 ++childIndex;
453 } 452 }
453 if (element === this.selection_.focusNode &&
454 childIndex === this.selection_.focusOffset) {
455 this.emit('|');
456 } else if (element === this.selection_.anchorNode &&
457 childIndex === this.selection_.anchorOffset) {
458 this.emit('^');
459 }
454 this.emit(`</${tagName}>`); 460 this.emit(`</${tagName}>`);
455 } 461 }
456 462
457 /** 463 /**
458 * @public 464 * @public
459 * @param {!HTMLElement} element 465 * @param {!HTMLElement} element
460 */ 466 */
461 serialize(element) { 467 serialize(element) {
462 if (this.selection_.isNone) 468 if (this.selection_.isNone)
463 return node.outerHTML; 469 return node.outerHTML;
464 this.serializeInternal(element, 0); 470 this.serializeInternal(element, 0);
465 return this.strings_.join(''); 471 return this.strings_.join('');
466 } 472 }
467 473
468 /** 474 /**
469 * @private 475 * @private
470 * @param {!Node} node 476 * @param {!Node} node
471 * @param {number} nodeIndex 477 * @param {number} nodeIndex
472 */ 478 */
473 serializeInternal(node, nodeIndex) { 479 serializeInternal(node, nodeIndex) {
474 if (isElement(node)) 480 if (isElement(node))
475 return this.handleElementNode(node, nodeIndex); 481 return this.handleElementNode(node, nodeIndex);
476 if (isCharacterData(node)) 482 if (isCharacterData(node))
477 return this.handleCharacterData(node); 483 return this.handleCharacterData(node);
478 throw new Error(`Unexpected node ${node}`); 484 throw new Error(`Unexpected node ${node}`);
479 } 485 }
480 } 486 }
481 487
482 class Sample { 488 class Sample {
yoichio 2016/05/30 02:08:36 Add comment describing simple example of sample =
483 /** 489 /**
484 * @public 490 * @public
485 * @param {string} sampleText 491 * @param {string} sampleText
486 */ 492 */
487 constructor(sampleText) { 493 constructor(sampleText) {
488 /** @const @type {!HTMLIFame} */ 494 /** @const @type {!HTMLIFame} */
489 this.iframe_ = document.createElement('iframe'); 495 this.iframe_ = document.createElement('iframe');
490 document.body.appendChild(this.iframe_); 496 document.body.appendChild(this.iframe_);
491 /** @const @type {!HTMLDocument} */ 497 /** @const @type {!HTMLDocument} */
492 this.document_ = this.iframe_.contentDocument; 498 this.document_ = this.iframe_.contentDocument;
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
563 for (const line of getStack()) { 569 for (const line of getStack()) {
564 const match = RE_TEST_FUNCTION.exec(line); 570 const match = RE_TEST_FUNCTION.exec(line);
565 if (!match) 571 if (!match)
566 continue; 572 continue;
567 return `${match[1]}(${match[2]})`; 573 return `${match[1]}(${match[2]})`;
568 } 574 }
569 return ''; 575 return '';
570 } 576 }
571 577
572 /** 578 /**
579 * @param {string} expectedText
580 */
581 function checkExpectedText(expectedText) {
yoichio 2016/05/30 02:08:36 Could you extract sanity check functions from this
582 /** @type {number} */
583 const anchorOffset = expectedText.indexOf('^');
584 /** @type {number} */
585 const focusOffset = expectedText.indexOf('|');
586 if (anchorOffset != expectedText.lastIndexOf('^')) {
587 throw new Error(
588 `You should have at most one anchor marker "^" in "${expectedText}".`);
589 }
590 if (focusOffset != expectedText.lastIndexOf('|')) {
591 throw new Error(
592 `You should have at most one focus marker "|" in "${expectedText}".`);
593 }
594 if (anchorOffset >= 0 && focusOffset < 0) {
595 throw new Error(
596 `You should have both anchor marker "^" and focus marker "|" in ${expect edText}`);
597 }
598 }
599
600 /**
573 * @param {string} inputText 601 * @param {string} inputText
574 * @param {function(!Selection)|string} 602 * @param {function(!Selection)|string}
575 * @param {string} expectedText 603 * @param {string} expectedText
576 * @param {string=} opt_description 604 * @param {string=} opt_description
577 */ 605 */
578 function assertSelection( 606 function assertSelection(
579 inputText, tester, expectedText, opt_description = '') { 607 inputText, tester, expectedText, opt_description = '') {
580 /** @type {string} */ 608 /** @type {string} */
581 const description = 609 const description =
582 opt_description === '' ? assembleDescription() : opt_description; 610 opt_description === '' ? assembleDescription() : opt_description;
583 if (expectedText.indexOf('^') != expectedText.lastIndexOf('^')) { 611 checkExpectedText(expectedText);
584 throw new Error(
585 `You should have at most one anchor marker "^" in "${expectedText}".`) ;
586 }
587 if (expectedText.indexOf('|') != expectedText.lastIndexOf('|')) {
588 throw new Error(
589 `You should have at most one focus marker "|" in "${expectedText}".`);
590 }
591 const sample = new Sample(inputText); 612 const sample = new Sample(inputText);
592 if (typeof(tester) === 'function') { 613 if (typeof(tester) === 'function') {
593 tester.call(window, sample.selection); 614 tester.call(window, sample.selection);
594 } else if (typeof(tester) === 'string') { 615 } else if (typeof(tester) === 'string') {
595 const strings = tester.split(' '); 616 const strings = tester.split(' ');
596 sample.document.execCommand(strings[0], false, strings[1]); 617 sample.document.execCommand(strings[0], false, strings[1]);
597 } else { 618 } else {
598 throw new Error(`Invalid tester: ${tester}`); 619 throw new Error(`Invalid tester: ${tester}`);
599 } 620 }
600 /** @type {string} */ 621 /** @type {string} */
601 const actualText = sample.serialize(); 622 const actualText = sample.serialize();
602 // We keep sample HTML when assertion is false for ease of debugging test 623 // We keep sample HTML when assertion is false for ease of debugging test
603 // case. 624 // case.
604 if (actualText == expectedText) 625 if (actualText == expectedText)
605 sample.remove(); 626 sample.remove();
606 assert_equals(actualText, expectedText, description); 627 assert_equals(actualText, expectedText, description);
607 } 628 }
608 629
609 // Export symbols 630 // Export symbols
610 window.Sample = Sample; 631 window.Sample = Sample;
611 window.assert_selection = assertSelection; 632 window.assert_selection = assertSelection;
612 })(); 633 })();
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/LayoutTests/editing/execCommand/unlink.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698