|
2 | 2 | // Use of this source code is governed by a BSD-style license that can be |
3 | 3 | // found in the LICENSE file. |
4 | 4 |
|
| 5 | +import * as Host from '../../core/host/host.js'; |
| 6 | +import * as Platform from '../../core/platform/platform.js'; |
| 7 | +import * as ProtocolClient from '../../core/protocol_client/protocol_client.js'; |
| 8 | +import * as SDK from '../../core/sdk/sdk.js'; |
| 9 | +import {findMenuItemWithLabel} from '../../testing/ContextMenuHelpers.js'; |
| 10 | +import {describeWithEnvironment} from '../../testing/EnvironmentHelpers.js'; |
| 11 | +import {expectCall} from '../../testing/ExpectStubCall.js'; |
| 12 | +import {stubFileManager} from '../../testing/FileManagerHelpers.js'; |
| 13 | +import {createViewFunctionStub, type ViewFunctionStub} from '../../testing/ViewFunctionHelpers.js'; |
| 14 | +import * as UI from '../../ui/legacy/legacy.js'; |
| 15 | + |
5 | 16 | import * as ProtocolMonitor from './protocol_monitor.js'; |
6 | 17 |
|
7 | | -describe('ProtocolMonitor', () => { |
| 18 | +const {InspectorBackend} = ProtocolClient; |
| 19 | +const {ProtocolMonitorImpl} = ProtocolMonitor.ProtocolMonitor; |
| 20 | +type ProtocolMonitorImpl = ProtocolMonitor.ProtocolMonitor.ProtocolMonitorImpl; |
| 21 | +const {JSONEditor} = ProtocolMonitor.JSONEditor; |
| 22 | +type JSONEditor = ProtocolMonitor.JSONEditor.JSONEditor; |
| 23 | + |
| 24 | +let view!: ViewFunctionStub<typeof ProtocolMonitorImpl>; |
| 25 | +let protocolMonitor!: ProtocolMonitorImpl; |
| 26 | +let jsonEditor!: JSONEditor; |
| 27 | +let sendRawMessageStub!: sinon.SinonStub; |
| 28 | + |
| 29 | +describeWithEnvironment('ProtocolMonitor', () => { |
| 30 | + beforeEach(() => { |
| 31 | + // sendRawMessageStub = sinon.stub(InspectorBackend.test,'sendRawMessage'); |
| 32 | + sendRawMessageStub = sinon.stub(); |
| 33 | + InspectorBackend.test.sendRawMessage = sendRawMessageStub; |
| 34 | + jsonEditor = new JSONEditor(document.createElement('div')); |
| 35 | + view = createViewFunctionStub(ProtocolMonitorImpl, {editorWidget: jsonEditor}); |
| 36 | + protocolMonitor = new ProtocolMonitorImpl(view); |
| 37 | + }); |
| 38 | + |
| 39 | + it('sends commands', async () => { |
| 40 | + view.input.onCommandSubmitted( |
| 41 | + new CustomEvent('submit', {detail: '{"command":"Test.test","parameters":{"test":"test"}}'})); |
| 42 | + assert.isTrue(sendRawMessageStub.calledOnce); |
| 43 | + assert.isTrue(sendRawMessageStub.calledOnce); |
| 44 | + assert.strictEqual(sendRawMessageStub.getCall(0).args[0], 'Test.test'); |
| 45 | + assert.deepEqual(sendRawMessageStub.getCall(0).args[1], {test: 'test'}); |
| 46 | + assert.deepEqual(sendRawMessageStub.getCall(0).args[3], ''); |
| 47 | + }); |
| 48 | + |
| 49 | + it('records commands', async () => { |
| 50 | + protocolMonitor.wasShown(); |
| 51 | + InspectorBackend.test.onMessageSent?.({domain: 'Test', method: 'Test.test', params: {test: 'test'}, id: 1}, null); |
| 52 | + assert.deepEqual((await view.nextInput).messages.map(m => ({method: m.method, params: m.params, id: m.id})), [ |
| 53 | + { |
| 54 | + method: 'Test.test', |
| 55 | + params: {test: 'test'}, |
| 56 | + id: 1, |
| 57 | + }, |
| 58 | + ]); |
| 59 | + |
| 60 | + InspectorBackend.test.onMessageReceived?.( |
| 61 | + { |
| 62 | + id: 1, |
| 63 | + method: 'Test.test', |
| 64 | + params: {test: 'test'}, |
| 65 | + requestTime: 0, |
| 66 | + result: {test: 'test'}, |
| 67 | + }, |
| 68 | + null); |
| 69 | + assert.deepEqual( |
| 70 | + (await view.nextInput).messages.map(m => ({method: m.method, params: m.params, id: m.id, result: m.result})), [ |
| 71 | + { |
| 72 | + method: 'Test.test', |
| 73 | + params: {test: 'test'}, |
| 74 | + id: 1, |
| 75 | + result: {test: 'test'}, |
| 76 | + }, |
| 77 | + ]); |
| 78 | + }); |
| 79 | + |
| 80 | + it('only records commands if recording is enabled', async () => { |
| 81 | + InspectorBackend.test.onMessageSent?.({domain: 'Test', method: 'Test.test', params: {test: 'test'}, id: 1}, null); |
| 82 | + |
| 83 | + protocolMonitor.wasShown(); |
| 84 | + InspectorBackend.test.onMessageSent?.({domain: 'Test', method: 'Test.test', params: {test: 'test'}, id: 2}, null); |
| 85 | + assert.deepEqual((await view.nextInput).messages.map(m => ({method: m.method, params: m.params, id: m.id})), [ |
| 86 | + { |
| 87 | + method: 'Test.test', |
| 88 | + params: {test: 'test'}, |
| 89 | + id: 2, |
| 90 | + }, |
| 91 | + ]); |
| 92 | + |
| 93 | + view.input.onRecord({target: {toggled: false}} as unknown as Event); |
| 94 | + InspectorBackend.test.onMessageSent?.({domain: 'Test', method: 'Test.test', params: {test: 'test'}, id: 3}, null); |
| 95 | + view.input.onRecord({target: {toggled: true}} as unknown as Event); |
| 96 | + InspectorBackend.test.onMessageSent?.({domain: 'Test', method: 'Test.test', params: {test: 'test'}, id: 4}, null); |
| 97 | + assert.deepEqual((await view.nextInput).messages.map(m => ({method: m.method, params: m.params, id: m.id})), [ |
| 98 | + { |
| 99 | + method: 'Test.test', |
| 100 | + params: {test: 'test'}, |
| 101 | + id: 2, |
| 102 | + }, |
| 103 | + { |
| 104 | + method: 'Test.test', |
| 105 | + params: {test: 'test'}, |
| 106 | + id: 4, |
| 107 | + }, |
| 108 | + ]); |
| 109 | + }); |
| 110 | + |
| 111 | + it('clears messages', async () => { |
| 112 | + protocolMonitor.wasShown(); |
| 113 | + InspectorBackend.test.onMessageSent?.({domain: 'Test', method: 'Test.test', params: {test: 'test'}, id: 2}, null); |
| 114 | + assert.lengthOf((await view.nextInput).messages, 1); |
| 115 | + |
| 116 | + view.input.onClear(); |
| 117 | + assert.lengthOf((await view.nextInput).messages, 0); |
| 118 | + }); |
| 119 | + |
| 120 | + it('saves to file', async () => { |
| 121 | + const fileManager = stubFileManager(); |
| 122 | + const fileManagerCloseCall = expectCall(fileManager.close); |
| 123 | + |
| 124 | + protocolMonitor.wasShown(); |
| 125 | + InspectorBackend.test.onMessageSent?.({domain: 'Test', method: 'Test.test', params: {test: 'test'}, id: 2}, null); |
| 126 | + |
| 127 | + const TIMESTAMP = 42; |
| 128 | + const clock = sinon.useFakeTimers(); |
| 129 | + clock.tick(TIMESTAMP); |
| 130 | + const FILENAME = 'ProtocolMonitor-' + Platform.DateUtilities.toISO8601Compact(new Date(TIMESTAMP)) + '.json' as |
| 131 | + Platform.DevToolsPath.RawPathString; |
| 132 | + |
| 133 | + (await view.nextInput).onSave(); |
| 134 | + |
| 135 | + assert.isTrue(fileManager.save.calledOnce); |
| 136 | + assert.isTrue(fileManager.save.calledOnceWith(FILENAME, '', true, false)); |
| 137 | + await fileManagerCloseCall; |
| 138 | + assert.isTrue(fileManager.append.calledOnceWith(FILENAME, sinon.match('"method": "Test.test"'))); |
| 139 | + |
| 140 | + clock.restore(); |
| 141 | + }); |
| 142 | + |
| 143 | + describe('context menu', () => { |
| 144 | + let menu!: UI.ContextMenu.ContextMenu; |
| 145 | + let element!: HTMLElement; |
| 146 | + |
| 147 | + function triggerContextMenu(index: number) { |
| 148 | + menu = new UI.ContextMenu.ContextMenu(new Event('contextmenu')); |
| 149 | + element = {dataset: {index: `${index}`}} as unknown as HTMLElement; |
| 150 | + view.input.onSelect(new CustomEvent('select', {detail: element})); |
| 151 | + view.input.onContextMenu(new CustomEvent('contextmenu', {detail: {menu, element}})); |
| 152 | + } |
| 153 | + |
| 154 | + beforeEach(() => { |
| 155 | + menu = new UI.ContextMenu.ContextMenu(new Event('contextmenu')); |
| 156 | + protocolMonitor.wasShown(); |
| 157 | + InspectorBackend.test.onMessageSent?.( |
| 158 | + {domain: 'Test', method: 'Test.test1', params: {test: 'test'}, id: 2}, null); |
| 159 | + InspectorBackend.test.onMessageSent?.( |
| 160 | + {domain: 'Test', method: 'Test.test2', params: {test: 'test'}, id: 2}, null); |
| 161 | + triggerContextMenu(1); |
| 162 | + }); |
| 163 | + |
| 164 | + it('priovides edit and resend context menu item', async () => { |
| 165 | + assert.isFalse(view.input.sidebarVisible); |
| 166 | + |
| 167 | + let editAndResend = findMenuItemWithLabel(menu.editSection(), 'Edit and resend'); |
| 168 | + assert.exists(editAndResend); |
| 169 | + menu.invokeHandler(editAndResend.id()); |
| 170 | + |
| 171 | + assert.strictEqual((await view.nextInput).command, '{"command":"Test.test2","parameters":{"test":"test"}}'); |
| 172 | + assert.isTrue(view.input.sidebarVisible); |
| 173 | + |
| 174 | + const displayCommandStub = sinon.stub(jsonEditor, 'displayCommand'); |
| 175 | + |
| 176 | + triggerContextMenu(0); |
| 177 | + editAndResend = findMenuItemWithLabel(menu.editSection(), 'Edit and resend'); |
| 178 | + assert.exists(editAndResend); |
| 179 | + menu.invokeHandler(editAndResend.id()); |
| 180 | + |
| 181 | + assert.isTrue(displayCommandStub.calledOnceWith('Test.test1', {test: 'test'}, '')); |
| 182 | + }); |
| 183 | + |
| 184 | + it('priovides filter context menu item', async () => { |
| 185 | + const filter = findMenuItemWithLabel(menu.editSection(), 'Filter'); |
| 186 | + assert.exists(filter); |
| 187 | + menu.invokeHandler(filter.id()); |
| 188 | + |
| 189 | + assert.strictEqual((await view.nextInput).filter, 'method:Test.test2'); |
| 190 | + }); |
| 191 | + |
| 192 | + it('priovides documentation context menu item', async () => { |
| 193 | + const documentation = findMenuItemWithLabel(menu.footerSection(), 'Documentation'); |
| 194 | + assert.exists(documentation); |
| 195 | + |
| 196 | + const openInNewTabStub = sinon.stub(Host.InspectorFrontendHost.InspectorFrontendHostInstance, 'openInNewTab'); |
| 197 | + menu.invokeHandler(documentation.id()); |
| 198 | + |
| 199 | + assert.isTrue( |
| 200 | + openInNewTabStub.calledOnceWith('https://chromedevtools.github.io/devtools-protocol/tot/Test#method-test2')); |
| 201 | + }); |
| 202 | + }); |
| 203 | + |
| 204 | + describe('Display command written in editor inside input bar', () => { |
| 205 | + it('should display the command edited inside the CDP editor into the input bar', async () => { |
| 206 | + jsonEditor.command = 'Test.test'; |
| 207 | + jsonEditor.parameters = [ |
| 208 | + { |
| 209 | + name: 'test', |
| 210 | + type: ProtocolMonitor.JSONEditor.ParameterType.STRING, |
| 211 | + description: 'test', |
| 212 | + optional: false, |
| 213 | + value: 'test', |
| 214 | + }, |
| 215 | + ]; |
| 216 | + view.input.onSplitChange(new CustomEvent('change', {detail: 'OnlyMain'})); |
| 217 | + assert.deepEqual((await view.nextInput).command, '{"command":"Test.test","parameters":{"test":"test"}}'); |
| 218 | + }); |
| 219 | + |
| 220 | + it('should update the selected target inside the input bar', async () => { |
| 221 | + jsonEditor.targetId = 'value2'; |
| 222 | + sinon.stub(SDK.TargetManager.TargetManager.instance(), 'targets').returns([ |
| 223 | + {id: () => 'value1'} as SDK.Target.Target, |
| 224 | + {id: () => 'value2'} as SDK.Target.Target, |
| 225 | + ]); |
| 226 | + view.input.onSplitChange(new CustomEvent('change', {detail: 'OnlyMain'})); |
| 227 | + assert.deepEqual((await view.nextInput).selectedTargetId, 'value2'); |
| 228 | + }); |
| 229 | + |
| 230 | + it('should not display the command into the input bar if the command is empty string', async () => { |
| 231 | + jsonEditor.command = ''; |
| 232 | + view.input.onSplitChange(new CustomEvent('change', {detail: 'OnlyMain'})); |
| 233 | + |
| 234 | + assert.deepEqual((await view.nextInput).command, ''); |
| 235 | + }); |
| 236 | + }); |
| 237 | + |
8 | 238 | describe('parseCommandInput', () => { |
9 | 239 | it('parses various JSON formats', async () => { |
10 | 240 | const input = { |
|
0 commit comments