Skip to content

Commit 23c8791

Browse files
author
Triona Doyle
committed
Set up Playwright E2E foundation and automate the Argo CD SSO authentication
Signed-off-by: Triona Doyle <bot@example.com>
1 parent 02400d0 commit 23c8791

11 files changed

Lines changed: 467 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ kuttl-test.json
3131
# ignore vendor
3232
vendor/
3333
.vscode/
34+
.DS_Store

test/ui-e2e/.auth/setup.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { test as setup } from '@playwright/test';
2+
3+
const authFile = '.auth/storageState.json';
4+
5+
setup('authenticate to OpenShift Cluster', async ({ page, baseURL }) => {
6+
// Navigate to the OpenShift Console
7+
const targetUrl = baseURL || process.env.CONSOLE_URL || process.env.BASE_URL;
8+
9+
if (!targetUrl) {
10+
throw new Error("No Console URL provided! Ensure your bash script exports BASE_URL or CONSOLE_URL.");
11+
}
12+
13+
console.log(`Navigating to OpenShift Console: ${targetUrl}`);
14+
await page.goto(targetUrl);
15+
16+
//set locators
17+
const idpScreenText = page.getByText(/Log in with/i);
18+
const usernameInput = page.getByLabel(/Username/i)
19+
.or(page.locator('input[name="username"]'))
20+
.or(page.getByPlaceholder(/Username/i));
21+
22+
//wait for the IDP screen OR the Username field to appear
23+
try {
24+
await Promise.race([
25+
idpScreenText.waitFor({ state: 'visible', timeout: 15000 }),
26+
usernameInput.waitFor({ state: 'visible', timeout: 15000 })
27+
]);
28+
} catch (e) {
29+
console.log("Timed out waiting for OpenShift login page to render.");
30+
}
31+
32+
const idpName = process.env.IDP || 'kube:admin';
33+
const user = process.env.CLUSTER_USER || 'kubeadmin';
34+
35+
if (await idpScreenText.isVisible()) {
36+
console.log(`IDP selection screen detected. Selecting provider: "${idpName}"`);
37+
38+
// look for the specific IDP
39+
const idpLink = page.getByRole('link', { name: new RegExp(idpName, 'i') });
40+
41+
await idpLink.waitFor({ state: 'visible', timeout: 5000 });
42+
await idpLink.click();
43+
} else {
44+
console.log("No IDP screen detected (or already selected), proceeding to credentials...");
45+
}
46+
47+
// fill in the Credentials
48+
await usernameInput.waitFor({ state: 'visible', timeout: 10000 });
49+
await usernameInput.fill(user);
50+
51+
const passwordInput = page.getByLabel(/Password/i)
52+
.or(page.locator('input[name="password"]'))
53+
.or(page.getByPlaceholder(/Password/i));
54+
55+
if (!process.env.CLUSTER_PASSWORD) {
56+
throw new Error("CLUSTER_PASSWORD is not set in the environment!");
57+
}
58+
59+
await passwordInput.fill(process.env.CLUSTER_PASSWORD);
60+
await page.getByRole('button', { name: /Log in/i }).click();
61+
62+
//save the auth state
63+
await page.waitForLoadState('networkidle');
64+
await page.context().storageState({ path: authFile });
65+
});

test/ui-e2e/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
# Playwright
3+
node_modules/
4+
/test-results/
5+
/playwright-report/
6+
/blob-report/
7+
/playwright/.cache/
8+
/playwright/.auth/
9+
.auth/storageState.json
10+
.env

test/ui-e2e/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# GitOps Operator - UI End-to-End Tests
2+
3+
This suite validates the OpenShift GitOps Operator UI, focusing on Argo CD and SSO integration.
4+
5+
## Prerequisites
6+
1. **Node.js** (v18+)
7+
2. **OpenShift CLI (oc)**: Installed and in your PATH.
8+
3. **Install Dependencies:** Navigate to this directory and install required packages:
9+
```bash
10+
cd test/ui-e2e
11+
npm install
12+
npx playwright install chromium
13+
```
14+
15+
## Environment Variables
16+
You must provide cluster credentials before running tests. You can either `export` these in your terminal (or pipeline), or create a `.env` file in the `test/ui-e2e` directory:
17+
18+
```text
19+
# .env file example
20+
CLUSTER_PASSWORD=your_openshift_admin_password
21+
OC_API_URL=[https://api.cluster.com:6443](https://api.cluster.com:6443)
22+
CLUSTER_USER=kubeadmin # (Optional) Defaults to kubeadmin
23+
IDP=kube:admin # (Optional) Defaults to kube:admin
24+
```
25+
26+
## Execution Commands
27+
28+
All commands use the `./run-ui-tests.sh` wrapper which handles auth, OpenShift token generation, and URL discovery. **Ensure you are in the `test/ui-e2e` directory.**
29+
30+
**Run All Tests (Headless):**
31+
```bash
32+
./run-ui-tests.sh --project=chromium
33+
```
34+
35+
**Run All Tests (Headed + Trace):**
36+
```bash
37+
./run-ui-tests.sh --project=chromium --headed --reporter=list --trace on
38+
```
39+
40+
**Run Single Test (Headed + Trace):**
41+
```bash
42+
./run-ui-tests.sh tests/login.spec.ts --project=chromium --headed --trace on
43+
```
44+
45+
**View Trace Results:**
46+
```bash
47+
npx playwright show-trace test-results/**/*/trace.zip
48+
```
49+
50+
** Helpful Flags Explained**
51+
* `--headed`: Runs tests in a visible browser. Without this, tests run in "headless" mode (invisible background).
52+
* `--reporter=list`: Changes console output to a clean, line-by-line list so you can see exactly which test is running in real-time.
53+
* `--trace on`: Captures a full "recording" (DOM snapshots, network, actions) of the test for debugging.
54+
55+
## Architecture
56+
57+
**Global Setup:**
58+
`.auth/setup.ts` logs into the OCP console to generate a reusable session (`storageState.json`). This prevents having to log in repeatedly for every test file.
59+
60+
**Spec Isolation:**
61+
`login.spec.ts` explicitly clears session cookies to force a full SSO UI validation from a fresh state.
62+
63+
## Troubleshooting
64+
65+
* **"Invalid login or password" during automated login:** If you are testing against multiple clusters sequentially, your terminal's `oc` CLI might be holding onto a sticky session from an older cluster. Run `oc logout` before running the bash script to force a clean authentication.

test/ui-e2e/package-lock.json

Lines changed: 111 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/ui-e2e/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "ui-e2e",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"scripts": {},
6+
"keywords": [],
7+
"author": "",
8+
"license": "ISC",
9+
"description": "",
10+
"devDependencies": {
11+
"@playwright/test": "^1.59.1",
12+
"@types/node": "^25.6.0",
13+
"dotenv": "^17.4.2"
14+
}
15+
}

test/ui-e2e/playwright.config.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { defineConfig, devices } from '@playwright/test';
2+
3+
/**
4+
* Read environment variables from file.
5+
* https://github.com/motdotla/dotenv
6+
*/
7+
8+
// top of playwright.config.ts
9+
import dotenv from 'dotenv';
10+
import path from 'path';
11+
dotenv.config({ path: path.resolve(__dirname, '.env') });
12+
13+
/**
14+
* See https://playwright.dev/docs/test-configuration.
15+
*/
16+
export default defineConfig({
17+
testDir: './tests',
18+
/* Run tests in files in parallel */
19+
fullyParallel: true,
20+
/* Fail the build on CI if you accidentally left test.only in the source code. */
21+
forbidOnly: !!process.env.CI,
22+
/* Retry on CI only */
23+
retries: process.env.CI ? 2 : 0,
24+
/* Opt out of parallel tests on CI. */
25+
workers: process.env.CI ? 1 : undefined,
26+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
27+
reporter: [
28+
['list'],
29+
['html', { open: process.env.CI ? 'never' : 'on-failure' }]
30+
],
31+
32+
/* GLOBAL FOUNDATION: These apply to everything */
33+
use: {
34+
baseURL: process.env.ARGOCD_URL,
35+
ignoreHTTPSErrors: true,
36+
trace: 'on-first-retry',
37+
},
38+
39+
/* Configure for major browsers */
40+
projects: [
41+
{
42+
name: 'setup',
43+
testDir: './',
44+
testMatch: '**/.auth/setup.ts',
45+
/* Only changes the URL for this specific project */
46+
use: {
47+
baseURL: process.env.CONSOLE_URL, },
48+
},
49+
50+
// Update chromium project
51+
{
52+
name: 'chromium',
53+
dependencies: ['setup'],
54+
use: {
55+
...devices['Desktop Chrome'],
56+
storageState: '.auth/storageState.json',
57+
// project still has ignoreHTTPSErrors: true from above
58+
},
59+
},
60+
61+
{
62+
name: 'firefox',
63+
use: {
64+
...devices['Desktop Firefox'],
65+
// storageState and dependencies here later if we want to run Firefox tests but for now just focus on Chromium
66+
},
67+
},
68+
// ... webkit etc ...
69+
],
70+
71+
/* Run your local dev server before starting the tests */
72+
// webServer: {
73+
// command: 'npm run start',
74+
// url: 'http://localhost:3000',
75+
// reuseExistingServer: !process.env.CI,
76+
// },
77+
});

test/ui-e2e/run-ui-tests.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/bash
2+
3+
#making sure we are in the correct dir
4+
cd "$(dirname "$0")" || exit 1
5+
6+
# username (might be something different for rosa - can be overwritten with export CLUSTER_USER)
7+
export CLUSTER_USER=${CLUSTER_USER:-"kubeadmin"}
8+
export IDP=${IDP:-"kube:admin"}
9+
10+
#check auth state first
11+
echo "Checking cluster authentication..."
12+
if ! oc whoami > /dev/null 2>&1; then
13+
if [ -n "$OC_API_URL" ] && [ -n "$CLUSTER_PASSWORD" ]; then
14+
echo "Attempting automated login..."
15+
oc login "$OC_API_URL" -u "$CLUSTER_USER" -p "$CLUSTER_PASSWORD" --insecure-skip-tls-verify=true
16+
else
17+
echo "Error: Not logged in. Missing OC_API_URL or CLUSTER_PASSWORD."
18+
exit 1
19+
fi
20+
fi
21+
22+
#find the URLs for console and argocd
23+
echo "Discovering component URLs..."
24+
export ARGOCD_URL=$(oc get route openshift-gitops-server -n openshift-gitops -o jsonpath='{"https://"}{.spec.host}')
25+
export CONSOLE_URL=$(oc whoami --show-console)
26+
27+
if [ -z "$ARGOCD_URL" ] || [ -z "$CONSOLE_URL" ]; then
28+
echo "Error: Could not find Argo CD or Console routes."
29+
exit 1
30+
fi
31+
32+
echo "OpenShift Console: $CONSOLE_URL"
33+
echo " Argo CD UI: $ARGOCD_URL"
34+
35+
#clean up any old Playwright state
36+
echo "Getting rid of any old browser sessions..."
37+
rm -f .auth/storageState.json || true
38+
39+
#run Playwright
40+
echo " Starting Playwright tests..."
41+
npx playwright test "$@"

0 commit comments

Comments
 (0)