github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/packages/pyroscope-flamegraph/src/Toolbar.spec.tsx (about) 1 import React from 'react'; 2 import { render, screen } from '@testing-library/react'; 3 import userEvent from '@testing-library/user-event'; 4 import { Maybe } from 'true-myth'; 5 import Toolbar from './Toolbar'; 6 import { HeadMode, TailMode } from './fitMode/fitMode'; 7 8 // since 'react-debounce-input' uses lodash.debounce under the hood 9 jest.mock('lodash.debounce', () => 10 jest.fn((fn) => { 11 fn.flush = () => {}; 12 return fn; 13 }) 14 ); 15 16 describe('ProfileHeader', () => { 17 beforeAll(() => { 18 window.HTMLElement.prototype.getBoundingClientRect = function () { 19 return { 20 x: 0, 21 y: 0, 22 bottom: 0, 23 right: 0, 24 toJSON: () => {}, 25 height: 0, 26 top: 0, 27 left: 0, 28 width: 900, 29 }; 30 }; 31 }); 32 33 it('should render toolbar correctly', () => { 34 const { asFragment } = render( 35 <Toolbar 36 view="both" 37 flamegraphType="single" 38 handleSearchChange={() => {}} 39 reset={() => {}} 40 updateFitMode={() => {}} 41 fitMode={HeadMode} 42 updateView={() => {}} 43 isFlamegraphDirty={false} 44 selectedNode={Maybe.nothing()} 45 onFocusOnSubtree={() => {}} 46 highlightQuery="" 47 /> 48 ); 49 50 expect(screen.getByRole('toolbar')).toBeInTheDocument(); 51 expect(asFragment()).toMatchSnapshot(); 52 }); 53 54 describe('Reset button', () => { 55 const onReset = jest.fn(); 56 57 beforeEach(() => {}); 58 59 afterEach(() => { 60 jest.clearAllMocks(); 61 }); 62 63 it('renders as disabled when flamegraph is not dirty', () => { 64 const component = ( 65 <Toolbar 66 view="both" 67 flamegraphType="single" 68 isFlamegraphDirty={false} 69 handleSearchChange={() => {}} 70 reset={onReset} 71 updateFitMode={() => {}} 72 fitMode={HeadMode} 73 updateView={() => {}} 74 selectedNode={Maybe.nothing()} 75 onFocusOnSubtree={() => {}} 76 highlightQuery="" 77 /> 78 ); 79 render(component); 80 expect(screen.getByRole('button', { name: /Reset/ })).toBeDisabled(); 81 }); 82 83 it('calls onReset when clicked (and enabled)', () => { 84 const component = ( 85 <Toolbar 86 view="both" 87 flamegraphType="single" 88 isFlamegraphDirty 89 handleSearchChange={() => {}} 90 reset={onReset} 91 updateFitMode={() => {}} 92 fitMode={HeadMode} 93 updateView={() => {}} 94 selectedNode={Maybe.nothing()} 95 onFocusOnSubtree={() => {}} 96 highlightQuery="" 97 /> 98 ); 99 render(component); 100 expect(screen.getByRole('button', { name: /Reset/ })).not.toBeDisabled(); 101 screen.getByRole('button', { name: /Reset/ }).click(); 102 103 expect(onReset).toHaveBeenCalled(); 104 }); 105 }); 106 107 describe('HighlightSearch', () => { 108 it('calls callback when typed', () => { 109 const onChange = jest.fn(); 110 111 const component = ( 112 <Toolbar 113 view="both" 114 flamegraphType="single" 115 isFlamegraphDirty 116 handleSearchChange={onChange} 117 reset={() => {}} 118 updateFitMode={() => {}} 119 fitMode={HeadMode} 120 updateView={() => {}} 121 selectedNode={Maybe.nothing()} 122 onFocusOnSubtree={() => {}} 123 highlightQuery="" 124 /> 125 ); 126 127 render(component); 128 userEvent.type(screen.getByRole('searchbox'), 'foobar'); 129 expect(onChange).toHaveBeenCalledWith('foobar'); 130 }); 131 }); 132 133 describe('FitMode', () => { 134 const updateFitMode = jest.fn(); 135 const component = ( 136 <Toolbar 137 view="both" 138 flamegraphType="single" 139 handleSearchChange={() => {}} 140 reset={() => {}} 141 updateFitMode={updateFitMode} 142 fitMode={HeadMode} 143 updateView={() => {}} 144 isFlamegraphDirty={false} 145 selectedNode={Maybe.nothing()} 146 onFocusOnSubtree={() => {}} 147 highlightQuery="" 148 /> 149 ); 150 151 beforeEach(() => { 152 render(component); 153 }); 154 155 afterEach(() => { 156 jest.clearAllMocks(); 157 }); 158 159 it('updates to HEAD first', () => { 160 screen.getByRole('button', { name: 'Head first' }).click(); 161 162 expect(updateFitMode).toHaveBeenCalledWith(HeadMode); 163 }); 164 165 it('updates to TAIL first', () => { 166 screen.getByRole('button', { name: 'Tail first' }).click(); 167 168 expect(updateFitMode).toHaveBeenCalledWith(TailMode); 169 }); 170 }); 171 172 describe('Focus on subtree', () => { 173 it('renders as disabled when theres no selected node', () => { 174 const component = ( 175 <Toolbar 176 view="both" 177 flamegraphType="single" 178 isFlamegraphDirty={false} 179 handleSearchChange={() => {}} 180 reset={() => {}} 181 updateFitMode={() => {}} 182 fitMode={HeadMode} 183 updateView={() => {}} 184 selectedNode={Maybe.nothing()} 185 onFocusOnSubtree={() => {}} 186 highlightQuery="" 187 /> 188 ); 189 render(component); 190 expect(screen.getByRole('button', { name: /Collapse/ })).toBeDisabled(); 191 }); 192 193 it('calls callback when clicked', () => { 194 const onFocusOnSubtree = jest.fn(); 195 const component = ( 196 <Toolbar 197 view="both" 198 flamegraphType="single" 199 isFlamegraphDirty={false} 200 handleSearchChange={() => {}} 201 reset={() => {}} 202 updateFitMode={() => {}} 203 fitMode={HeadMode} 204 updateView={() => {}} 205 selectedNode={Maybe.just({ i: 999, j: 999 })} 206 onFocusOnSubtree={onFocusOnSubtree} 207 highlightQuery="" 208 /> 209 ); 210 211 render(component); 212 screen.getByRole('button', { name: /Collapse/ }).click(); 213 214 expect(onFocusOnSubtree).toHaveBeenCalledWith(999, 999); 215 }); 216 }); 217 });