chromium/third_party/blink/web_tests/editing/spelling/cold_mode_no_repeated_full_testing.html

<!doctype html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../assert_selection.js"></script>
<script src="spellcheck_test.js"></script>
<script>
// This test asserts that in an editor with a large amount of text, spell
// checker does not repeatedly check the editor in full after trivial
// operations (e.g., selection move), and does so only after significant
// changes have taken place.

const n = 100;
const text = 'zz zz zz zz.';
const checkedText = '#zz# #zz# #zz# #zz#.'
const markup = `<div>${text}</div>`;
const checkedMarkup = `<div>${checkedText}</div>`;

let checkedTextLengthBefore = 0;

const step1 = () => spellcheck_test(
    `<div contenteditable>${markup.repeat(n)}</div>`,
    document => document.querySelector('div').focus(),
    `<div contenteditable>${checkedMarkup.repeat(n)}</div>`,
    {
      title: 'Step 1: Cold mode checks full contenteditable for the first pass',
      needsFullCheck: true,
      callback: step2,
    }
);

const step2 = sample => spellcheck_test(
    sample,
    () => {
      checkedTextLengthBefore =
        internals.spellCheckedTextLength(sample.document);
      sample.selection.modify('move', 'forward', 'line');
    },
    `<div contenteditable>${checkedMarkup.repeat(n)}</div>`,
    {
      title: 'Step 2: Selection move',
      // Note: needsFullCheck means spinning spellchecker lifecycle until idle,
      // not forcing spellchecker to check the editor in full.
      needsFullCheck: true,
      callback: step3,
    }
);

const step3 = sample => spellcheck_test(
    sample,
    () => {
      let recheckedTextLength =
          internals.spellCheckedTextLength(sample.document) - checkedTextLengthBefore;
      assert_equals(recheckedTextLength, 0);
    },
    `<div contenteditable>${checkedMarkup.repeat(n)}</div>`,
    {
      title: 'Step 3: Spell checker does not recheck a fully-checked contenteditable on selection move',
      callback: step4,
    }
);

const step4 = sample => spellcheck_test(
    sample,
    () => {
      checkedTextLengthBefore =
        internals.spellCheckedTextLength(sample.document);
      sample.selection.modify('extend', 'forward', 'line');
      sample.document.execCommand('delete')
    },
    `<div contenteditable>${checkedMarkup.repeat(n - 1)}</div>`,
    {
      title: 'Step 4: Minor modification - delete one paragraph of text',
      callback: step5,
    }
);

const step5 = sample => spellcheck_test(
    sample,
    () => {
      let recheckedTextLength =
          internals.spellCheckedTextLength(sample.document) - checkedTextLengthBefore;
      assert_less_than_equal(recheckedTextLength, text.length);
    },
    `<div contenteditable>${checkedMarkup.repeat(n - 1)}</div>`,
    {
      title: 'Step 5: Hot mode does not recheck more than one paragraph',
      callback: step6,
    }
);

const step6 = sample => spellcheck_test(
    sample,
    () => {
      const document = sample.document;
      document.execCommand('InsertText', false, (text + '\n').repeat(n));
    },
    `<div contenteditable>${checkedMarkup.repeat(n * 2 - 1)}</div>`,
    {
      title: 'Step 6: Spell checker rechecks text in full after significant changes',
      needsFullCheck: true,
    }
);

step1();
</script>