chrome-devtools-frontend
Version:
Chrome DevTools UI
280 lines (225 loc) • 12.6 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 {
createTarget,
} from '../../testing/EnvironmentHelpers.js';
import {
describeWithMockConnection,
} from '../../testing/MockConnection.js';
import * as Host from '../host/host.js';
import * as Platform from '../platform/platform.js';
import * as SDK from './sdk.js';
const {urlString} = Platform.DevToolsPath;
describeWithMockConnection('TargetManager', () => {
let targetManager: SDK.TargetManager.TargetManager;
beforeEach(() => {
targetManager = SDK.TargetManager.TargetManager.instance();
});
function resourceTreeModel(target: SDK.Target.Target): SDK.ResourceTreeModel.ResourceTreeModel {
const model = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
assert.exists(model);
return model;
}
it('allows observing targets', () => {
const observer = sinon.spy(new SDK.TargetManager.Observer());
const target1 = createTarget();
targetManager.observeTargets(observer);
assert.isTrue(observer.targetAdded.calledOnceWith(target1));
const target2 = createTarget();
assert.isTrue(observer.targetAdded.calledTwice);
assert.isTrue(observer.targetAdded.calledWith(target2));
target2.dispose('YOLO!');
assert.isTrue(observer.targetRemoved.calledOnceWith(target2));
targetManager.unobserveTargets(observer);
createTarget();
assert.isTrue(observer.targetAdded.calledTwice);
});
it('allows observing models', () => {
const observer = sinon.spy(new SDK.TargetManager.SDKModelObserver<SDK.ResourceTreeModel.ResourceTreeModel>());
const target1 = createTarget();
targetManager.observeModels(SDK.ResourceTreeModel.ResourceTreeModel, observer);
assert.isTrue(observer.modelAdded.calledOnceWith(resourceTreeModel(target1)));
const target2 = createTarget();
assert.isTrue(observer.modelAdded.calledTwice);
assert.isTrue(observer.modelAdded.calledWith(resourceTreeModel(target2)));
target2.dispose('YOLO!');
assert.isTrue(observer.modelRemoved.calledOnceWith(resourceTreeModel(target2)));
targetManager.unobserveModels(SDK.ResourceTreeModel.ResourceTreeModel, observer);
createTarget();
assert.isTrue(observer.modelAdded.calledTwice);
});
it('allows listening to models', () => {
const WillReloadPage = SDK.ResourceTreeModel.Events.WillReloadPage;
const thisObject = {};
const listener = sinon.spy();
const target1 = createTarget();
targetManager.addModelListener(SDK.ResourceTreeModel.ResourceTreeModel, WillReloadPage, listener, thisObject);
resourceTreeModel(target1).dispatchEventToListeners(WillReloadPage);
assert.isTrue(listener.calledOnce);
assert.isTrue(listener.calledOn(thisObject));
const target2 = createTarget();
resourceTreeModel(target2).dispatchEventToListeners(WillReloadPage);
assert.isTrue(listener.calledTwice);
assert.isTrue(listener.calledOn(thisObject));
targetManager.removeModelListener(SDK.ResourceTreeModel.ResourceTreeModel, WillReloadPage, listener, thisObject);
resourceTreeModel(target1).dispatchEventToListeners(WillReloadPage);
assert.isTrue(listener.calledTwice);
});
it('allows observing targets in scope', () => {
const target1 = createTarget();
const target2 = createTarget();
targetManager.setScopeTarget(target1);
const observer = sinon.spy(new SDK.TargetManager.Observer());
targetManager.observeTargets(observer, {scoped: true});
assert.isTrue(observer.targetAdded.calledOnceWith(target1));
createTarget({parentTarget: target2});
assert.isTrue(observer.targetAdded.calledOnceWith(target1));
const subtarget1 = createTarget({parentTarget: target1});
assert.isTrue(observer.targetAdded.calledTwice);
assert.isTrue(observer.targetAdded.calledWith(subtarget1));
});
it('allows observing models in scope', () => {
const target1 = createTarget();
const target2 = createTarget();
targetManager.setScopeTarget(target1);
const observer = sinon.spy(new SDK.TargetManager.SDKModelObserver<SDK.ResourceTreeModel.ResourceTreeModel>());
targetManager.observeModels(SDK.ResourceTreeModel.ResourceTreeModel, observer, {scoped: true});
assert.isTrue(observer.modelAdded.calledOnceWith(resourceTreeModel(target1)));
createTarget({parentTarget: target2});
assert.isTrue(observer.modelAdded.calledOnce);
const subtarget1 = createTarget({parentTarget: target1});
assert.isTrue(observer.modelAdded.calledTwice);
assert.isTrue(observer.modelAdded.calledWith(resourceTreeModel(subtarget1)));
});
it('calls second observers even if the first is changing the scope', () => {
const target1 = createTarget();
const target2 = createTarget();
targetManager.setScopeTarget(target1);
const observer1 = sinon.stub(new SDK.TargetManager.SDKModelObserver<SDK.ResourceTreeModel.ResourceTreeModel>());
observer1.modelRemoved.callsFake(() => targetManager.setScopeTarget(target2));
const observer2 = sinon.spy(new SDK.TargetManager.SDKModelObserver<SDK.RuntimeModel.RuntimeModel>());
targetManager.observeModels(SDK.ResourceTreeModel.ResourceTreeModel, observer1, {scoped: true});
targetManager.observeModels(SDK.RuntimeModel.RuntimeModel, observer2, {scoped: true});
target1.dispose('YOLO!');
assert.isTrue(observer1.modelRemoved.calledOnce);
assert.isTrue(observer2.modelRemoved.calledOnce);
});
it('allows listening to models in scope', () => {
const WillReloadPage = SDK.ResourceTreeModel.Events.WillReloadPage;
const listener = sinon.spy();
const target1 = createTarget();
targetManager.setScopeTarget(target1);
const thisObject = {};
targetManager.addModelListener(
SDK.ResourceTreeModel.ResourceTreeModel, WillReloadPage, listener, thisObject, {scoped: true});
resourceTreeModel(target1).dispatchEventToListeners(WillReloadPage);
assert.isTrue(listener.calledOnce);
assert.isTrue(listener.calledOn(thisObject));
const target2 = createTarget();
resourceTreeModel(target2).dispatchEventToListeners(WillReloadPage);
assert.isTrue(listener.calledOnce);
const subtarget1 = createTarget({parentTarget: target1});
resourceTreeModel(subtarget1).dispatchEventToListeners(WillReloadPage);
assert.isTrue(listener.calledTwice);
targetManager.setScopeTarget(target2);
resourceTreeModel(target1).dispatchEventToListeners(WillReloadPage);
assert.isTrue(listener.calledTwice);
resourceTreeModel(target2).dispatchEventToListeners(WillReloadPage);
assert.isTrue(listener.calledThrice);
});
it('can transition between scopes', () => {
const target1 = createTarget();
const target2 = createTarget();
const targetObserver = sinon.spy(new SDK.TargetManager.Observer());
const modelObserver = sinon.spy(new SDK.TargetManager.SDKModelObserver<SDK.ResourceTreeModel.ResourceTreeModel>());
const scopeChangeListener = sinon.spy();
targetManager.observeTargets(targetObserver, {scoped: true});
targetManager.observeModels(SDK.ResourceTreeModel.ResourceTreeModel, modelObserver, {scoped: true});
targetManager.addScopeChangeListener(scopeChangeListener);
assert.isTrue(targetObserver.targetAdded.calledOnceWith(target1));
assert.isTrue(modelObserver.modelAdded.calledOnce);
assert.isFalse(targetObserver.targetRemoved.called);
assert.isFalse(modelObserver.modelRemoved.called);
assert.isFalse(scopeChangeListener.called);
targetObserver.targetAdded.resetHistory();
modelObserver.modelAdded.resetHistory();
targetManager.setScopeTarget(target2);
assert.isTrue(targetObserver.targetRemoved.calledOnceWith(target1));
assert.isTrue(modelObserver.modelRemoved.calledOnce);
assert.isTrue(targetObserver.targetAdded.calledOnceWith(target2));
assert.isTrue(modelObserver.modelAdded.calledOnce);
assert.isTrue(targetObserver.targetAdded.calledAfter(targetObserver.targetRemoved));
assert.isTrue(modelObserver.modelAdded.calledAfter(modelObserver.modelRemoved));
assert.isTrue(scopeChangeListener.called);
targetObserver.targetAdded.resetHistory();
targetObserver.targetRemoved.resetHistory();
modelObserver.modelAdded.resetHistory();
modelObserver.modelRemoved.resetHistory();
scopeChangeListener.resetHistory();
targetManager.setScopeTarget(null);
assert.isFalse(targetObserver.targetAdded.called);
assert.isFalse(modelObserver.modelAdded.calledOnce);
assert.isTrue(targetObserver.targetRemoved.calledOnceWith(target1));
assert.isTrue(modelObserver.modelRemoved.called);
assert.isTrue(scopeChangeListener.called);
targetObserver.targetAdded.resetHistory();
targetObserver.targetRemoved.resetHistory();
modelObserver.modelAdded.resetHistory();
modelObserver.modelRemoved.resetHistory();
scopeChangeListener.resetHistory();
const target3 = createTarget();
assert.isFalse(targetObserver.targetAdded.called);
assert.isFalse(modelObserver.modelAdded.called);
assert.isFalse(scopeChangeListener.called);
targetManager.setScopeTarget(target3);
assert.isTrue(targetObserver.targetAdded.called);
assert.isTrue(modelObserver.modelAdded.called);
assert.isTrue(scopeChangeListener.called);
});
it('short-cicuits setting the same scope target', () => {
const target1 = createTarget();
targetManager.setScopeTarget(target1);
const targetObserver = sinon.spy(new SDK.TargetManager.Observer());
const modelObserver = sinon.spy(new SDK.TargetManager.SDKModelObserver<SDK.ResourceTreeModel.ResourceTreeModel>());
targetObserver.targetAdded.resetHistory();
targetObserver.targetRemoved.resetHistory();
modelObserver.modelAdded.resetHistory();
modelObserver.modelRemoved.resetHistory();
targetManager.setScopeTarget(target1);
assert.isFalse(targetObserver.targetAdded.called);
assert.isFalse(modelObserver.modelAdded.called);
assert.isFalse(targetObserver.targetRemoved.called);
assert.isFalse(modelObserver.modelRemoved.called);
});
it('notifies about inspected URL change', () => {
const targets = [createTarget(), createTarget()];
const inspectedURLChangedHostApi =
sinon.spy(Host.InspectorFrontendHost.InspectorFrontendHostInstance, 'inspectedURLChanged');
const inspectedURLChangedEventListener = sinon.spy();
targetManager.addEventListener(SDK.TargetManager.Events.INSPECTED_URL_CHANGED, inspectedURLChangedEventListener);
targetManager.setScopeTarget(null);
assert.isTrue(inspectedURLChangedHostApi.notCalled && inspectedURLChangedEventListener.notCalled);
targets.forEach(t => t.setInspectedURL(urlString`${`https://a.com/${t.id()}`}`));
assert.isTrue(inspectedURLChangedHostApi.notCalled && inspectedURLChangedEventListener.notCalled);
targetManager.setScopeTarget(targets[0]);
assert.isTrue(inspectedURLChangedHostApi.calledOnce && inspectedURLChangedEventListener.calledOnce);
assert.strictEqual(inspectedURLChangedHostApi.lastCall.firstArg, `https://a.com/${targets[0].id()}`);
assert.strictEqual(inspectedURLChangedEventListener.lastCall.firstArg.data, targets[0]);
targetManager.setScopeTarget(targets[0]);
assert.isTrue(inspectedURLChangedHostApi.calledOnce && inspectedURLChangedEventListener.calledOnce);
targets.forEach(t => t.setInspectedURL(urlString`${`https://b.com/${t.id()}`}`));
assert.isTrue(inspectedURLChangedHostApi.calledTwice && inspectedURLChangedEventListener.calledTwice);
assert.strictEqual(inspectedURLChangedHostApi.lastCall.firstArg, `https://b.com/${targets[0].id()}`);
assert.strictEqual(inspectedURLChangedEventListener.lastCall.firstArg.data, targets[0]);
targetManager.setScopeTarget(targets[1]);
assert.isTrue(inspectedURLChangedHostApi.calledThrice && inspectedURLChangedEventListener.calledThrice);
assert.strictEqual(inspectedURLChangedHostApi.lastCall.firstArg, `https://b.com/${targets[1].id()}`);
assert.strictEqual(inspectedURLChangedEventListener.lastCall.firstArg.data, targets[1]);
targets.forEach(t => t.setInspectedURL(urlString`${`https://c.com/${t.id()}`}`));
assert.strictEqual(inspectedURLChangedHostApi.callCount, 4);
assert.strictEqual(inspectedURLChangedEventListener.callCount, 4);
assert.strictEqual(inspectedURLChangedHostApi.lastCall.firstArg, `https://c.com/${targets[1].id()}`);
assert.strictEqual(inspectedURLChangedEventListener.lastCall.firstArg.data, targets[1]);
});
});