diff --git a/addon-test-support/@ember/test-helpers/dom/-is-focusable.ts b/addon-test-support/@ember/test-helpers/dom/-is-focusable.ts index 3b72f7449..06627a2a5 100644 --- a/addon-test-support/@ember/test-helpers/dom/-is-focusable.ts +++ b/addon-test-support/@ember/test-helpers/dom/-is-focusable.ts @@ -1,14 +1,12 @@ import isFormControl from './-is-form-control'; -import { isDocument } from './-target'; +import { isDocument, isContentEditable } from './-target'; const FOCUSABLE_TAGS = ['A']; type FocusableElement = HTMLAnchorElement; // eslint-disable-next-line require-jsdoc -function isFocusableElement( - element: HTMLElement | SVGElement | Element -): element is FocusableElement { +function isFocusableElement(element: Element): element is FocusableElement { return FOCUSABLE_TAGS.indexOf(element.tagName) > -1; } @@ -24,11 +22,11 @@ export default function isFocusable( return false; } - if ( - isFormControl(element) || - (element as HTMLElement).isContentEditable || - isFocusableElement(element) - ) { + if (isFormControl(element)) { + return !element.disabled; + } + + if (isContentEditable(element) || isFocusableElement(element)) { return true; } diff --git a/addon-test-support/@ember/test-helpers/dom/-target.ts b/addon-test-support/@ember/test-helpers/dom/-target.ts index 294ffb4c6..350d8e903 100644 --- a/addon-test-support/@ember/test-helpers/dom/-target.ts +++ b/addon-test-support/@ember/test-helpers/dom/-target.ts @@ -11,3 +11,8 @@ export function isElement(target: any): target is Element { export function isDocument(target: any): target is Document { return target.nodeType === Node.DOCUMENT_NODE; } + +// eslint-disable-next-line require-jsdoc +export function isContentEditable(element: Element): element is HTMLElement { + return 'isContentEditable' in element && (element as HTMLElement).isContentEditable; +} diff --git a/addon-test-support/@ember/test-helpers/dom/fill-in.ts b/addon-test-support/@ember/test-helpers/dom/fill-in.ts index 232ae5c07..eb50c39ae 100644 --- a/addon-test-support/@ember/test-helpers/dom/fill-in.ts +++ b/addon-test-support/@ember/test-helpers/dom/fill-in.ts @@ -4,7 +4,7 @@ import { __focus__ } from './focus'; import settled from '../settled'; import fireEvent from './fire-event'; import { nextTickPromise } from '../-utils'; -import Target from './-target'; +import Target, { isContentEditable } from './-target'; import { log } from '@ember/test-helpers/dom/-logging'; /** @@ -32,25 +32,29 @@ export default function fillIn(target: Target, text: string): Promise { throw new Error('Must pass an element or selector to `fillIn`.'); } - let element = getElement(target) as any; + let element = getElement(target) as Element | HTMLElement; if (!element) { throw new Error(`Element not found when calling \`fillIn('${target}')\`.`); } - let isControl = isFormControl(element); - if (!isControl && !element.isContentEditable) { - throw new Error('`fillIn` is only usable on form controls or contenteditable elements.'); - } if (typeof text === 'undefined' || text === null) { throw new Error('Must provide `text` when calling `fillIn`.'); } - __focus__(element); + if (isFormControl(element)) { + if (element.disabled) { + throw new Error(`Can not \`fillIn\` disabled '${target}'.`); + } + + __focus__(element); - if (isControl) { element.value = text; - } else { + } else if (isContentEditable(element)) { + __focus__(element); + element.innerHTML = text; + } else { + throw new Error('`fillIn` is only usable on form controls or contenteditable elements.'); } fireEvent(element, 'input'); diff --git a/addon-test-support/@ember/test-helpers/dom/type-in.ts b/addon-test-support/@ember/test-helpers/dom/type-in.ts index 6f8bf4944..10a0daeef 100644 --- a/addon-test-support/@ember/test-helpers/dom/type-in.ts +++ b/addon-test-support/@ember/test-helpers/dom/type-in.ts @@ -3,7 +3,6 @@ import settled from '../settled'; import getElement from './-get-element'; import isFormControl, { FormControl } from './-is-form-control'; import { __focus__ } from './focus'; -import isFocusable from './-is-focusable'; import { Promise } from 'rsvp'; import fireEvent from './fire-event'; import Target from './-target'; @@ -58,12 +57,14 @@ export default function typeIn(target: Target, text: string, options: Options = throw new Error('Must provide `text` when calling `typeIn`.'); } - let { delay = 50 } = options; - - if (isFocusable(element)) { - __focus__(element); + if (element.disabled) { + throw new Error(`Can not \`typeIn\` disabled '${target}'.`); } + __focus__(element); + + let { delay = 50 } = options; + return fillOut(element, text, delay) .then(() => fireEvent(element, 'change')) .then(settled); diff --git a/tests/unit/dom/fill-in-test.js b/tests/unit/dom/fill-in-test.js index cfd7cdf9d..56dd8a3a9 100644 --- a/tests/unit/dom/fill-in-test.js +++ b/tests/unit/dom/fill-in-test.js @@ -44,6 +44,26 @@ module('DOM Helper: fillIn', function (hooks) { ); }); + test('filling in a disabled element', async function (assert) { + element = buildInstrumentedElement('input'); + element.dataset.testDisabled = ''; + element.setAttribute('disabled', true); + + await setupContext(context); + + assert.rejects( + fillIn(`[data-test-disabled]`, 'foo'), + new Error("Can not `fillIn` disabled '[data-test-disabled]'."), + 'renders user selector' + ); + + assert.rejects( + fillIn(element, 'foo'), + new Error("Can not `fillIn` disabled '[object HTMLInputElement]'."), + 'renders Element instance' + ); + }); + test('rejects if selector is not found', async function (assert) { element = buildInstrumentedElement('div'); diff --git a/tests/unit/dom/type-in-test.js b/tests/unit/dom/type-in-test.js index a508a63b3..e23cdeced 100644 --- a/tests/unit/dom/type-in-test.js +++ b/tests/unit/dom/type-in-test.js @@ -70,7 +70,7 @@ module('DOM Helper: typeIn', function (hooks) { document.getElementById('ember-testing').innerHTML = ''; }); - test('filling in an input', async function (assert) { + test('typing in an input', async function (assert) { element = buildInstrumentedElement('input'); await typeIn(element, 'foo'); @@ -100,7 +100,7 @@ module('DOM Helper: typeIn', function (hooks) { assert.verifySteps(expectedEventsWithArguments); }); - test('filling in an input with a delay', async function (assert) { + test('typing in an input with a delay', async function (assert) { element = buildInstrumentedElement('input'); await typeIn(element, 'foo', { delay: 150 }); @@ -109,7 +109,7 @@ module('DOM Helper: typeIn', function (hooks) { assert.equal(element.value, 'foo'); }); - test('filling in a textarea', async function (assert) { + test('typing in a textarea', async function (assert) { element = buildInstrumentedElement('textarea'); await typeIn(element, 'foo'); @@ -118,13 +118,30 @@ module('DOM Helper: typeIn', function (hooks) { assert.equal(element.value, 'foo'); }); - test('filling in a non-fillable element', async function (assert) { + test('typing in not a form control', async function (assert) { element = buildInstrumentedElement('div'); await setupContext(context); assert.rejects(typeIn(`#${element.id}`, 'foo'), /`typeIn` is only usable on form controls/); }); + test('typing in a disabled element', async function (assert) { + element = buildInstrumentedElement('input'); + element.dataset.testDisabled = ''; + element.setAttribute('disabled', ''); + + await setupContext(context); + assert.rejects( + typeIn(`[data-test-disabled]`, 'foo'), + new Error("Can not `typeIn` disabled '[data-test-disabled]'.") + ); + + assert.rejects( + typeIn(element, 'foo'), + new Error("Can not `typeIn` disabled '[object HTMLInputElement]'.") + ); + }); + test('rejects if selector is not found', async function (assert) { element = buildInstrumentedElement('div');