chrome-devtools-frontend
Version:
Chrome DevTools UI
143 lines (116 loc) • 5.93 kB
text/typescript
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Common from '../../core/common/common.js';
import * as SDK from '../../core/sdk/sdk.js';
import type * as Protocol from '../../generated/protocol.js';
import {
createTarget,
registerNoopActions,
} from '../../testing/EnvironmentHelpers.js';
import {
describeWithMockConnection,
dispatchEvent,
} from '../../testing/MockConnection.js';
import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
import type * as TextEditor from '../../ui/components/text_editor/text_editor.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as Console from './console.js';
describeWithMockConnection('ConsoleContextSelector', () => {
let target: SDK.Target.Target;
let consolePrompt: Console.ConsolePrompt.ConsolePrompt;
let keyBinding: CodeMirror.KeyBinding[];
let evaluateOnTarget: sinon.SinonStub;
let editor: TextEditor.TextEditor.TextEditor;
beforeEach(() => {
registerNoopActions(['console.clear', 'console.clear.history', 'console.create-pin']);
const keymapOf = sinon.spy(CodeMirror.keymap, 'of');
consolePrompt = new Console.ConsolePrompt.ConsolePrompt();
sinon.assert.called(keymapOf);
keyBinding = keymapOf.firstCall.firstArg;
const editorContainer = consolePrompt.element.querySelector('.console-prompt-editor-container');
editor = editorContainer!.firstElementChild as TextEditor.TextEditor.TextEditor;
editor.state = {doc: 'foo', selection: {main: {head: 42}}} as unknown as CodeMirror.EditorState;
editor.dispatch = () => {};
target = createTarget();
const targetContext = createExecutionContext(target);
UI.Context.Context.instance().setFlavor(SDK.RuntimeModel.ExecutionContext, targetContext);
evaluateOnTarget = sinon.stub(target.runtimeAgent(), 'invoke_evaluate');
});
let id = 0;
function createExecutionContext(target: SDK.Target.Target): SDK.RuntimeModel.ExecutionContext {
++id;
dispatchEvent(target, 'Runtime.executionContextCreated', {
context: {
id,
origin: 'http://example.com',
name: `c${id}`,
uniqueId: `c${id}`,
auxData: {
frameId: 'f2',
},
},
});
const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel);
assert.exists(runtimeModel);
const executionContext = runtimeModel.executionContext(id);
assert.exists(executionContext);
return executionContext;
}
function compileScriptResponse(exception?: string): Protocol.Runtime.CompileScriptResponse {
const exceptionDetails = exception ? {exception: {description: exception}} : undefined;
return {exceptionDetails, getError: () => {}} as unknown as Protocol.Runtime.CompileScriptResponse;
}
it('evaluates on enter', async () => {
const enterBinding = keyBinding.find(b => b.key === 'Enter');
sinon.stub(target.runtimeAgent(), 'invoke_compileScript').resolves(compileScriptResponse());
enterBinding!.run!({} as CodeMirror.EditorView);
await new Promise(resolve => setTimeout(resolve, 0));
sinon.assert.called(evaluateOnTarget);
});
it('allows user to enable pasting by typing \'allow pasting\'', async () => {
const setting = Common.Settings.Settings.instance().createSetting(
'disable-self-xss-warning', false, Common.Settings.SettingStorageType.SYNCED);
assert.isFalse(setting.get());
const enterBinding = keyBinding.find(b => b.key === 'Enter');
sinon.stub(target.runtimeAgent(), 'invoke_compileScript').resolves(compileScriptResponse());
consolePrompt.showSelfXssWarning();
enterBinding!.run!({} as CodeMirror.EditorView);
await new Promise(resolve => setTimeout(resolve, 0));
assert.isFalse(setting.get());
consolePrompt.showSelfXssWarning();
editor.state = {doc: 'allow pasting', selection: {main: {head: 42}}} as unknown as CodeMirror.EditorState;
enterBinding!.run!({} as CodeMirror.EditorView);
await new Promise(resolve => setTimeout(resolve, 0));
assert.isTrue(setting.get());
});
it('does not evaluate incomplete expression', async () => {
const enterBinding = keyBinding.find(b => b.key === 'Enter');
sinon.stub(target.runtimeAgent(), 'invoke_compileScript')
.resolves(compileScriptResponse('SyntaxError: Unexpected end of input'));
enterBinding!.run!({} as CodeMirror.EditorView);
await new Promise(resolve => setTimeout(resolve, 0));
sinon.assert.notCalled(evaluateOnTarget);
});
it('evaluate incomplete expression if forced', async () => {
const ctrlEnterBinding = keyBinding.find(b => b.key === 'Ctrl-Enter');
sinon.stub(target.runtimeAgent(), 'invoke_compileScript')
.resolves(compileScriptResponse('SyntaxError: Unexpected end of input'));
ctrlEnterBinding!.run!({} as CodeMirror.EditorView);
await new Promise(resolve => setTimeout(resolve, 0));
sinon.assert.called(evaluateOnTarget);
});
it('does not evaluate if the current context has changed', async () => {
const anotherTarget = createTarget();
const anotherTargetContext = createExecutionContext(target);
const evaluateOnAnotherTarget = sinon.stub(anotherTarget.runtimeAgent(), 'invoke_evaluate');
const enterBinding = keyBinding.find(b => b.key === 'Enter');
sinon.stub(target.runtimeAgent(), 'invoke_compileScript').resolves(compileScriptResponse());
sinon.stub(anotherTarget.runtimeAgent(), 'invoke_compileScript').resolves(compileScriptResponse());
enterBinding!.run!({} as CodeMirror.EditorView);
UI.Context.Context.instance().setFlavor(SDK.RuntimeModel.ExecutionContext, anotherTargetContext);
await new Promise(resolve => setTimeout(resolve, 0));
sinon.assert.notCalled(evaluateOnAnotherTarget);
sinon.assert.notCalled(evaluateOnTarget);
});
});