diff --git a/tests/cypress/support/commands.ts b/tests/cypress/support/commands.ts index 1b554b6..682a861 100644 --- a/tests/cypress/support/commands.ts +++ b/tests/cypress/support/commands.ts @@ -59,6 +59,11 @@ declare global { pfCloseButtonIfExists(ariaLabel?: string, options?: Partial): Chainable; menuToggleContains(text: string | RegExp, options?: Partial): Chainable>; verifyTraceCount(expectedCount: number | string, options?: Partial): Chainable; + // Tracing-specific commands + setupTracePage(tempoInstance: string, tenant: string, timeframe?: string, serviceFilter?: string): Chainable; + navigateToTraceDetails(): Chainable; + dragCutoffResizer(position: number, resizerType?: 'left' | 'right'): Chainable; + verifyCutoffPosition(expectedWidthPercent: number, tolerance?: number): Chainable; } } } @@ -657,4 +662,113 @@ Cypress.Commands.add( cy.log(`✓ Verified trace count: ${countStr} traces found`); }, +); + +// Tracing-specific commands for common operations + +Cypress.Commands.add( + 'setupTracePage', + (tempoInstance: string, tenant: string, timeframe?: string, serviceFilter?: string) => { + cy.log(`Setting up trace page: ${tempoInstance} / ${tenant}`); + + // Navigate to traces page + cy.visit('/observe/traces'); + cy.url().should('include', '/observe/traces'); + + // Select Tempo instance + cy.pfTypeahead('Select a Tempo instance').click(); + cy.pfSelectMenuItem(tempoInstance).click(); + + // Select tenant + cy.pfTypeahead('Select a tenant').click(); + cy.pfSelectMenuItem(tenant).click(); + + // Set timeframe if provided + if (timeframe) { + cy.pfMenuToggle('Last 30 minutes').click(); + cy.pfSelectMenuItem(timeframe).click(); + } + + // Set service filter if provided + if (serviceFilter) { + cy.pfMenuToggle('Service Name').click(); + cy.pfMenuToggleByLabel('Multi typeahead checkbox').click(); + cy.pfCheckMenuItem(serviceFilter); + } + + cy.log(`✓ Trace page setup complete for ${tempoInstance} / ${tenant}`); + }, +); + +Cypress.Commands.add( + 'navigateToTraceDetails', + () => { + cy.log('Navigating to trace details'); + cy.muiFirstTraceLink().click(); + cy.findByTestId('span-duration-bar').eq(1).click(); + cy.log('✓ In trace details view'); + }, +); + +Cypress.Commands.add( + 'dragCutoffResizer', + (position: number, resizerType: 'left' | 'right' = 'right') => { + cy.log(`Dragging ${resizerType} resizer to ${position}% position`); + + // Wait for the trace timeline to be fully loaded + cy.get(`[data-elem="resizer${resizerType === 'right' ? 'Right' : 'Left'}"]`).should('be.visible'); + + // Perform the drag operation + cy.get(`[data-elem="resizer${resizerType === 'right' ? 'Right' : 'Left'}"]`) + .first() + .then(($resizer) => { + const resizerRect = $resizer[0].getBoundingClientRect(); + + // Get the timeline container using canvas as a stable reference point + cy.get('canvas[height="60"]') + .parent() + .then(($timeline) => { + const timelineRect = $timeline[0].getBoundingClientRect(); + const targetX = timelineRect.left + (timelineRect.width * (position / 100)); + + // Drag the resizer to the specified position + cy.wrap($resizer) + .trigger('mousedown', { which: 1 }) + .trigger('mousemove', { + clientX: targetX, + clientY: resizerRect.top + resizerRect.height / 2 + }) + .trigger('mouseup'); + }); + }); + + // Wait for the timeline to update after the drag operation + cy.wait(1000); + cy.log(`✓ ${resizerType} resizer dragged to ${position}% position`); + }, +); + +Cypress.Commands.add( + 'verifyCutoffPosition', + (expectedWidthPercent: number, tolerance: number = 2) => { + cy.log(`Verifying cutoff box position is around ${expectedWidthPercent}%`); + + // Verify the cutoff box is positioned correctly + cy.get('[data-elem="cutoffBox"]') + .last() // Get the right cutoff box + .invoke('attr', 'style') + .then((style) => { + // Extract the width percentage value + const widthMatch = style.match(/width:\s*(\d+(?:\.\d+)?)%/); + expect(widthMatch).to.not.be.null; + const widthValue = parseFloat(widthMatch[1]); + + // Check within tolerance + const minWidth = expectedWidthPercent - tolerance; + const maxWidth = expectedWidthPercent + tolerance; + expect(widthValue).to.be.within(minWidth, maxWidth); + + cy.log(`✓ Cutoff box width is ${widthValue}% (within acceptable range of ${minWidth}-${maxWidth}%)`); + }); + }, ); \ No newline at end of file diff --git a/tests/e2e/dt-plugin-tests.cy.ts b/tests/e2e/dt-plugin-tests.cy.ts index d16e2b1..204771c 100644 --- a/tests/e2e/dt-plugin-tests.cy.ts +++ b/tests/e2e/dt-plugin-tests.cy.ts @@ -461,4 +461,42 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { cy.pfSelectMenuItem('20').click(); cy.verifyTraceCount(20); }); + + it('Test Distributed Traces Cutoffbox functionality', () => { + // Setup the trace page with tempo instance and filters + cy.setupTracePage('chainsaw-rbac / simplst', 'dev', 'Last 15 minutes', 'frontend'); + + // Navigate to trace details + cy.navigateToTraceDetails(); + + cy.log('Test MUI box cutoff functionality by interacting with resizer'); + + // Store the original time range values for comparison + cy.get('[style*="left: 25%"]').contains(/\d+(\.\d+)?(μs|ms|s)/) + .invoke('text') + .as('secondTimeValue'); + + // Drag the right resizer to 50% position + cy.dragCutoffResizer(50, 'right'); + + // Verify the cutoff box is positioned correctly (around 50%) + cy.verifyCutoffPosition(50, 2); // 50% ± 2% tolerance + + // Verify that the time range has been updated to reflect the cutoff selection + cy.log('Verify the time range reflects the cutoff selection'); + cy.get('@secondTimeValue').then((secondValue) => { + // Check that the updated time range shows values around the selected area + cy.get('[style*="left: 0%"][style*="border-width: 0px"]') + .should('be.visible') + .and('not.be.empty'); + + // Verify that the range shows millisecond values that align with the cutoff + cy.get('[style*="left: 100%"] span[style*="position: absolute; right: 0.75rem"]') + .should('be.visible') + .invoke('text') + .should('match', /\d+(\.\d+)?(μs|ms|s)/); // Should match time format + + cy.log('✓ MUI box cutoff functionality verified - time range updated correctly'); + }); + }); });