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 });