# Instructions - Following Playwright test failed. - Explain why, be concise, respect Playwright best practices. - Provide a snippet of code with the fix, if possible. # Test info - Name: ui/component.test.ts >> Component UI Test Suite >> Verify CI >> verify CI provider on CI tab - Location: tests/ui/component.test.ts:83:5 # Error details ``` TimeoutError: Step timeout of 40000ms exceeded. ``` ``` Error: expect(locator).toBeVisible() failed Locator: locator('table').filter({ has: getByRole('columnheader', { name: 'ID', exact: true }) }).locator('tbody tr[index]').first().getByRole('button', { name: 'View Logs' }) Expected: visible Error: element(s) not found Call log: - Expect "toBeVisible" with timeout 60000ms - waiting for locator('table').filter({ has: getByRole('columnheader', { name: 'ID', exact: true }) }).locator('tbody tr[index]').first().getByRole('button', { name: 'View Logs' }) ``` # Page snapshot ```yaml - generic [ref=e2]: - alert [ref=e4]: - img [ref=e6] - generic [ref=e8]: Request failed with 504 Gateway Time-out - button [ref=e10] [cursor=pointer]: - img [ref=e12] - generic [ref=e14]: - navigation [ref=e16]: - generic [ref=e17]: - link "Home" [ref=e19] [cursor=pointer]: - /url: / - img [ref=e20] - generic [ref=e29]: - img [ref=e31] - combobox "Search..." [ref=e33] - generic "Self-service" [ref=e36]: - link "Self-service" [ref=e37] [cursor=pointer]: - /url: /create - img [ref=e39] - button "Your starred items" [ref=e42] [cursor=pointer]: - img [ref=e43] - button "Application launcher" [ref=e46] [cursor=pointer]: - img [ref=e47] - button "Help" [ref=e50] [cursor=pointer]: - img [ref=e51] - separator [ref=e53] - button "Admin" [ref=e55] [cursor=pointer]: - generic [ref=e56]: - img [ref=e57] - paragraph [ref=e60]: Admin - img [ref=e61] - generic [ref=e64]: - navigation "sidebar nav": - generic [ref=e66]: - generic [ref=e69]: - link "Home" [ref=e71] [cursor=pointer]: - /url: / - img [ref=e75] - generic [ref=e77]: Home - link "Catalog" [ref=e79] [cursor=pointer]: - /url: /catalog - img [ref=e83] - generic [ref=e85]: Catalog - link "APIs" [ref=e87] [cursor=pointer]: - /url: /api-docs - img [ref=e91] - generic [ref=e93]: APIs - link "Learning Paths" [ref=e95] [cursor=pointer]: - /url: /learning-paths - img [ref=e99] - generic [ref=e101]: Learning Paths - separator [ref=e102] - link "Docs" [ref=e105] [cursor=pointer]: - /url: /docs - img [ref=e109] - generic [ref=e111]: Docs - generic [ref=e112]: - separator [ref=e113] - button "Administration" [ref=e114] [cursor=pointer]: - generic [ref=e115]: - img [ref=e119] - generic [ref=e122]: Administration - img [ref=e124] - main [ref=e126]: - generic [ref=e127]: - generic [ref=e128]: - paragraph [ref=e129]: component — service - heading "e2e-tests-go-jxuycwel Add to favorites" [level=1] [ref=e130]: - generic [ref=e131]: - generic "component:default/e2e-tests-go-jxuycwel | service | Secure Supply Chain Example for Go Runtime http based application with advanced continuous integration pipeline covering building, CVE scanning, security scanning, signatures, attestations, SLSA provenance and SBOM along with Gitops-based continuous deployment." [ref=e133]: e2e-tests-go-jxuycwel - button "Add to favorites" [ref=e134] [cursor=pointer]: - img [ref=e137] - generic [ref=e139]: - generic [ref=e141]: - paragraph [ref=e142]: Owner - paragraph [ref=e143]: - link "user:guest" [ref=e144] [cursor=pointer]: - /url: /catalog/default/user/guest - generic "user:default/guest" [ref=e145]: - img [ref=e147] - text: user:guest - generic [ref=e150]: - paragraph [ref=e151]: Lifecycle - paragraph [ref=e152]: experimental - button "more" [ref=e153] [cursor=pointer]: - img [ref=e155] - tablist "tabs" [ref=e161]: - tab "Overview" [ref=e162] [cursor=pointer] - tab "Topology" [ref=e163] [cursor=pointer] - tab "CI" [selected] [ref=e164] [cursor=pointer] - tab "CD" [ref=e165] [cursor=pointer] - tab "Kubernetes" [ref=e166] [cursor=pointer] - tab "API" [ref=e167] [cursor=pointer] - tab "Dependencies" [ref=e168] [cursor=pointer] - tab "Docs" [ref=e169] [cursor=pointer] - article [ref=e171]: - generic [ref=e172]: - alert [ref=e175]: - 'button "Error: Request failed with 504 Gateway Time-out" [ref=e176] [cursor=pointer]': - generic [ref=e177]: - img [ref=e178] - 'heading "Error: Request failed with 504 Gateway Time-out" [level=6] [ref=e180]' - img [ref=e183] - generic [ref=e188]: - heading "Security Information" [level=4] [ref=e191] - generic [ref=e193]: - tablist "Multi CI" [ref=e196]: - tab "Azure Pipelines" [selected] [ref=e197] [cursor=pointer]: - generic [ref=e198]: Azure Pipelines - generic [ref=e202]: - generic [ref=e203]: - generic [ref=e206]: - generic [ref=e208]: - img [ref=e210] - button "Name" [ref=e212] [cursor=pointer] - textbox: Name - img - generic "search" [ref=e214]: - img [ref=e216] - textbox "Search by name" [ref=e218] - generic [ref=e220]: - generic "rows per page" [ref=e221]: - button "1 - 0 of 0" [ref=e222] [cursor=pointer] - textbox: "5" - img - button "first page" [disabled]: - generic: - img - button "previous page" [disabled]: - generic: - img - generic "page number" [ref=e223]: - generic [ref=e224]: - textbox [ref=e225]: "1" - group - paragraph [ref=e226]: of 0 - button "next page" [ref=e227] [cursor=pointer]: - img [ref=e229] - button "last page" [ref=e231] [cursor=pointer]: - img [ref=e233] - generic [ref=e237]: - img [ref=e238] - heading "No Pipeline Runs" [level=6] [ref=e240] - paragraph [ref=e241]: No pipeline runs available. Create a new pipeline run to get started. ``` # Test source ```ts 1 | import { expect, Locator, Page } from '@playwright/test'; 2 | import { BaseCIPlugin } from './baseCIPlugin'; 3 | import { checkWebsiteStatus } from '../../commonUi'; 4 | import { AzurePO } from '../../page-objects/azurePo'; 5 | import { CiPo } from '../../page-objects/ciPo'; 6 | import { CommonPO } from '../../page-objects/commonPo'; 7 | import { LoggerFactory, Logger } from '../../../logger/logger'; 8 | 9 | export class AzurePlugin extends BaseCIPlugin { 10 | private readonly logger: Logger = LoggerFactory.getLogger('AzurePlugin'); 11 | 12 | constructor(name: string, registryOrg: string) { 13 | super(name, registryOrg); 14 | } 15 | 16 | private getPprTable(page: Page): Locator { 17 | return page.locator('table').filter({ 18 | has: page.getByRole('columnheader', { name: AzurePO.columnHeaders[0], exact: true }) 19 | }); 20 | } 21 | 22 | private async checkColumnHeaders(table: Locator): Promise { 23 | for (const header of AzurePO.columnHeaders) { 24 | await expect(table.getByRole('columnheader', { name: header })).toBeVisible(); 25 | } 26 | } 27 | 28 | private async checkRowCellsVisible(cells: Locator[]): Promise { 29 | expect(cells).toHaveLength(AzurePO.columnHeaders.length); 30 | for (const cell of cells) { 31 | await expect(cell).toBeVisible(); 32 | } 33 | } 34 | 35 | private async checkRowCellContents(page: Page, cells: Locator[]): Promise { 36 | const { cellIndex } = AzurePO; 37 | 38 | await expect(cells[cellIndex.id]).toHaveText(AzurePO.pprNumberRegex); 39 | 40 | const pprLink = cells[cellIndex.build].getByRole('link'); 41 | await expect(pprLink).toBeVisible(); 42 | const pprLinkHref = await pprLink.getAttribute('href'); 43 | expect(pprLinkHref).not.toBeNull(); 44 | await checkWebsiteStatus(page, pprLinkHref!); 45 | 46 | await expect(cells[cellIndex.source]).toHaveText(AzurePO.branchCommitRegex); 47 | 48 | await expect(cells[cellIndex.state].getByTestId(CiPo.statusOkTestId)).toBeVisible(); 49 | await expect(cells[cellIndex.state]).toContainText(CiPo.statusSucceededText); 50 | 51 | await expect(cells[cellIndex.duration]).toHaveText(AzurePO.durationRegex); 52 | 53 | await expect(cells[cellIndex.age]).toHaveText(AzurePO.relativeTimeRegex); 54 | 55 | await expect(cells[cellIndex.logs].getByRole('button', { name: AzurePO.viewLogsButtonName })).toBeVisible(); 56 | } 57 | 58 | public async checkCIHeading(page: Page): Promise { 59 | await expect(page.getByRole('heading', { name: AzurePO.pipelinesHeading })).toBeVisible(); 60 | } 61 | 62 | public async checkActions(page: Page): Promise { 63 | const pipelineRunsTable = this.getPprTable(page); 64 | const firstRow = pipelineRunsTable.locator(CommonPO.dataRowSelector).first(); 65 | 66 | const viewLogsButton = firstRow.getByRole('button', { name: AzurePO.viewLogsButtonName }); > 67 | await expect(viewLogsButton).toBeVisible(); | ^ Error: expect(locator).toBeVisible() failed 68 | await viewLogsButton.click(); 69 | 70 | const logsPopup = page.getByRole('heading', { name: AzurePO.logsPopupHeadingRegex }); 71 | await expect(logsPopup).toBeVisible(); 72 | 73 | const closeButton = page.getByRole('button', { name: AzurePO.closeButtonName }); 74 | await closeButton.click(); 75 | } 76 | 77 | public async checkPipelineRunsTable(page: Page): Promise { 78 | const pipelineRunsTable = this.getPprTable(page); 79 | await expect(pipelineRunsTable).toBeVisible(); 80 | 81 | await this.checkColumnHeaders(pipelineRunsTable); 82 | 83 | const tableRows = pipelineRunsTable.locator(CommonPO.dataRowSelector); 84 | await expect(tableRows.first()).toBeVisible(); 85 | 86 | const tableRowCells = await tableRows.first().locator('td').all(); 87 | await this.checkRowCellsVisible(tableRowCells); 88 | await this.checkRowCellContents(page, tableRowCells); 89 | } 90 | 91 | // eslint-disable-next-line no-unused-vars 92 | public async checkImageRegistryLinks(_page: Page): Promise { 93 | this.logger.info('Skipping checkImageRegistryLinks - not applicable for Azure DevOps CI'); 94 | } 95 | } 96 | ```