blob: 3692ad07a46948feb84253b505aea067baefb7d7 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chai';
import type * as SDK from '../../core/sdk/sdk.js';
import {assertScreenshot, renderElementIntoDOM} from '../../testing/DOMHelpers.js';
import {describeWithEnvironment} from '../../testing/EnvironmentHelpers.js';
import {createViewFunctionStub} from '../../testing/ViewFunctionHelpers.js';
import * as RenderCoordinator from '../../ui/components/render_coordinator/render_coordinator.js';
import * as LayerViewer from './layer_viewer.js';
function createMockLayerTree() {
const rootLayer = {
id: () => '1',
width: () => 800,
height: () => 600,
drawsContent: () => true,
gpuMemoryUsage: () => 1024 * 1024,
parent: () => null,
nodeForSelfOrAncestor: () => null,
} as unknown as SDK.LayerTreeBase.Layer;
const childLayer = {
id: () => '2',
width: () => 400,
height: () => 300,
drawsContent: () => true,
gpuMemoryUsage: () => 512 * 1024,
parent: () => rootLayer,
nodeForSelfOrAncestor: () => null,
} as unknown as SDK.LayerTreeBase.Layer;
const layerTree = {
contentRoot: () => rootLayer,
root: () => rootLayer,
forEachLayer: (callback: (layer: SDK.LayerTreeBase.Layer) => void) => {
callback(rootLayer);
callback(childLayer);
},
} as SDK.LayerTreeBase.LayerTreeBase;
return {rootLayer, childLayer, layerTree};
}
describeWithEnvironment('LayerTreeOutline', () => {
it('renders a layer tree', async () => {
const {rootLayer, childLayer} = createMockLayerTree();
const treeData = [{
layer: rootLayer,
isExpanded: true,
children: [{
layer: childLayer,
isExpanded: false,
children: [],
}],
}];
const viewInput = {
treeData,
hoveredLayer: null,
selectedLayer: rootLayer,
layerCount: 2,
totalLayerMemory: 1024 * 1024 + 512 * 1024,
onSelect: () => {},
onHover: () => {},
onContextMenu: () => {},
};
const target = document.createElement('div');
renderElementIntoDOM(target, {includeCommonStyles: true});
LayerViewer.LayerTreeOutline.DEFAULT_VIEW(viewInput, {}, target);
await RenderCoordinator.done();
const tree = target.querySelector('devtools-tree');
if (tree) {
const outline = tree.getInternalTreeOutlineForTest();
await outline.firstChild()?.expandRecursively();
}
await assertScreenshot('layer_viewer/layer_tree_outline.png');
});
it('handles hover and selection of layers', async () => {
const layerViewHost = new LayerViewer.LayerViewHost.LayerViewHost();
const view = createViewFunctionStub(LayerViewer.LayerTreeOutline.LayerTreeOutline);
const treeOutline = new LayerViewer.LayerTreeOutline.LayerTreeOutline(layerViewHost, view);
renderElementIntoDOM(treeOutline);
const {childLayer, layerTree} = createMockLayerTree();
treeOutline.setLayerTree(layerTree);
// Wait for the initial render triggered by wasShown / setLayerTree
let viewInput = await view.nextInput;
assert.strictEqual(viewInput.layerCount, 2);
// Simulate hover over the child layer
viewInput.onHover(childLayer);
viewInput = await view.nextInput;
assert.strictEqual(viewInput.hoveredLayer, childLayer, 'hovered layer should be updated');
// Simulate mouse leave
viewInput.onHover(null);
viewInput = await view.nextInput;
assert.isNull(viewInput.hoveredLayer, 'hovered layer should be null after mouse leave');
// Simulate selecting the child layer
viewInput.onSelect(childLayer);
viewInput = await view.nextInput;
assert.strictEqual(viewInput.selectedLayer, childLayer, 'selected layer should be updated');
assert.strictEqual(layerViewHost.selection()?.layer(), childLayer,
'layerViewHost selection should be the child layer');
});
});