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  }