import {ProjectFixture} from 'sentry-fixture/project';
import {render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary';
import {ViewHierarchy} from '.';
// Mocks for useVirtualizedTree hook
class ResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
}
window.ResizeObserver = ResizeObserver;
window.Element.prototype.scrollTo = jest.fn();
window.Element.prototype.scrollIntoView = jest.fn();
const DEFAULT_VALUES = {alpha: 1, height: 1, width: 1, x: 1, y: 1, visible: true};
const DEFAULT_MOCK_DATA = {
rendering_system: 'test-rendering-system',
windows: [
{
...DEFAULT_VALUES,
type: 'Container',
identifier: 'test_identifier',
x: 200,
children: [
{
...DEFAULT_VALUES,
type: 'Nested Container',
x: 10,
y: 10,
width: 3,
height: 4,
identifier: 'nested',
children: [
{
...DEFAULT_VALUES,
type: 'Text',
children: [],
},
],
},
],
},
],
};
describe('View Hierarchy', function () {
let MOCK_DATA;
let project;
beforeEach(() => {
MOCK_DATA = DEFAULT_MOCK_DATA;
project = ProjectFixture();
});
it('can continue make selections for inspecting data', async function () {
render();
// 1 for the tree node, 1 for the details panel header
expect(screen.getAllByText('Container - test_identifier')).toHaveLength(2);
await userEvent.click(screen.getByText('Nested Container - nested'));
// 1 for the tree node, 1 for the details panel header
expect(screen.getAllByText('Nested Container - nested')).toHaveLength(2);
// Only visible in the tree node
expect(screen.getByText('Container - test_identifier')).toBeInTheDocument();
await userEvent.click(screen.getByText('Text'));
// 1 for the tree node, 1 for the details panel header, 1 for the details value
expect(screen.getAllByText('Text')).toHaveLength(3);
// Only visible in the tree node
expect(screen.getByText('Nested Container - nested')).toBeInTheDocument();
});
it('can expand and collapse by clicking the icon', async function () {
render();
expect(screen.queryByText('Text')).toBeInTheDocument();
await userEvent.click(
within(screen.getByLabelText('Nested Container - nested')).getByRole('button', {
name: 'Collapse',
})
);
expect(screen.queryByText('Text')).not.toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: 'Expand'}));
expect(screen.queryByText('Text')).toBeInTheDocument();
});
it('can navigate with keyboard shortcuts after a selection', async function () {
render();
await userEvent.click(screen.getAllByText('Container - test_identifier')[0]);
await userEvent.keyboard('{ArrowDown}');
// 1 for the tree node, 1 for the details panel header
expect(screen.getAllByText('Nested Container - nested')).toHaveLength(2);
});
it('can expand/collapse with the keyboard', async function () {
render();
await userEvent.click(screen.getAllByText('Nested Container - nested')[0]);
await userEvent.keyboard('{Enter}');
expect(screen.queryByText('Text')).not.toBeInTheDocument();
await userEvent.keyboard('{Enter}');
expect(screen.getByText('Text')).toBeInTheDocument();
});
it('can render multiple windows together', function () {
MOCK_DATA.windows = [
...MOCK_DATA.windows,
{
...DEFAULT_VALUES,
type: 'Second Window',
children: [
{
...DEFAULT_VALUES,
type: 'Second Window Child',
children: [],
},
],
},
];
render();
expect(screen.getByText('Second Window')).toBeInTheDocument();
expect(screen.getByText('Second Window Child')).toBeInTheDocument();
});
it('does not render the wireframe for the Unity platform', function () {
const mockUnityProject = ProjectFixture({platform: 'unity'});
render();
expect(screen.queryByTestId('view-hierarchy-wireframe')).not.toBeInTheDocument();
});
it('draws the selected node when a tree selection is made', async function () {
render();
const canvas = screen.getByTestId(
'view-hierarchy-wireframe-overlay'
) as HTMLCanvasElement;
const context = canvas.getContext('2d');
if (!context) {
throw new Error('Canvas context is not defined');
}
expect(context.fillRect).not.toHaveBeenCalledWith(210, 11, 3, 4);
await userEvent.click(screen.getByText('Nested Container - nested'));
// This is the nested container, the x, y positions are shifted by the parent
expect(context.fillRect).toHaveBeenCalledWith(210, 11, 3, 4);
});
it('does not render a wireframe selection initially', function () {
render();
const canvas = screen.getByTestId(
'view-hierarchy-wireframe-overlay'
) as HTMLCanvasElement;
const context = canvas.getContext('2d');
if (!context) {
throw new Error('Canvas context is not defined');
}
// The overlay should not have rendered anything before any interactions
expect(context.fillRect).not.toHaveBeenCalled();
});
it('renders an empty state if there is no data in windows to visualize', function () {
render(
);
expect(
screen.getByText('There is no view hierarchy data to visualize')
).toBeInTheDocument();
});
it('renders with depth markers', function () {
render();
});
it('renders an icon with a tooltip for the rendering system', async function () {
MOCK_DATA.rendering_system = 'flutter';
render();
await userEvent.hover(screen.getByTestId('rendering-system-icon'));
expect(await screen.findByText('Rendering System: flutter')).toBeInTheDocument();
});
});