github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/packages/pyroscope-flamegraph/src/FlameGraph/FlameGraphComponent/Flamegraph_render.spec.tsx (about) 1 // eslint-disable-next-line @typescript-eslint/ban-ts-comment 2 // @ts-ignore 3 import CanvasConverter from 'canvas-to-buffer'; 4 import { createCanvas } from 'canvas'; 5 import { Maybe } from 'true-myth'; 6 import TestData from './testData'; 7 import Flamegraph from './Flamegraph'; 8 import { DefaultPalette } from './colorPalette'; 9 import { configureToMatchImageSnapshot } from 'jest-image-snapshot'; 10 import type { MatchImageSnapshotOptions } from 'jest-image-snapshot'; 11 12 type focusedNodeType = ConstructorParameters<typeof Flamegraph>[2]; 13 type zoomType = ConstructorParameters<typeof Flamegraph>[5]; 14 15 expect.extend({ 16 toMatchImageSnapshot(received: string, options: MatchImageSnapshotOptions) { 17 // If these checks pass, assume we're in a JSDOM environment with the 'canvas' package. 18 if (process.env.RUN_SNAPSHOTS) { 19 const toMatchImageSnapshot = configureToMatchImageSnapshot({ 20 // Big enough threshold to account for different font rendering 21 // TODO: fix it 22 failureThreshold: 0.1, 23 failureThresholdType: 'percent', 24 }) as any; 25 26 // TODO 27 // for some reason it fails with 28 // Expected 1 arguments, but got 3. 29 // hence the any 30 return toMatchImageSnapshot.call(this, received, options); 31 } 32 33 return { 34 pass: true, 35 message: () => 36 `Skipping 'toMatchImageSnapshot' assertion since env var 'RUN_SNAPSHOTS' is not set.`, 37 }; 38 }, 39 }); 40 // All tests here refer strictly to the rendering bit of "Flamegraph" 41 describe("render group:snapshot'", () => { 42 // TODO i'm thinking here if we can simply reuse this? 43 const canvas = createCanvas(800, 0) as unknown as HTMLCanvasElement; 44 const fitMode = 'HEAD'; 45 const highlightQuery = ''; 46 const zoom: zoomType = Maybe.nothing(); 47 const focusedNode: focusedNodeType = Maybe.nothing(); 48 49 it('renders a simple flamegraph', () => { 50 const flame = new Flamegraph( 51 TestData.SimpleTree, 52 canvas, 53 focusedNode, 54 fitMode, 55 highlightQuery, 56 zoom, 57 DefaultPalette 58 ); 59 60 flame.render(); 61 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 62 }); 63 64 // this test servers to validate funcionality like collapsing nodes 65 it('renders a complex flamegraph', () => { 66 const flame = new Flamegraph( 67 TestData.ComplexTree, 68 canvas, 69 focusedNode, 70 fitMode, 71 highlightQuery, 72 zoom, 73 DefaultPalette 74 ); 75 76 flame.render(); 77 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 78 }); 79 80 it('renders a double(diff) flamegraph', () => { 81 const flame = new Flamegraph( 82 TestData.DiffTree, 83 canvas, 84 focusedNode, 85 fitMode, 86 highlightQuery, 87 zoom, 88 DefaultPalette 89 ); 90 91 flame.render(); 92 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 93 }); 94 95 it('renders a highlighted flamegraph', () => { 96 const highlightQuery = 'main'; 97 const focusedNode: focusedNodeType = Maybe.nothing(); 98 99 const flame = new Flamegraph( 100 TestData.SimpleTree, 101 canvas, 102 focusedNode, 103 fitMode, 104 highlightQuery, 105 zoom, 106 DefaultPalette 107 ); 108 109 flame.render(); 110 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 111 }); 112 113 it('renders a highlighted double flamegraph', () => { 114 const highlightQuery = 'main'; 115 const focusedNode: focusedNodeType = Maybe.nothing(); 116 117 const flame = new Flamegraph( 118 TestData.DiffTree, 119 canvas, 120 focusedNode, 121 fitMode, 122 highlightQuery, 123 zoom, 124 DefaultPalette 125 ); 126 127 flame.render(); 128 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 129 }); 130 131 it('renders a zoomed flamegraph', () => { 132 const zoom = Maybe.just({ i: 2, j: 8 }); 133 const focusedNode: focusedNodeType = Maybe.nothing(); 134 135 const flame = new Flamegraph( 136 TestData.SimpleTree, 137 canvas, 138 focusedNode, 139 fitMode, 140 highlightQuery, 141 zoom, 142 DefaultPalette 143 ); 144 145 flame.render(); 146 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 147 }); 148 149 it('renders a zoomed with fitMode="TAIL"', () => { 150 // we need a smaller canvas 151 // so that the function names don't fit 152 const canvas = createCanvas(300, 0) as unknown as HTMLCanvasElement; 153 const fitMode = 'TAIL'; 154 const focusedNode: focusedNodeType = Maybe.nothing(); 155 156 const flame = new Flamegraph( 157 TestData.SimpleTree, 158 canvas, 159 focusedNode, 160 fitMode, 161 highlightQuery, 162 zoom, 163 DefaultPalette 164 ); 165 166 flame.render(); 167 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 168 }); 169 170 describe('focused', () => { 171 it('renders a focused node in the beginning', () => { 172 const zoom: zoomType = Maybe.nothing(); 173 const focusedNode = Maybe.just({ i: 2, j: 0 }); 174 175 const flame = new Flamegraph( 176 TestData.SimpleTree, 177 canvas, 178 focusedNode, 179 fitMode, 180 highlightQuery, 181 zoom, 182 DefaultPalette 183 ); 184 185 flame.render(); 186 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 187 }); 188 189 it('renders a focused node (when node is not in the beginning)', () => { 190 const zoom: zoomType = Maybe.nothing(); 191 const focusedNode = Maybe.just({ i: 2, j: 8 }); 192 193 const flame = new Flamegraph( 194 TestData.SimpleTree, 195 canvas, 196 focusedNode, 197 fitMode, 198 highlightQuery, 199 zoom, 200 DefaultPalette 201 ); 202 203 flame.render(); 204 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 205 }); 206 207 it('also zooms', () => { 208 const focusedNode = Maybe.just({ i: 1, j: 0 }); 209 const zoom = Maybe.just({ i: 2, j: 0 }); // main.fastFunction 210 211 const flame = new Flamegraph( 212 TestData.SimpleTree, 213 canvas, 214 focusedNode, 215 fitMode, 216 highlightQuery, 217 zoom, 218 DefaultPalette 219 ); 220 221 flame.render(); 222 expect(canvasToBuffer(canvas)).toMatchImageSnapshot(); 223 }); 224 }); 225 }); 226 227 function canvasToBuffer(canvas: HTMLCanvasElement) { 228 const converter = new CanvasConverter(canvas, { 229 image: { types: ['png'] }, 230 }); 231 232 return converter.toBuffer(); 233 }