import {mat3, vec2} from 'gl-matrix'; import type {ViewHierarchyWindow} from 'sentry/components/events/viewHierarchy'; import { calculateScale, getDeepestNodeAtPoint, getHierarchyDimensions, } from 'sentry/components/events/viewHierarchy/utils'; import {defined} from 'sentry/utils'; import {Rect} from 'sentry/utils/profiling/speedscope'; const LEAF_NODE = { x: 2, y: 2, width: 5, height: 5, }; const INTERMEDIATE_NODE = { x: 10, y: 5, width: 10, height: 10, children: [LEAF_NODE], }; const DEFAULT_MOCK_HIERARCHY = [ { x: 0, y: 0, width: 10, height: 10, children: [INTERMEDIATE_NODE], }, {x: 10, y: 0, width: 20, height: 20}, ] as ViewHierarchyWindow[]; describe('View Hierarchy Utils', function () { let MOCK_HIERARCHY: ViewHierarchyWindow[]; beforeEach(function () { MOCK_HIERARCHY = [...DEFAULT_MOCK_HIERARCHY]; }); describe('getHierarchyDimensions', function () { it('properly calculates coordinates and shifts children by default', function () { const actual = getHierarchyDimensions(MOCK_HIERARCHY); expect(actual).toEqual({ nodes: [ {node: MOCK_HIERARCHY[0], rect: new Rect(0, 0, 10, 10)}, {node: INTERMEDIATE_NODE, rect: new Rect(10, 5, 10, 10)}, {node: LEAF_NODE, rect: new Rect(12, 7, 5, 5)}, {node: MOCK_HIERARCHY[1], rect: new Rect(10, 0, 20, 20)}, ], maxWidth: 30, maxHeight: 20, }); }); it('does not shift children when specified', function () { const actual = getHierarchyDimensions(MOCK_HIERARCHY, true); // One array for each root expect(actual).toEqual({ nodes: [ {node: MOCK_HIERARCHY[0], rect: new Rect(0, 0, 10, 10)}, {node: INTERMEDIATE_NODE, rect: new Rect(10, 5, 10, 10)}, {node: LEAF_NODE, rect: new Rect(2, 2, 5, 5)}, {node: MOCK_HIERARCHY[1], rect: new Rect(10, 0, 20, 20)}, ], maxWidth: 30, maxHeight: 20, }); }); }); describe('calculateScale', function () { let maxCoordinateDimensions: {height: number; width: number}, actual: number, border: {x: number; y: number}; const bounds = { height: 10, width: 10, }; beforeEach(function () { border = {x: 0, y: 0}; }); it('zooms in on small content', function () { maxCoordinateDimensions = {height: 2, width: 2}; actual = calculateScale(bounds, maxCoordinateDimensions, border); expect(actual).toEqual(5); }); it('shrinks larger content', function () { maxCoordinateDimensions = {height: 20, width: 20}; actual = calculateScale(bounds, maxCoordinateDimensions, border); expect(actual).toEqual(0.5); }); it('works with an irregular size where height is the dominant factor', function () { maxCoordinateDimensions = {height: 20, width: 2}; actual = calculateScale(bounds, maxCoordinateDimensions, border); expect(actual).toEqual(0.5); }); it('works with an irregular size where width is the dominant factor', function () { maxCoordinateDimensions = {height: 10, width: 32}; actual = calculateScale(bounds, maxCoordinateDimensions, border); expect(actual).toEqual(0.3125); }); it('factors in the border', function () { maxCoordinateDimensions = {height: 20, width: 20}; border = {x: 2, y: 2}; actual = calculateScale(bounds, maxCoordinateDimensions, border); expect(actual).toEqual(0.4); }); }); describe('getDeepestNodeAtPoint', function () { beforeEach(function () { MOCK_HIERARCHY = [ { x: 0, y: 0, width: 10, height: 10, children: [INTERMEDIATE_NODE], }, ] as ViewHierarchyWindow[]; }); it('returns the deepest node at a point', function () { const hierarchyDimensions = getHierarchyDimensions(MOCK_HIERARCHY); const actual = getDeepestNodeAtPoint( hierarchyDimensions.nodes, [10, 5], mat3.create(), 1 ); if (!defined(actual)) { throw new Error('Expected a node to be returned'); } expect(actual.node).toEqual(INTERMEDIATE_NODE); }); it('returns the deepest node at a point with a scale', function () { const hierarchyDimensions = getHierarchyDimensions(MOCK_HIERARCHY); const actual = getDeepestNodeAtPoint( hierarchyDimensions.nodes, [24, 14], mat3.create(), 0.5 ); if (!defined(actual)) { throw new Error('Expected a node to be returned'); } expect(actual.node).toEqual(LEAF_NODE); }); it('returns the deepest node at a point with a translation', function () { const hierarchyDimensions = getHierarchyDimensions(MOCK_HIERARCHY); const actual = getDeepestNodeAtPoint( hierarchyDimensions.nodes, [22, 17], mat3.translate(mat3.create(), mat3.create(), vec2.fromValues(10, 10)), // Translate by 10, 10 1 ); if (!defined(actual)) { throw new Error('Expected a node to be returned'); } expect(actual.node).toEqual(LEAF_NODE); }); it('returns the root if the root is clicked', function () { const hierarchyDimensions = getHierarchyDimensions([...DEFAULT_MOCK_HIERARCHY]); const actual = getDeepestNodeAtPoint( hierarchyDimensions.nodes, [0, 0], mat3.create(), 1 ); if (!defined(actual)) { throw new Error('Expected a node to be returned'); } expect(actual.node).toEqual(MOCK_HIERARCHY[0]); }); it('returns null if no node is found', function () { const hierarchyDimensions = getHierarchyDimensions(MOCK_HIERARCHY); const actual = getDeepestNodeAtPoint( hierarchyDimensions.nodes, [100, 100], // The click is outside of any nodes mat3.create(), 1 ); expect(actual).toBeNull(); }); }); });