blob: 577ee71ea76eb57b93d50e89a7d5a5a612b9f87a [file] [log] [blame]
/**
* @license
* Copyright 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../test/common-test-setup';
import './gr-textarea';
import {fixture, html, assert} from '@open-wc/testing';
import {waitForEventOnce} from '../utils/event-util';
import {AUTOCOMPLETE_HINT_CLASS, GrTextarea} from './gr-textarea';
import {CursorPositionChangeEventDetail} from '../api/embed';
async function rafPromise() {
return new Promise(res => {
requestAnimationFrame(res);
});
}
suite('gr-textarea test', () => {
let element: GrTextarea;
setup(async () => {
element = await fixture(html` <gr-textarea> </gr-textarea>`);
});
test('text area is registered correctly', () => {
assert.instanceOf(element, GrTextarea);
});
test('when disabled textarea have contenteditable set to false', async () => {
element.disabled = true;
await element.updateComplete;
const editableDiv = element.shadowRoot!.querySelector('.editableDiv');
await element.updateComplete;
assert.equal(editableDiv?.getAttribute('contenteditable'), 'false');
});
test('when disabled textarea have aria-disabled set', async () => {
element.disabled = true;
await element.updateComplete;
const editableDiv = element.shadowRoot!.querySelector('.editableDiv');
await element.updateComplete;
assert.isDefined(editableDiv?.getAttribute('aria-disabled'));
});
test('when textarea has placeholder, set aria-placeholder to placeholder text', async () => {
const placeholder = 'A sample placehodler...';
element.placeholder = placeholder;
await element.updateComplete;
const editableDiv = element.shadowRoot!.querySelector('.editableDiv');
await element.updateComplete;
assert.equal(editableDiv?.getAttribute('aria-placeholder'), placeholder);
});
test('renders the value', async () => {
const value = 'Some value';
element.value = value;
await element.updateComplete;
const editableDiv = element.shadowRoot!.querySelector(
'.editableDiv'
) as HTMLDivElement;
await element.updateComplete;
assert.equal(editableDiv?.innerText, value);
});
test('streams change event when editable div has input event', async () => {
const value = 'Some value \n other value';
const INPUT_EVENT = 'input';
let changeCalled = false;
element.addEventListener(INPUT_EVENT, () => {
changeCalled = true;
});
const changeEventPromise = waitForEventOnce(element, INPUT_EVENT);
const editableDiv = element.shadowRoot!.querySelector(
'.editableDiv'
) as HTMLDivElement;
editableDiv.innerText = value;
editableDiv.dispatchEvent(new Event('input'));
await changeEventPromise;
assert.isTrue(changeCalled);
});
test('does not have focus by default', async () => {
assert.isFalse(element.isFocused);
});
test('when focused, isFocused is set to true', async () => {
await element.focus();
assert.isTrue(element.isFocused);
});
test('when cursor position is set to 0', async () => {
const CURSOR_POSITION_CHANGE_EVENT = 'cursorPositionChange';
let cursorPosition = -1;
const cursorPositionChangeEventPromise = waitForEventOnce(
element,
CURSOR_POSITION_CHANGE_EVENT
);
element.addEventListener(CURSOR_POSITION_CHANGE_EVENT, (event: Event) => {
const detail = (event as CustomEvent<CursorPositionChangeEventDetail>)
.detail;
cursorPosition = detail.position;
});
element.setCursorPosition(0);
await cursorPositionChangeEventPromise;
assert.equal(cursorPosition, 0);
});
test('when cursor position is set to 1', async () => {
const CURSOR_POSITION_CHANGE_EVENT = 'cursorPositionChange';
let cursorPosition = -1;
const cursorPositionChangeEventPromise = waitForEventOnce(
element,
CURSOR_POSITION_CHANGE_EVENT
);
element.addEventListener(CURSOR_POSITION_CHANGE_EVENT, (event: Event) => {
const detail = (event as CustomEvent<CursorPositionChangeEventDetail>)
.detail;
cursorPosition = detail.position;
});
element.value = 'Some value';
await element.updateComplete;
element.setCursorPosition(1);
await cursorPositionChangeEventPromise;
assert.equal(cursorPosition, 1);
});
test('when cursor position is set to new line', async () => {
const CURSOR_POSITION_CHANGE_EVENT = 'cursorPositionChange';
let cursorPosition = -1;
const cursorPositionChangeEventPromise = waitForEventOnce(
element,
CURSOR_POSITION_CHANGE_EVENT
);
element.addEventListener(CURSOR_POSITION_CHANGE_EVENT, (event: Event) => {
const detail = (event as CustomEvent<CursorPositionChangeEventDetail>)
.detail;
cursorPosition = detail.position;
});
element.value = 'Some \n\n\n value';
await element.updateComplete;
element.setCursorPosition(7);
await cursorPositionChangeEventPromise;
assert.equal(cursorPosition, 7);
});
test('when textarea is empty, placeholder hint is shown', async () => {
const editableDiv = element.shadowRoot!.querySelector(
'.editableDiv'
) as HTMLDivElement;
const placeholderHint = 'Some value';
element.placeholderHint = placeholderHint;
await element.updateComplete;
assert.equal(editableDiv?.dataset['placeholder'], placeholderHint);
});
test('when TAB is pressed, placeholder hint is added as content', async () => {
const editableDiv = element.shadowRoot!.querySelector(
'.editableDiv'
) as HTMLDivElement;
const placeholderHint = 'Some value';
element.placeholderHint = placeholderHint;
await element.updateComplete;
editableDiv.dispatchEvent(new KeyboardEvent('keydown', {key: 'Tab'}));
await element.updateComplete;
assert.equal(element.value, placeholderHint);
});
test('when cursor is at end, hint is shown', async () => {
const editableDiv = element.shadowRoot!.querySelector(
'.editableDiv'
) as HTMLDivElement;
const oldValue = 'Hola';
const hint = 'amigos';
element.hint = hint;
await element.updateComplete;
element.value = oldValue;
await element.putCursorAtEnd();
await element.updateComplete;
editableDiv.dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
await element.updateComplete;
await rafPromise();
const spanHintElement = editableDiv?.querySelector(
'.' + AUTOCOMPLETE_HINT_CLASS
) as HTMLSpanElement;
const styles = window.getComputedStyle(spanHintElement, ':before');
assert.equal(styles['content'], '"' + hint + '"');
});
test('when TAB is pressed, hint is added as content', async () => {
const editableDiv = element.shadowRoot!.querySelector(
'.editableDiv'
) as HTMLDivElement;
const oldValue = 'Hola';
const hint = 'amigos';
element.hint = hint;
element.value = oldValue;
await element.updateComplete;
await element.putCursorAtEnd();
editableDiv.dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
await rafPromise();
editableDiv.dispatchEvent(new KeyboardEvent('keydown', {key: 'Tab'}));
await element.updateComplete;
assert.equal(element.value, oldValue + hint);
});
test('when cursor is at end, Mod + ArrowRight does not change cursor position', async () => {
const CURSOR_POSITION_CHANGE_EVENT = 'cursorPositionChange';
let cursorPosition = -1;
const value = 'Hola amigos';
const editableDiv = element.shadowRoot!.querySelector(
'.editableDiv'
) as HTMLDivElement;
element.addEventListener(CURSOR_POSITION_CHANGE_EVENT, (event: Event) => {
const detail = (event as CustomEvent<CursorPositionChangeEventDetail>)
.detail;
cursorPosition = detail.position;
});
await element.updateComplete;
element.value = value;
await element.putCursorAtEnd();
await element.updateComplete;
editableDiv.dispatchEvent(
new KeyboardEvent('keydown', {key: 'ArrowRight', metaKey: true})
);
await element.updateComplete;
await rafPromise();
assert.equal(cursorPosition, value.length);
});
test('when cursor is at 0, Mod + ArrowLeft does not change cursor position', async () => {
const CURSOR_POSITION_CHANGE_EVENT = 'cursorPositionChange';
let cursorPosition = -1;
const value = 'Hola amigos';
const editableDiv = element.shadowRoot!.querySelector(
'.editableDiv'
) as HTMLDivElement;
element.addEventListener(CURSOR_POSITION_CHANGE_EVENT, (event: Event) => {
const detail = (event as CustomEvent<CursorPositionChangeEventDetail>)
.detail;
cursorPosition = detail.position;
});
await element.updateComplete;
element.value = value;
element.setCursorPosition(0);
await element.updateComplete;
editableDiv.dispatchEvent(
new KeyboardEvent('keydown', {key: 'ArrowLeft', metaKey: true})
);
await element.updateComplete;
await rafPromise();
assert.equal(cursorPosition, 0);
});
test('when TAB is pressed inside a code snippet, a tab is added', async () => {
const editableDiv = element.shadowRoot!.querySelector(
'.editableDiv'
) as HTMLDivElement;
const textContent = 'Some text\n```\ncode snippet\n```\nMore text';
element.value = textContent;
await element.updateComplete;
// Set cursor position inside the code snippet
element.setCursorPosition(19); // Position after 'code '
editableDiv.dispatchEvent(new KeyboardEvent('keydown', {key: 'Tab'}));
await element.updateComplete;
await rafPromise();
const expectedValue = 'Some text\n```\ncode \tsnippet\n```\nMore text';
assert.equal(element.value, expectedValue);
});
});