Skip to content

Commit

Permalink
UIEvents: Test the textInput event
Browse files Browse the repository at this point in the history
  • Loading branch information
zcorpan authored Feb 23, 2024
1 parent fc207f9 commit 7dcb66e
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 0 deletions.
1 change: 1 addition & 0 deletions uievents/idlharness.window.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ idl_test(
CompositionEvent: ['new CompositionEvent("event")'],
UIEvent: ['new UIEvent("event")'],
InputEvent: ['new InputEvent("event")'],
TextEvent: ['(() => { const ev = document.createEvent("TextEvent"); ev.initTextEvent("event"); return ev; })()'],
});
}
);
88 changes: 88 additions & 0 deletions uievents/textInput/api.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!doctype html>
<meta charset=utf-8>
<title>textInput: API</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<input class=test-el>
<textarea class=test-el></textarea>
<div contenteditable class=test-el></div>
<script src="support/common.js"></script>
<script>
test(() => {
assert_throws_js(TypeError, () => {
new TextEvent('textInput');
});
}, "No constructor");

test(() => {
const e = document.createEvent('TextEvent');
assert_equals(Object.getPrototypeOf(e), window.TextEvent.prototype);
assert_equals(Object.getPrototypeOf(Object.getPrototypeOf(e)), window.UIEvent.prototype);
assert_equals(Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(e))), window.Event.prototype);
}, "document.CreateEvent('TextEvent') prototype chain");

test(() => {
const e = document.createEvent('TextEvent');
assert_throws_js(TypeError, () => { e.initTextEvent(); });
}, "initTextEvent() no arguments");

test(() => {
const e = document.createEvent('TextEvent');
e.initTextEvent('foo');
assert_equals(e.type, 'foo');
assert_equals(e.bubbles, false);
assert_equals(e.cancelable, false);
assert_equals(e.view, null);
assert_equals(e.data, 'undefined');
}, "initTextEvent('foo')");

test(() => {
const e = document.createEvent('TextEvent');
e.initTextEvent('foo', true, true, window, 'bar');
assert_equals(e.type, 'foo');
assert_equals(e.bubbles, true);
assert_equals(e.cancelable, true);
assert_equals(e.view, window);
assert_equals(e.data, 'bar');
}, "initTextEvent('foo', true, true, window, 'bar')");

test(() => {
const div = document.createElement('div');
let textinputCount = 0;
let textInputCount = 0;
div.addEventListener('textinput', e => {
assert_equals(e.type, 'textinput');
textinputCount++;
});
div.addEventListener('textInput', e => {
assert_equals(e.type, 'textInput');
textInputCount++;
});
const textinputEvent = document.createEvent('TextEvent');
textinputEvent.initTextEvent('textinput');
div.dispatchEvent(textinputEvent);

const textInputEvent = document.createEvent('TextEvent');
textInputEvent.initTextEvent('textInput');
div.dispatchEvent(textInputEvent);

assert_equals(textinputCount, 1);
assert_equals(textInputCount, 1);
}, "case sensitivity: textInput vs textinput");

const els = document.querySelectorAll('.test-el');
for (const el of els) {
promise_test(t => {
return new Promise((resolve, reject) => {
el.addEventListener('textInput', reject);
el.addEventListener('input', t.step_func(e => {
const actualValue = 'value' in el ? el.value : el.textContent;
assert_equals(actualValue, 'a');
resolve();
}));
el.focus();
document.execCommand('insertText', false, 'a');
});
}, `execCommand('insertText', false, 'a'), ${elDesc(el)}` );
}
</script>
16 changes: 16 additions & 0 deletions uievents/textInput/backspace.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<meta charset=utf-8>
<title>textInput: backspace</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
[contenteditable] { border: thin inset silver; }
</style>
<p>Press Backspace in each text field below.</p>
<input class=test-el value=abc>
<textarea class=test-el>abc</textarea>
<div contenteditable=true class=test-el>abc</div>
<script src="support/common.js"></script>
<script src="support/no-textInput.sub.js?key=Backspace&selectionStart=2&selectionEnd=2&expectedValue=ac"></script>
16 changes: 16 additions & 0 deletions uievents/textInput/basic.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<meta charset=utf-8>
<title>textInput: basic</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
[contenteditable] { border: thin inset silver; }
</style>
<p>Type "a" into each text field below.</p>
<input class=test-el>
<textarea class=test-el></textarea>
<div contenteditable=true class=test-el></div>
<script src="support/common.js"></script>
<script src="support/basic.sub.js?key=a&selectionStart=0&selectionEnd=0&expectedValue=a"></script>
16 changes: 16 additions & 0 deletions uievents/textInput/delete-selection.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<meta charset=utf-8>
<title>textInput: delete selection</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
[contenteditable] { border: thin inset silver; }
</style>
<p>Press Delete (Fn+Backspace for macOS) in each text field below.</p>
<input class=test-el value=abc>
<textarea class=test-el>abc</textarea>
<div contenteditable=true class=test-el>abc</div>
<script src="support/common.js"></script>
<script src="support/no-textInput.sub.js?key=Delete&selectionStart=1&selectionEnd=2&expectedValue=ac"></script>
16 changes: 16 additions & 0 deletions uievents/textInput/delete.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<meta charset=utf-8>
<title>textInput: delete</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
[contenteditable] { border: thin inset silver; }
</style>
<p>Press Delete (Fn+Backspace for macOS) in each text field below.</p>
<input class=test-el value=abc>
<textarea class=test-el>abc</textarea>
<div contenteditable=true class=test-el>abc</div>
<script src="support/common.js"></script>
<script src="support/no-textInput.sub.js?key=Delete&selectionStart=1&selectionEnd=1&expectedValue=ac"></script>
14 changes: 14 additions & 0 deletions uievents/textInput/enter-input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<meta charset=utf-8>
<title>textInput: Enter key for input element</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
[contenteditable] { border: thin inset silver; }
</style>
<p>Press Enter in the text field below.</p>
<input class=test-el>
<script src="support/common.js"></script>
<script src="support/no-textInput.sub.js?key=Enter&selectionStart=0&selectionEnd=0&expectedValue="></script>
15 changes: 15 additions & 0 deletions uievents/textInput/enter-textarea-contenteditable.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<title>textInput: Enter key for textarea and contenteditable</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
[contenteditable] { border: thin inset silver; }
</style>
<p>Press Enter in each text field below.</p>
<textarea class=test-el></textarea>
<div contenteditable=true class=test-el></div>
<script src="support/common.js"></script>
<script src="support/basic.sub.js?key=Enter&selectionStart=0&selectionEnd=0&expectedValue=\n"></script>
17 changes: 17 additions & 0 deletions uievents/textInput/smiley-manual.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<meta charset=utf-8>
<title>textInput: smiley (manual)</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
[contenteditable] { border: thin inset silver; }
</style>
<p>Type "🙂" into each text field below.</p>
<input class=test-el>
<textarea class=test-el></textarea>
<div contenteditable=true class=test-el></div>
<script>
setup({ explicit_timeout: true });
</script>
<script src="support/common.js"></script>
<script src="support/basic.sub.js?key=🙂&selectionStart=0&selectionEnd=0&expectedValue=🙂"></script>
49 changes: 49 additions & 0 deletions uievents/textInput/support/basic.sub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const els = document.querySelectorAll('.test-el');
const key = "{{GET[key]}}";
const keyRaw = keyMapping[key] || key;
const expectedData = key === "Enter" ? "\n" : key;
const selectionStart = {{GET[selectionStart]}};
const selectionEnd = {{GET[selectionEnd]}};
const expectedValue = "{{GET[expectedValue]}}";

for (const el of els) {
promise_test(t => {
return new Promise((resolve, reject) => {
let beforeinputEvents = 0;
let textInputEvents = 0;
el.addEventListener('beforeinput', t.step_func(e => {
beforeinputEvents++;
}));
el.addEventListener('textInput', t.step_func(e => {
textInputEvents++;
assert_equals(beforeinputEvents, 1);
assert_equals(e.data, expectedData);
assert_true(e.bubbles);
assert_true(e.cancelable);
assert_equals(e.view, window);
assert_equals(e.detail, 0);
assert_true(e instanceof window.TextEvent);
}));
el.addEventListener('input', t.step_func(e => {
assert_equals(textInputEvents, 1);
if (expectedValue === "\n" && !(el instanceof HTMLInputElement) && !(el instanceof HTMLTextAreaElement)) {
// New paragraph in contenteditable during editing is weird.
// innerHTML is <div><br></div><div><br></div>
// ...but later changes to <br>
// So, check that there's at least one <br>.
assert_true(getValue(el).indexOf('<br>') > -1);
} else {
assert_equals(getValue(el), expectedValue);
}
resolve();
}));
el.onfocus = t.step_func(e => {
if (window.test_driver) {
test_driver.send_keys(el, keyRaw);
}
});
el.focus();
setSelection(el, selectionStart, selectionEnd);
});
}, `${document.title}, ${elDesc(el)}`);
}
38 changes: 38 additions & 0 deletions uievents/textInput/support/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
function elDesc(el) {
let rv = `<${el.localName}`;
if (el.hasAttribute('contenteditable')) {
rv += ` contenteditable="${el.getAttribute('contenteditable')}"`;
}
if (el.hasAttribute('type')) {
rv += ` type="${el.getAttribute('type')}"`;
}
rv += `>`;
return rv;
}

function setSelection(el, selectionStart, selectionEnd) {
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
el.selectionStart = selectionStart;
el.selectionEnd = selectionEnd;
} else {
const s = getSelection();
s.removeAllRanges();
const r = new Range();
r.setStart(el.firstChild || el, selectionStart);
r.setEnd(el.firstChild || el, selectionEnd);
s.addRange(r);
}
}

function getValue(el) {
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
return el.value;
}
return el.innerHTML;
}

const keyMapping = {
"Enter": "\uE006",
"Backspace": "\uE003",
"Delete": "\uE017",
};
29 changes: 29 additions & 0 deletions uievents/textInput/support/no-textInput.sub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const els = document.querySelectorAll('.test-el');
const key = "{{GET[key]}}";
const keyRaw = keyMapping[key] || key;
const expectedData = key === "Enter" ? "\n" : key;
const selectionStart = {{GET[selectionStart]}};
const selectionEnd = {{GET[selectionEnd]}};
const expectedValue = "{{GET[expectedValue]}}";

for (const el of els) {
promise_test(t => {
return new Promise((resolve, reject) => {
el.addEventListener('textInput', reject);
el.addEventListener('keyup', t.step_func(e => {
if (e.key !== key) {
return;
}
assert_equals(getValue(el), expectedValue);
resolve();
}));
el.onfocus = t.step_func(e => {
if (window.test_driver) {
test_driver.send_keys(el, keyRaw);
}
});
el.focus();
setSelection(el, selectionStart, selectionEnd);
});
}, `${document.title}, ${elDesc(el)}`);
}

0 comments on commit 7dcb66e

Please sign in to comment.