github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/cypress/integration/webapp/basic.ts (about)

     1  const BAR_HEIGHT = 21.5;
     2  
     3  // / <reference types="cypress" />
     4  describe('basic test', () => {
     5    beforeEach(function () {
     6      const basePath = Cypress.env('basePath') || '';
     7  
     8      cy.intercept(`${basePath}/api/apps`, {
     9        fixture: 'appNames.json',
    10      }).as('appNames');
    11    });
    12  
    13    it('changes app via the application dropdown', () => {
    14      cy.visit('/');
    15      cy.wait(`@appNames`);
    16  
    17      cy.get('.navbar').findAllByTestId('toggler').click();
    18  
    19      // For some reason couldn't find the appropriate query
    20      cy.findAllByRole('menuitem').then((items) => {
    21        items.each((i, item) => {
    22          if (item.innerText.includes('pyroscope.server.inuse_space')) {
    23            item.click();
    24          }
    25        });
    26      });
    27  
    28      cy.location().then((loc) => {
    29        const queryParams = new URLSearchParams(loc.search);
    30        expect(queryParams.get('query')).to.eq('pyroscope.server.inuse_space{}');
    31      });
    32    });
    33  
    34    it('view buttons should change view when clicked', () => {
    35      // mock data since the first preselected application
    36      // could have no data
    37      cy.intercept('**/render*', {
    38        fixture: 'simple-golang-app-cpu.json',
    39        times: 1,
    40      }).as('render1');
    41  
    42      cy.visit('/');
    43  
    44      cy.findByTestId('table').click();
    45      cy.findByTestId('table-ui').should('be.visible');
    46      cy.findByTestId('flamegraph-view').should('not.exist');
    47  
    48      cy.findByTestId('both').click();
    49      cy.findByTestId('table-ui').should('be.visible');
    50      cy.findByTestId('flamegraph-view').should('be.visible');
    51  
    52      cy.findByTestId('flamegraph').click();
    53      cy.findByTestId('table-ui').should('not.exist');
    54      cy.findByTestId('flamegraph-view').should('be.visible');
    55    });
    56  
    57    // TODO make this a unit test
    58    it('sorting works', () => {
    59      /**
    60       * @param row 'first' | 'last'
    61       * @param column 'location' | 'self' | 'total'
    62       */
    63  
    64      const columns = {
    65        location: {
    66          index: 1,
    67          selector: '.symbol-name',
    68        },
    69        self: {
    70          index: 2,
    71          selector: 'span',
    72        },
    73        total: {
    74          index: 3,
    75          selector: 'span',
    76        },
    77      };
    78  
    79      const sortColumn = (columnIndex) =>
    80        cy
    81          .findByTestId('table-ui')
    82          .find(`thead > tr > :nth-child(${columnIndex})`)
    83          .click();
    84  
    85      const getCellContent = (row, column) => {
    86        const query = `tbody > :nth-child(${row}) > :nth-child(${column.index})`;
    87        return cy
    88          .findByTestId('table-ui')
    89          .find(query)
    90          .then((cell) => cell[0].innerText);
    91      };
    92  
    93      cy.intercept('**/render*', {
    94        fixture: 'render.json',
    95        times: 1,
    96      }).as('render');
    97  
    98      cy.visit('/');
    99  
   100      cy.findByTestId('table-ui')
   101        .find('tbody > tr')
   102        .then((rows) => {
   103          const first = 1;
   104          const last = rows.length;
   105  
   106          // sort by location desc
   107          sortColumn(columns.location.index);
   108          getCellContent(first, columns.location).should('eq', 'function_6');
   109          getCellContent(last, columns.location).should('eq', 'function_0');
   110  
   111          // sort by location asc
   112          sortColumn(columns.location.index);
   113          getCellContent(first, columns.location).should('eq', 'function_0');
   114          getCellContent(last, columns.location).should('eq', 'function_6');
   115  
   116          // sort by self desc
   117          sortColumn(columns.self.index);
   118          getCellContent(first, columns.self).should('eq', '5.00 seconds');
   119          getCellContent(last, columns.self).should('eq', '0.55 seconds');
   120  
   121          // sort by self asc
   122          sortColumn(columns.self.index);
   123          getCellContent(first, columns.self).should('eq', '0.55 seconds');
   124          getCellContent(last, columns.self).should('eq', '5.00 seconds');
   125  
   126          // sort by total desc
   127          sortColumn(columns.total.index);
   128          getCellContent(first, columns.total).should('eq', '5.16 seconds');
   129          getCellContent(last, columns.total).should('eq', '0.50 seconds');
   130  
   131          // sort by total asc
   132          sortColumn(columns.total.index);
   133          getCellContent(first, columns.total).should('eq', '0.50 seconds');
   134          getCellContent(last, columns.total).should('eq', '5.16 seconds');
   135        });
   136    });
   137  
   138    it('validates "Reset View" button works', () => {
   139      cy.intercept('**/render*', {
   140        fixture: 'simple-golang-app-cpu.json',
   141      }).as('render');
   142  
   143      cy.visit('/');
   144  
   145      cy.findByRole('button', { name: /Reset/ }).should('be.disabled');
   146      cy.waitForFlamegraphToRender().click(0, BAR_HEIGHT * 2);
   147      cy.findByRole('button', { name: /Reset/ }).should('not.be.disabled');
   148      cy.findByRole('button', { name: /Reset/ }).click();
   149      cy.findByRole('button', { name: /Reset/ }).should('be.disabled');
   150    });
   151  
   152    describe('tooltip', () => {
   153      // on smaller screens component will be collapsed by default
   154      beforeEach(() => {
   155        cy.viewport(1440, 900);
   156      });
   157      it('flamegraph tooltip works in single view', () => {
   158        cy.intercept('**/render*', {
   159          fixture: 'simple-golang-app-cpu.json',
   160        }).as('render');
   161  
   162        cy.visit('/');
   163  
   164        cy.findAllByTestId('tooltip').should('not.be.visible');
   165  
   166        cy.waitForFlamegraphToRender().trigger('mousemove');
   167  
   168        cy.findByTestId('flamegraph-view')
   169          .findByTestId('tooltip')
   170          .should('be.visible');
   171  
   172        cy.findByTestId('tooltip-title').should('have.text', 'total');
   173        cy.findByTestId('tooltip-table').should(
   174          'have.text',
   175          'Share of CPU:100%CPU Time:9.88 secondsSamples:988'
   176        );
   177  
   178        cy.waitForFlamegraphToRender().trigger('mouseout');
   179        cy.findByTestId('flamegraph-view')
   180          .findByTestId('tooltip')
   181          .should('not.be.visible');
   182      });
   183  
   184      it('flamegraph tooltip works in comparison view', () => {
   185        const findFlamegraph = (n: number) => {
   186          const query = `> :nth-child(${n})`;
   187  
   188          return cy.findByTestId('comparison-container').find(query);
   189        };
   190  
   191        cy.intercept('**/render*from=1633024300&until=1633024300*', {
   192          fixture: 'simple-golang-app-cpu.json',
   193          times: 1,
   194        }).as('render-right');
   195  
   196        cy.intercept('**/render*from=1633024290&until=1633024290*', {
   197          fixture: 'simple-golang-app-cpu2.json',
   198          times: 1,
   199        }).as('render-left');
   200  
   201        cy.visit(
   202          '/comparison?query=simple.golang.app.cpu%7B%7D&from=1633024298&until=1633024302&leftFrom=1633024290&leftUntil=1633024290&rightFrom=1633024300&rightUntil=1633024300'
   203        );
   204  
   205        cy.wait('@render-right');
   206        cy.wait('@render-left');
   207  
   208        // flamegraph 1 (the left one)
   209        cy.log('left flamegraph');
   210        findFlamegraph(1)
   211          .findByTestId('flamegraph-view')
   212          .findByTestId('tooltip')
   213          .should('not.be.visible');
   214  
   215        findFlamegraph(1).waitForFlamegraphToRender().trigger('mousemove');
   216  
   217        findFlamegraph(1)
   218          .findByTestId('flamegraph-view')
   219          .findByTestId('tooltip')
   220          .should('have.css', 'visibility', 'visible');
   221  
   222        findFlamegraph(1)
   223          .findByTestId('tooltip-title')
   224          .should('have.text', 'total');
   225        findFlamegraph(1)
   226          .findByTestId('tooltip-table')
   227          .should(
   228            'have.text',
   229            'Share of CPU:100%CPU Time:9.91 secondsSamples:991'
   230          );
   231  
   232        findFlamegraph(1).waitForFlamegraphToRender().trigger('mousemove');
   233        findFlamegraph(1).waitForFlamegraphToRender().trigger('mouseout');
   234  
   235        findFlamegraph(1)
   236          .findByTestId('flamegraph-view')
   237          .findByTestId('tooltip')
   238          .should('not.be.visible');
   239  
   240        // flamegraph 2 (right one)
   241        cy.log('right flamegraph');
   242        findFlamegraph(2)
   243          .findByTestId('flamegraph-view')
   244          .findByTestId('tooltip')
   245          .should('not.be.visible');
   246  
   247        findFlamegraph(2).waitForFlamegraphToRender().trigger('mousemove', 0, 0, {
   248          force: true,
   249        });
   250  
   251        findFlamegraph(2)
   252          .findByTestId('flamegraph-view')
   253          .findByTestId('tooltip')
   254          .should('have.css', 'visibility', 'visible');
   255  
   256        findFlamegraph(2)
   257          .findByTestId('tooltip-title')
   258          .should('have.text', 'total');
   259        findFlamegraph(2)
   260          .findByTestId('tooltip-table')
   261          .should(
   262            'have.text',
   263            'Share of CPU:100%CPU Time:9.88 secondsSamples:988'
   264          );
   265  
   266        findFlamegraph(2)
   267          .waitForFlamegraphToRender()
   268          .trigger('mouseout', { force: true });
   269        findFlamegraph(2)
   270          .findByTestId('flamegraph-view')
   271          .findByTestId('tooltip')
   272          .should('not.be.visible');
   273      });
   274  
   275      it('flamegraph tooltip works in diff view', () => {
   276        cy.intercept('**/render*', {
   277          fixture: 'simple-golang-app-cpu-diff.json',
   278          times: 3,
   279        }).as('render');
   280  
   281        cy.visit(
   282          '/comparison-diff?query=testapp%7B%7D&rightQuery=testapp%7B%7D&leftQuery=testapp%7B%7D&leftFrom=1&leftUntil=1&rightFrom=1&rightUntil=1&from=now-5m'
   283        );
   284  
   285        cy.wait('@render');
   286        cy.wait('@render');
   287        cy.wait('@render');
   288  
   289        cy.waitForFlamegraphToRender();
   290  
   291        // This test has a race condition, since it does not wait for the canvas to be rendered
   292        cy.findByTestId('flamegraph-view')
   293          .findByTestId('tooltip')
   294          .should('not.be.visible');
   295  
   296        cy.waitForFlamegraphToRender().trigger('mousemove', 0, 0);
   297        cy.findByTestId('flamegraph-view')
   298          .findByTestId('tooltip')
   299          .should('have.css', 'visibility', 'visible');
   300  
   301        cy.findByTestId('tooltip-title').should('have.text', 'total');
   302        cy.findByTestId('tooltip-table').should(
   303          'have.text',
   304          'BaselineComparisonDiffShare of CPU:100%100%CPU Time:9.91 seconds9.87 secondsSamples:991987'
   305        );
   306      });
   307  
   308      it('table tooltip works in single view', () => {
   309        cy.intercept('**/render*', {
   310          fixture: 'simple-golang-app-cpu.json',
   311          times: 3,
   312        }).as('render');
   313  
   314        cy.visit('/');
   315  
   316        cy.wait('@render');
   317  
   318        cy.findByTestId('table-view')
   319          .findByTestId('tooltip')
   320          .should('not.be.visible');
   321  
   322        cy.findByTestId('table-view').trigger('mousemove', 150, 80);
   323        cy.findByTestId('table-view')
   324          .findByTestId('tooltip')
   325          .should('have.css', 'visibility', 'visible');
   326  
   327        cy.findByTestId('tooltip-table').should(
   328          'have.text',
   329          'Self (% of total CPU)Total (% of total CPU)CPU Time:0.02 seconds(0.20%)0.03 seconds(0.30%)'
   330        );
   331      });
   332  
   333      it('table tooltip works in diff view', () => {
   334        cy.intercept('**/render*', {
   335          fixture: 'simple-golang-app-cpu-diff.json',
   336          times: 3,
   337        }).as('render');
   338  
   339        cy.visit(
   340          '/comparison-diff?query=testapp%7B%7D&rightQuery=testapp%7B%7D&leftQuery=testapp%7B%7D&leftFrom=1&leftUntil=1&rightFrom=1&rightUntil=1&from=now-5m'
   341        );
   342  
   343        cy.wait('@render');
   344  
   345        cy.findByTestId('table-view')
   346          .findByTestId('tooltip')
   347          .should('not.be.visible');
   348  
   349        cy.findByTestId('table-view').trigger('mousemove', 150, 80);
   350        cy.findByTestId('table-view')
   351          .findByTestId('tooltip')
   352          .should('have.css', 'visibility', 'visible');
   353  
   354        cy.findByTestId('tooltip-table').should(
   355          'have.text',
   356          'BaselineComparisonDiffShare of CPU:100%99.8%(-0.20%)CPU Time:9.91 seconds9.85 secondsSamples:991985'
   357        );
   358      });
   359    });
   360  
   361    describe('highlight', () => {
   362      it('works in diff view', () => {
   363        cy.intercept('**/render*', {
   364          fixture: 'simple-golang-app-cpu-diff.json',
   365          times: 1,
   366        }).as('render');
   367  
   368        cy.visit(
   369          '/comparison-diff?query=simple.golang.app.cpu%7B%7D&from=1633024298&until=1633024302&leftFrom=1633024290&leftUntil=1633024290&rightFrom=1633024300&rightUntil=1633024300'
   370        );
   371  
   372        cy.wait('@render');
   373  
   374        cy.findByTestId('flamegraph-highlight').should('not.be.visible');
   375  
   376        cy.wait(500);
   377  
   378        cy.waitForFlamegraphToRender().trigger('mousemove', 0, 0);
   379        cy.findByTestId('flamegraph-highlight').should('be.visible');
   380      });
   381    });
   382  
   383    describe('contextmenu', () => {
   384      it("it works when 'clear view' is clicked", () => {
   385        cy.intercept('**/render*', {
   386          fixture: 'simple-golang-app-cpu.json',
   387          times: 1,
   388        }).as('render');
   389  
   390        cy.visit('/');
   391  
   392        // until we focus on a specific, it should not be enabled
   393        cy.waitForFlamegraphToRender().rightclick();
   394        cy.findByRole('menuitem', { name: /Reset View/ }).should(
   395          'have.attr',
   396          'aria-disabled',
   397          'true'
   398        );
   399  
   400        // click on the second item
   401        cy.waitForFlamegraphToRender().click(0, BAR_HEIGHT * 2);
   402        cy.waitForFlamegraphToRender().rightclick();
   403        cy.findByRole('menuitem', { name: /Reset View/ }).should(
   404          'not.have.attr',
   405          'aria-disabled'
   406        );
   407        cy.findByRole('menuitem', { name: /Reset View/ }).click();
   408        // TODO assert that it was indeed reset?
   409  
   410        // should be disabled again
   411        cy.waitForFlamegraphToRender().rightclick();
   412        cy.findByRole('menuitem', { name: /Reset View/ }).should(
   413          'have.attr',
   414          'aria-disabled',
   415          'true'
   416        );
   417      });
   418    });
   419  });