-
Notifications
You must be signed in to change notification settings - Fork 354
test: add UI E2E tests for Argo CD Resource Tree and Pod logs #1190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,16 +1,28 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #!/bin/bash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # use arguments to extract --env and keep the rest for Playwright | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ENV="local" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TEST_ARGS=() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while [[ "$#" -gt 0 ]]; do | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case $1 in | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --env=*) ENV="${1#*=}" ;; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *) TEST_ARGS+=("$1") ;; # Save all other args (files, --headed, etc.) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| esac | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shift | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -f .env ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Loading variables from .env file..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -a #export all variables | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source .env | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set +a # stop automatically exporting | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set +a #stop auto export | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #making sure we are in the correct dir | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cd "$(dirname "$0")" || exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
15
to
24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Load
Suggested fix- if [ -f .env ]; then
- echo "Loading variables from .env file..."
- set -a `#export` all variables
- source .env
- set +a `#stop` auto export
- fi
-
- `#making` sure we are in the correct dir
- cd "$(dirname "$0")" || exit 1
+ `#making` sure we are in the correct dir
+ cd "$(dirname "$0")" || exit 1
+
+ if [ -f .env ]; then
+ echo "Loading variables from .env file..."
+ set -a `#export` all variables
+ source .env
+ set +a `#stop` auto export
+ fi📝 Committable suggestion
Suggested change
🧰 Tools🪛 Shellcheck (0.11.0)[info] 18-18: Not following: .env was not specified as input (see shellcheck -x). (SC1091) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # username (might be something different for rosa - can be overwritten with export CLUSTER_USER) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #username (might be something different for rosa - can be overwritten with export CLUSTER_USER) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export CLUSTER_USER=${CLUSTER_USER:-"kubeadmin"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export IDP=${IDP:-"kube:admin"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -26,11 +38,11 @@ if [ -n "$OC_API_URL" ] && [ -n "$CLUSTER_PASSWORD" ]; then | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif ! oc whoami > /dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # If variables don't exist AND we aren't logged in, fail out | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #if variables don't exist AND we aren't logged in fail out | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Error: Not logged in. Missing OC_API_URL or CLUSTER_PASSWORD." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # If variables don't exist but we ARE logged in locally, just use the current session | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #if variables don't exist but we ARE logged in locally just use the current session | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "No .env credentials found. Using existing oc CLI session..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -53,4 +65,22 @@ rm -f .auth/storageState.json || true | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #run Playwright | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo " Starting Playwright tests..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| npx playwright test "$@" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 2. Execute based on the environment | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ "$ENV" == "ci" ]] || [[ "$ENV" == "pipeline" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Running headlessly in automation ($ENV)..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| npm ci | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Prevent sudo jump-scares for local Mac users simulating CI | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ "$(uname -s)" == "Darwin" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| npx playwright install chromium | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| npx playwright install chromium --with-deps | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| npx playwright test "${TEST_ARGS[@]}" --reporter=list | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+70
to
+82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
In CI/pipeline mode, Line 81 still passes user args verbatim. If Suggested fix if [[ "$ENV" == "ci" ]] || [[ "$ENV" == "pipeline" ]]; then
echo "Running headlessly in automation ($ENV)..."
npm ci
@@
- npx playwright test "${TEST_ARGS[@]}" --reporter=list
+ FILTERED_ARGS=()
+ for arg in "${TEST_ARGS[@]}"; do
+ [[ "$arg" == "--headed" ]] && continue
+ FILTERED_ARGS+=("$arg")
+ done
+ npx playwright test "${FILTERED_ARGS[@]}" --reporter=list📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Running Locally..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| npx playwright test "${TEST_ARGS[@]}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,64 @@ | ||||||||||
| import { Page, expect, Locator } from '@playwright/test'; | ||||||||||
|
|
||||||||||
| export class ApplicationDetailsPage { | ||||||||||
| readonly page: Page; | ||||||||||
| readonly resourceTreeContainer: Locator; | ||||||||||
| readonly slideOutPanel: Locator; | ||||||||||
| readonly logsTab: Locator; | ||||||||||
|
|
||||||||||
| constructor(page: Page) { | ||||||||||
| this.page = page; | ||||||||||
|
|
||||||||||
| //main container | ||||||||||
| this.resourceTreeContainer = page.locator('.application-details__tree'); | ||||||||||
|
|
||||||||||
| //details panel that slides out (isolate the active visible pane) | ||||||||||
| this.slideOutPanel = page.locator('.sliding-panel').filter({ visible: true }); | ||||||||||
|
|
||||||||||
| //logs tab inside the slide-out panel | ||||||||||
| this.logsTab = this.slideOutPanel.getByRole('button', { name: /logs/i }).or(this.slideOutPanel.getByText(/logs/i, { exact: true })); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| async verifyResourceTreeLoaded() { | ||||||||||
| //wait tree to be visible | ||||||||||
| await expect(this.resourceTreeContainer).toBeVisible({ timeout: 20000 }); | ||||||||||
| //wait for healthy status | ||||||||||
| await expect(this.page.getByText('Healthy', { exact: true }).first()).toBeVisible({ timeout: 30000 }); | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Scope the “Healthy” assertion to the resource tree container to avoid false positives.
Suggested fix- await expect(this.page.getByText('Healthy', { exact: true }).first()).toBeVisible({ timeout: 30000 });
+ await expect(
+ this.resourceTreeContainer.getByText('Healthy', { exact: true }).first()
+ ).toBeVisible({ timeout: 30000 });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| } | ||||||||||
|
|
||||||||||
| async clickResourceNode(kind: string, name: string) { | ||||||||||
| //find the innermost div representing the resource node | ||||||||||
| const node = this.resourceTreeContainer | ||||||||||
| .locator('div') | ||||||||||
| .filter({ hasText: kind }) | ||||||||||
| .filter({ hasText: name }) | ||||||||||
| .last(); | ||||||||||
|
|
||||||||||
| //scroll it into view and click it | ||||||||||
| await node.scrollIntoViewIfNeeded(); | ||||||||||
| await node.waitFor({ state: 'visible', timeout: 15000 }); | ||||||||||
| await node.click(); | ||||||||||
|
|
||||||||||
| //self-healing validation block to handle frontend rendering lag | ||||||||||
| await expect(async () => { | ||||||||||
| await expect(this.slideOutPanel).toBeVisible({ timeout: 2000 }); | ||||||||||
| }).toPass({ timeout: 10000 }); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| async verifyPodLogs(expectedLogText?: string) { | ||||||||||
| //click Logs | ||||||||||
| await this.logsTab.waitFor({ state: 'visible', timeout: 5000 }); | ||||||||||
| await this.logsTab.click(); | ||||||||||
|
|
||||||||||
| const logFilterInput = this.slideOutPanel.getByPlaceholder('containing'); | ||||||||||
| await expect(logFilterInput).toBeVisible({ timeout: 15000 }); | ||||||||||
|
|
||||||||||
| if (expectedLogText) { | ||||||||||
| //find log line anywhere in the slide-out panel | ||||||||||
| await expect(this.slideOutPanel).toContainText(expectedLogText, { timeout: 30000 }); | ||||||||||
| } else { | ||||||||||
| const genericLogLine = this.slideOutPanel.getByText(/\d{4}-\d{2}-\d{2}.*(INFO|Started)/).first(); | ||||||||||
| await expect(genericLogLine).toBeVisible({ timeout: 30000 }); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -63,8 +63,19 @@ export class ApplicationsPage { | |||||||||||||||
| await locator.press('Enter'); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async createApp(appName: string, repoUrl: string, repoPath: string) { | ||||||||||||||||
| async createApp(appName: string, repoUrl: string, repoPath: string) { | ||||||||||||||||
| await this.newAppButton.click(); | ||||||||||||||||
|
|
||||||||||||||||
| //handle the "failed to load data" banner if it appears inside the slide-out panel | ||||||||||||||||
| const errorBanner = this.page.getByText('try again'); | ||||||||||||||||
| try { | ||||||||||||||||
| //wait 3 secs | ||||||||||||||||
| await errorBanner.waitFor({ state: 'visible', timeout: 3000 }); | ||||||||||||||||
| await errorBanner.click(); | ||||||||||||||||
| } catch (error) { | ||||||||||||||||
| //banner didn't appear so just continue | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| await this.page.getByText('Loading...').first().waitFor({ state: 'hidden', timeout: 15000 }); | ||||||||||||||||
|
|
||||||||||||||||
| await this.appNameInput.fill(appName); | ||||||||||||||||
|
|
@@ -82,31 +93,33 @@ export class ApplicationsPage { | |||||||||||||||
| await this.createButton.click(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async syncApplication(appName: string, expectedResource: string = 'spring-petclinic') { | ||||||||||||||||
| async syncApplication(appName: string, expectedResource: string = 'spring-petclinic') { | ||||||||||||||||
| //search for app | ||||||||||||||||
| await this.page.getByPlaceholder(/Search applications/i).fill(appName); | ||||||||||||||||
|
|
||||||||||||||||
| const appContainer = this.page.locator('.white-box, .argo-table-list__row').filter({ hasText: appName }); | ||||||||||||||||
| await appContainer.waitFor({ state: 'visible', timeout: 20000 }); | ||||||||||||||||
| await expect(appContainer.getByText(/OutOfSync|Out of Sync/i).first()).toBeVisible({ timeout: 45000 }); | ||||||||||||||||
| //safe to open the panel | ||||||||||||||||
| await appContainer.getByText('Sync', { exact: true }).click(); | ||||||||||||||||
|
|
||||||||||||||||
| //slideout panel | ||||||||||||||||
| // Wait for the manifests to fetch from Git and render on the panel | ||||||||||||||||
| await expect(this.page.getByText(expectedResource).first()).toBeVisible({ timeout: 15000 }); | ||||||||||||||||
|
|
||||||||||||||||
| //click 'all' to ensure all resource checkboxes are ticked across all Argo CD versions | ||||||||||||||||
| //click 'all' | ||||||||||||||||
| const allLink = this.page.getByRole('link', { name: 'all', exact: true }); | ||||||||||||||||
| try { | ||||||||||||||||
| await allLink.waitFor({ state: 'visible', timeout: 3000 }); | ||||||||||||||||
| await allLink.waitFor({ state: 'visible', timeout: 5000 }); | ||||||||||||||||
| await allLink.click(); | ||||||||||||||||
| } catch (error) { | ||||||||||||||||
| //all link didn't appear within 3 sec | ||||||||||||||||
| // all link didn't appear within 5 sec | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| //wait for the manifests to render on the panel | ||||||||||||||||
| await expect(this.page.getByText(expectedResource).first()).toBeVisible({ timeout: 30000 }); | ||||||||||||||||
|
|
||||||||||||||||
| //click the main sync button | ||||||||||||||||
| await this.page.getByRole('button', { name: /^synchronize$/i }).first().click(); | ||||||||||||||||
|
Comment on lines
107
to
119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# First, explore the file structure and locate it
find . -type f -name "ApplicationsPage.ts" 2>/dev/null
# Get an outline of the file to understand its structure
ast-grep outline test/ui-e2e/src/pages/ApplicationsPage.ts --view expanded | head -100Repository: redhat-developer/gitops-operator Length of output: 1011 🏁 Script executed: #!/bin/bash
# Read the specific section in context (lines 95-130 to see surrounding code)
sed -n '95,130p' test/ui-e2e/src/pages/ApplicationsPage.tsRepository: redhat-developer/gitops-operator Length of output: 1716 🏁 Script executed: #!/bin/bash
# Search for "synchronize panel" or similar references to understand the panel structure
rg -n "panel|modal|dialog|synchronize" test/ui-e2e/src/pages/ApplicationsPage.ts -A 2 -B 2Repository: redhat-developer/gitops-operator Length of output: 1141 🏁 Script executed: #!/bin/bash
# Look for how other methods scope their selectors in this file
rg -n "getByRole|locator|within|filter" test/ui-e2e/src/pages/ApplicationsPage.ts | head -30Repository: redhat-developer/gitops-operator Length of output: 2152 Scope selectors for the 'all' link, expectedResource text, and synchronize button to the sync panel. Lines 107-119 use page-wide selectors ( 🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| //wait for the panel to close | ||||||||||||||||
| await expect(this.page.getByText('SYNCHRONIZE RESOURCES')).toBeHidden({ timeout: 10000 }); | ||||||||||||||||
| //wait for the panel to close | ||||||||||||||||
| await expect(this.page.getByText('SYNCHRONIZE RESOURCES')).toBeHidden({ timeout: 15000 }); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async verifyStatus(appName: string) { | ||||||||||||||||
|
|
@@ -118,4 +131,20 @@ async syncApplication(appName: string, expectedResource: string = 'spring-petcli | |||||||||||||||
| await expect(appContainer.getByText(/synced/i)).toBeVisible({ timeout: 90000 }); | ||||||||||||||||
| await expect(appContainer.getByText(/healthy/i)).toBeVisible({ timeout: 90000 }); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async openApplication(appName: string) { | ||||||||||||||||
| //re-apply search filter just in case the UI refreshed | ||||||||||||||||
| await this.page.getByPlaceholder(/Search applications/i).fill(appName); | ||||||||||||||||
|
|
||||||||||||||||
| //find the container, then specifically click the link of the app name | ||||||||||||||||
| const appLink = this.page.locator('.white-box, .argo-table-list__row') | ||||||||||||||||
| .filter({ hasText: appName }) | ||||||||||||||||
| .getByRole('link', { name: appName }); | ||||||||||||||||
|
|
||||||||||||||||
|
Comment on lines
+140
to
+143
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# First, locate the file in question
find . -type f -name "ApplicationsPage.ts" 2>/dev/null | head -20Repository: redhat-developer/gitops-operator Length of output: 214 🏁 Script executed: #!/bin/bash
# If found, examine the file structure and content around lines 140-143
if [ -f "test/ui-e2e/src/pages/ApplicationsPage.ts" ]; then
echo "=== File exists, examining structure ==="
wc -l test/ui-e2e/src/pages/ApplicationsPage.ts
echo ""
echo "=== AST outline to understand context ==="
ast-grep outline test/ui-e2e/src/pages/ApplicationsPage.ts --view expanded | head -100
fiRepository: redhat-developer/gitops-operator Length of output: 1097 🏁 Script executed: #!/bin/bash
# Read the specific lines with context
if [ -f "test/ui-e2e/src/pages/ApplicationsPage.ts" ]; then
echo "=== Lines 130-160 with context ==="
sed -n '130,160p' test/ui-e2e/src/pages/ApplicationsPage.ts | cat -n
fiRepository: redhat-developer/gitops-operator Length of output: 1301 🏁 Script executed: #!/bin/bash
# Check for similar patterns with getByRole in test files
rg "getByRole.*name:" test/ui-e2e --type typescript -A 1 -B 1 | head -40Repository: redhat-developer/gitops-operator Length of output: 205 🏁 Script executed: #!/bin/bash
# Check if there are test fixtures with similarly-named apps
rg "petclinic|app.*name|createApp" test/ui-e2e --type typescript | grep -E "(test|fixture|data|const)" | head -20Repository: redhat-developer/gitops-operator Length of output: 205 🏁 Script executed: #!/bin/bash
# Check for similar patterns with getByRole in test files (using ts type)
rg "getByRole.*name:" test/ui-e2e -A 1 -B 1 | head -40Repository: redhat-developer/gitops-operator Length of output: 2988 🏁 Script executed: #!/bin/bash
# Check test data and app creation patterns
rg "createApp|spring-petclinic|appName" test/ui-e2e/src -A 2 -B 1 | head -50Repository: redhat-developer/gitops-operator Length of output: 3873 🏁 Script executed: #!/bin/bash
# Check if syncApplication and verifyStatus have similar patterns
sed -n '96,130p' test/ui-e2e/src/pages/ApplicationsPage.ts | cat -nRepository: redhat-developer/gitops-operator Length of output: 1960 Use exact app-link matching in
Suggested fix- const appLink = this.page.locator('.white-box, .argo-table-list__row')
- .filter({ hasText: appName })
- .getByRole('link', { name: appName });
+ const appLink = this.page.locator('.white-box, .argo-table-list__row')
+ .filter({ has: this.page.getByRole('link', { name: appName, exact: true }) })
+ .getByRole('link', { name: appName, exact: true });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
| await appLink.waitFor({ state: 'visible', timeout: 15000 }); | ||||||||||||||||
| await appLink.click(); | ||||||||||||||||
|
|
||||||||||||||||
| //wait for the URL to change to the details page to ensure the click worked | ||||||||||||||||
| await expect(this.page).toHaveURL(/.*\/applications\/.*\/.*/, { timeout: 15000 }); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { test, expect } from '../src/fixtures'; | ||
| import { ApplicationDetailsPage } from '../src/pages/ApplicationDetailsPage'; | ||
| import { ApplicationsPage } from '../src/pages/ApplicationsPage'; | ||
|
|
||
| test.describe('Argo CD Resource Tree and Pod Logs', () => { | ||
|
|
||
| test.use({ storageState: '.auth/storageState.json' }); | ||
|
|
||
| test('Navigate to app details, open a Pod, and verify logs stream', async ({ page, managedApp }) => { | ||
| test.setTimeout(120000); | ||
|
|
||
| const appsPage = new ApplicationsPage(page); | ||
| const detailsPage = new ApplicationDetailsPage(page); | ||
|
|
||
| await appsPage.navigate(); | ||
| await page.getByPlaceholder(/Search applications/i).fill(managedApp); | ||
|
|
||
| //click the Application Name text/link | ||
| const appCard = page.locator('.white-box, .argo-table-list__row').filter({ hasText: managedApp }); | ||
| await appCard.getByText(managedApp, { exact: true }).first().click(); | ||
|
|
||
| //on details page | ||
| await detailsPage.verifyResourceTreeLoaded(); | ||
| //Deployment node | ||
| await detailsPage.clickResourceNode('deploy', 'spring-petclinic'); | ||
| await detailsPage.verifyPodLogs(); | ||
| }); | ||
|
|
||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Escape
|in the table cell to prevent broken renderingLine 71 currently creates an extra Markdown table column (
MD056). Escape the pipe in the inline flag so the table stays 2-column.Suggested fix
📝 Committable suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 71-71: Table column count
Expected: 2; Actual: 3; Too many cells, extra data will be missing
(MD056, table-column-count)
🤖 Prompt for AI Agents
Source: Linters/SAST tools