blob: b18682353a4f9e8aec7302e7b19a1aefbf9c8f37 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import type * as Common from '../../core/common/common.js';
import type * as Platform from '../../core/platform/platform.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as Protocol from '../../generated/protocol.js';
import {renderElementIntoDOM} from '../../testing/DOMHelpers.js';
import {createTarget, stubNoopSettings} from '../../testing/EnvironmentHelpers.js';
import {
describeWithMockConnection,
setMockConnectionResponseHandler,
} from '../../testing/MockConnection.js';
import {createViewFunctionStub} from '../../testing/ViewFunctionHelpers.js';
import * as ObjectUI from '../../ui/legacy/components/object_ui/object_ui.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as Elements from './elements.js';
const NODE_ID = 1 as Protocol.DOM.NodeId;
describeWithMockConnection('PropertiesWidget', () => {
let target: SDK.Target.Target;
let view: Elements.PropertiesWidget.PropertiesWidget;
beforeEach(() => {
stubNoopSettings();
target = createTarget();
setMockConnectionResponseHandler(
'DOM.getDocument', () => ({root: {nodeId: NODE_ID}} as Protocol.DOM.GetDocumentResponse));
setMockConnectionResponseHandler('DOM.getNodesForSubtreeByStyle', () => ({nodeIds: []}));
});
afterEach(() => {
view.detach();
});
const updatesUiOnEvent = <T extends keyof SDK.DOMModel.EventTypes>(
event: Platform.TypeScriptUtilities.NoUnion<T>, inScope: boolean) => async () => {
SDK.TargetManager.TargetManager.instance().setScopeTarget(inScope ? target : null);
const model = target.model(SDK.DOMModel.DOMModel);
assert.exists(model);
const node = new SDK.DOMModel.DOMNode(model);
sinon.stub(node, 'resolveToObject').withArgs('properties-sidebar-pane').resolves({
getAllProperties: () => ({}),
getOwnProperties: () => ({}),
arrayLength: () => 0,
} as unknown as SDK.RemoteObject.RemoteObject);
UI.Context.Context.instance().setFlavor(SDK.DOMModel.DOMNode, node);
view = new Elements.PropertiesWidget.PropertiesWidget();
renderElementIntoDOM(view);
await view.updateComplete;
const populateWithProperties =
sinon.spy(ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement, 'populateWithProperties');
model.dispatchEventToListeners(
event, ...[node] as unknown as Common.EventTarget.EventPayloadToRestParameters<SDK.DOMModel.EventTypes, T>);
await view.updateComplete;
assert.strictEqual(populateWithProperties.called, inScope);
};
it('updates UI on in scope attribute modified event', updatesUiOnEvent(SDK.DOMModel.Events.AttrModified, true));
it('does not update UI on out of scope attribute modified event',
updatesUiOnEvent(SDK.DOMModel.Events.AttrModified, false));
it('updates UI on in scope attribute removed event', updatesUiOnEvent(SDK.DOMModel.Events.AttrRemoved, true));
it('does not update UI on out of scope attribute removed event',
updatesUiOnEvent(SDK.DOMModel.Events.AttrModified, false));
it('updates UI on in scope charachter data modified event',
updatesUiOnEvent(SDK.DOMModel.Events.CharacterDataModified, true));
it('does not update UI on out of scope charachter data modified event',
updatesUiOnEvent(SDK.DOMModel.Events.CharacterDataModified, false));
it('updates UI on in scope child node count updated event',
updatesUiOnEvent(SDK.DOMModel.Events.ChildNodeCountUpdated, true));
it('does not update UI on out of scope child node count updated event',
updatesUiOnEvent(SDK.DOMModel.Events.ChildNodeCountUpdated, false));
it('invokes a getter when clicking on the invoke button', async () => {
SDK.TargetManager.TargetManager.instance().setScopeTarget(target);
const model = target.model(SDK.DOMModel.DOMModel);
assert.exists(model);
const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel);
assert.exists(runtimeModel);
const node = new SDK.DOMModel.DOMNode(model);
const object = runtimeModel.createRemoteObject({
type: Protocol.Runtime.RemoteObjectType.Object,
subtype: Protocol.Runtime.RemoteObjectSubtype.Null,
objectId: '1' as Protocol.Runtime.RemoteObjectId,
});
setMockConnectionResponseHandler('Runtime.getProperties', () => ({
result: [
{
name: 'myGetter',
isOwn: true,
enumerable: true,
configurable: true,
get: {
type: Protocol.Runtime.RemoteObjectType.Function,
objectId: '2' as Protocol.Runtime.RemoteObjectId,
className: 'Function',
description: 'get myGetter()',
},
},
],
}));
const callFunctionOn = sinon.stub().resolves({
result: {
type: Protocol.Runtime.RemoteObjectType.Object,
subtype: Protocol.Runtime.RemoteObjectSubtype.Null,
value: null,
},
});
setMockConnectionResponseHandler('Runtime.callFunctionOn', callFunctionOn);
sinon.stub(node, 'resolveToObject').withArgs('properties-sidebar-pane').resolves(object);
UI.Context.Context.instance().setFlavor(SDK.DOMModel.DOMNode, node);
const viewFunction = createViewFunctionStub(Elements.PropertiesWidget.PropertiesWidget);
view = new Elements.PropertiesWidget.PropertiesWidget(viewFunction);
renderElementIntoDOM(view);
await viewFunction.nextInput;
// Wait for the property widgets to update
await UI.Widget.Widget.allUpdatesComplete;
const {treeOutlineElement} = viewFunction.input;
const treeShadowRoot = treeOutlineElement.shadowRoot;
assert.exists(treeShadowRoot);
const invokeButton = treeShadowRoot.querySelector('.object-value-calculate-value-button');
assert.exists(invokeButton);
(invokeButton as HTMLElement).click();
sinon.assert.calledWith(callFunctionOn, sinon.match({
objectId: '1',
arguments: sinon.match([{objectId: '2'}]),
}));
});
});