mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 13:44:39 +08:00
This overrides the default CodeMirror input logic to put more text in the textarea. CodeMirror uses an offscreen <textarea> to detect input. Due to inconsistencies in the many browsers it supports, it simplifies things by regularly checking if something is in the <textarea>, adding those characters to the document, and then clearing the <textarea>. This breaks assistive technology that wants to read from CodeMirror, because the <textarea> that they interact with is constantly empty. Because we target up-to-date Chrome, we can guarantee consistent input events. This lets us leave the current line from the editor in our <textarea>. CodeMirror still expects a mostly empty <textarea>, so we pass CodeMirror a fake <textarea> that only contains the user's input. Change-Id: I025a1937bfd66fa574fd692da079a5888d9cb529 Reviewed-on: https://chromium-review.googlesource.com/1020193 Commit-Queue: Joel Einbinder <einbinder@chromium.org> Reviewed-by: Andrey Lushnikov <lushnikov@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#564347} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: d06dd5982b810b4c3576388ef3a31f060685dff7
231 lines
6.5 KiB
JavaScript
231 lines
6.5 KiB
JavaScript
// Copyright 2017 The Chromium Authors. All
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
/**
|
|
* @fileoverview using private properties isn't a Closure violation in tests.
|
|
* @suppress {accessControls}
|
|
*/
|
|
|
|
SourcesTestRunner.createTestEditor = function(clientHeight, textEditorDelegate) {
|
|
const textEditor =
|
|
new SourceFrame.SourcesTextEditor(textEditorDelegate || new SourceFrame.SourcesTextEditorDelegate());
|
|
clientHeight = clientHeight || 100;
|
|
textEditor.element.style.height = clientHeight + 'px';
|
|
textEditor.element.style.flex = 'none';
|
|
textEditor.show(UI.inspectorView.element);
|
|
return textEditor;
|
|
};
|
|
|
|
function textWithSelection(text, selections) {
|
|
if (!selections.length)
|
|
return text;
|
|
|
|
function lineWithCursor(line, column, cursorChar) {
|
|
return line.substring(0, column) + cursorChar + line.substring(column);
|
|
}
|
|
|
|
const lines = text.split('\n');
|
|
selections.sort(TextUtils.TextRange.comparator);
|
|
|
|
for (let i = selections.length - 1; i >= 0; --i) {
|
|
let selection = selections[i];
|
|
selection = selection.normalize();
|
|
const endCursorChar = (selection.isEmpty() ? '|' : '<');
|
|
lines[selection.endLine] = lineWithCursor(lines[selection.endLine], selection.endColumn, endCursorChar);
|
|
|
|
if (!selection.isEmpty())
|
|
lines[selection.startLine] = lineWithCursor(lines[selection.startLine], selection.startColumn, '>');
|
|
}
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
SourcesTestRunner.dumpTextWithSelection = function(textEditor, dumpWhiteSpaces) {
|
|
let text = textWithSelection(textEditor.text(), textEditor.selections());
|
|
|
|
if (dumpWhiteSpaces)
|
|
text = text.replace(/ /g, '.');
|
|
|
|
TestRunner.addResult(text);
|
|
};
|
|
|
|
SourcesTestRunner.setLineSelections = function(editor, selections) {
|
|
const coords = [];
|
|
|
|
for (let i = 0; i < selections.length; ++i) {
|
|
const selection = selections[i];
|
|
|
|
if (typeof selection.column === 'number') {
|
|
selection.from = selection.column;
|
|
selection.to = selection.column;
|
|
}
|
|
|
|
coords.push(new TextUtils.TextRange(selection.line, selection.from, selection.line, selection.to));
|
|
}
|
|
|
|
editor.setSelections(coords);
|
|
};
|
|
|
|
SourcesTestRunner.typeIn = function(editor, typeText, callback) {
|
|
callback = callback || new Function();
|
|
const noop = new Function();
|
|
|
|
for (let charIndex = 0; charIndex < typeText.length; ++charIndex) {
|
|
const iterationCallback = (charIndex + 1 === typeText.length ? callback : noop);
|
|
|
|
switch (typeText[charIndex]) {
|
|
case '\n':
|
|
SourcesTestRunner.fakeKeyEvent(editor, 'Enter', null, iterationCallback);
|
|
break;
|
|
case 'L':
|
|
SourcesTestRunner.fakeKeyEvent(editor, 'ArrowLeft', null, iterationCallback);
|
|
break;
|
|
case 'R':
|
|
SourcesTestRunner.fakeKeyEvent(editor, 'ArrowRight', null, iterationCallback);
|
|
break;
|
|
case 'U':
|
|
SourcesTestRunner.fakeKeyEvent(editor, 'ArrowUp', null, iterationCallback);
|
|
break;
|
|
case 'D':
|
|
SourcesTestRunner.fakeKeyEvent(editor, 'ArrowDown', null, iterationCallback);
|
|
break;
|
|
default:
|
|
SourcesTestRunner.fakeKeyEvent(editor, typeText[charIndex], null, iterationCallback);
|
|
}
|
|
}
|
|
};
|
|
|
|
const eventCodes = {
|
|
Enter: 13,
|
|
Home: 36,
|
|
ArrowLeft: 37,
|
|
ArrowUp: 38,
|
|
ArrowRight: 39,
|
|
ArrowDown: 40
|
|
};
|
|
|
|
function createCodeMirrorFakeEvent(editor, eventType, code, charCode, modifiers) {
|
|
function eventPreventDefault() {
|
|
this._handled = true;
|
|
}
|
|
|
|
const event = {
|
|
_handled: false,
|
|
type: eventType,
|
|
keyCode: code,
|
|
charCode: charCode,
|
|
preventDefault: eventPreventDefault,
|
|
stopPropagation: function() {},
|
|
target: editor._codeMirror.display.input.textarea
|
|
};
|
|
|
|
if (modifiers) {
|
|
for (let i = 0; i < modifiers.length; ++i)
|
|
event[modifiers[i]] = true;
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
function fakeCodeMirrorKeyEvent(editor, eventType, code, charCode, modifiers) {
|
|
const event = createCodeMirrorFakeEvent(editor, eventType, code, charCode, modifiers);
|
|
|
|
switch (eventType) {
|
|
case 'keydown':
|
|
editor._codeMirror.triggerOnKeyDown(event);
|
|
break;
|
|
case 'keypress':
|
|
editor._codeMirror.triggerOnKeyPress(event);
|
|
break;
|
|
case 'keyup':
|
|
editor._codeMirror.triggerOnKeyUp(event);
|
|
break;
|
|
default:
|
|
throw new Error('Unknown KeyEvent type');
|
|
}
|
|
|
|
return event._handled;
|
|
}
|
|
|
|
function fakeCodeMirrorInputEvent(editor, character) {
|
|
if (typeof character !== 'string')
|
|
return;
|
|
const input = editor._codeMirror.display.input;
|
|
const value = input.textarea.value;
|
|
const newValue =
|
|
value.substring(0, input.textarea.selectionStart) + character + value.substring(input.textarea.selectionEnd);
|
|
const caretPosition = input.textarea.selectionStart + character.length;
|
|
input.textarea.value = newValue;
|
|
input.textarea.setSelectionRange(caretPosition, caretPosition);
|
|
input.poll();
|
|
}
|
|
|
|
SourcesTestRunner.fakeKeyEvent = function(editor, originalCode, modifiers, callback) {
|
|
modifiers = modifiers || [];
|
|
let code;
|
|
let charCode;
|
|
|
|
if (originalCode === '\'') {
|
|
code = 222;
|
|
charCode = 0;
|
|
} else if (originalCode === '"') {
|
|
code = 222;
|
|
modifiers.push('shiftKey');
|
|
charCode = 34;
|
|
} else if (originalCode === '(') {
|
|
code = '9'.charCodeAt(0);
|
|
modifiers.push('shiftKey');
|
|
charCode = originalCode.charCodeAt(0);
|
|
}
|
|
|
|
code = code || eventCodes[originalCode] || originalCode;
|
|
|
|
if (typeof code === 'string')
|
|
code = code.charCodeAt(0);
|
|
|
|
if (fakeCodeMirrorKeyEvent(editor, 'keydown', code, charCode, modifiers)) {
|
|
callback();
|
|
return;
|
|
}
|
|
|
|
if (fakeCodeMirrorKeyEvent(editor, 'keypress', code, charCode, modifiers)) {
|
|
callback();
|
|
return;
|
|
}
|
|
|
|
const inputReadPromise = new Promise(x => editor._codeMirror.on('inputRead', x));
|
|
fakeCodeMirrorInputEvent(editor, originalCode);
|
|
fakeCodeMirrorKeyEvent(editor, 'keyup', code, charCode, modifiers);
|
|
inputReadPromise.then(callback);
|
|
};
|
|
|
|
SourcesTestRunner.dumpSelectionStats = function(textEditor) {
|
|
const listHashMap = {};
|
|
const sortedKeys = [];
|
|
const selections = textEditor.selections();
|
|
|
|
for (let i = 0; i < selections.length; ++i) {
|
|
const selection = selections[i];
|
|
const text = textEditor.text(selection);
|
|
|
|
if (!listHashMap[text]) {
|
|
listHashMap[text] = 1;
|
|
sortedKeys.push(text);
|
|
} else {
|
|
++listHashMap[text];
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < sortedKeys.length; ++i) {
|
|
let keyName = sortedKeys[i];
|
|
|
|
if (!keyName.length)
|
|
keyName = '<Empty string>';
|
|
else
|
|
keyName = '\'' + keyName + '\'';
|
|
|
|
TestRunner.addResult(keyName + ': ' + listHashMap[sortedKeys[i]]);
|
|
}
|
|
};
|