# 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-python-djypsonf Add to favorites" [level=1] [ref=e130]: - generic [ref=e131]: - generic "component:default/e2e-tests-python-djypsonf | service | Secure Supply Chain Example for Python is an interpreted, object-oriented, high-level programming language with dynamic semantics. This sample demonstrates software supply chain security functionalty using an 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-python-djypsonf - 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 | ```