Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BRIDGE_BASE_ADDRESS=https://console-openshift-console.apps.your-cluster.example.com
BRIDGE_KUBEADMIN_PASSWORD=your-password
TEST_NS=cy-test-ns
UDN_NS=udn-test-ns
HIDE_XHR=true
52 changes: 52 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: E2E Tests

on:
pull_request:
branches: [main, release-*]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: e2e-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true

jobs:
e2e:
name: Cypress E2E
runs-on: ubuntu-latest
timeout-minutes: 30

env:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIDE_XHR needs to be set to true for public env

BRIDGE_BASE_ADDRESS: ${{ secrets.CONSOLE_URL }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the tests seem to be run via dev mode - in case of networking plugin it may be easier to build an image and update the env var in cluster network operator.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for hot cluster it makes sense. however still we should be using a cluster network operator compatible with our plugin. Do we have some way of checking that?

BRIDGE_KUBEADMIN_PASSWORD: ${{ secrets.KUBEADMIN_PASSWORD }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should not be needed.
CC: @sjd78

BRIDGE_E2E_BROWSER_NAME: electron
TEST_NS: ${{ secrets.TEST_NS || 'cy-test-ns' }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we use secrets to store a namspace name?

UDN_NS: udn-test-ns

steps:
- name: Checkout
uses: actions/checkout@v4
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin every GitHub Action to a full commit SHA.

actions/checkout@v4, actions/setup-node@v4, and actions/upload-artifact@v4 use mutable tags. Replace each tag with a full commit SHA and retain an adjacent version comment.

As per path instructions, “Pin actions by full SHA, not tag.”

Also applies to: 30-30, 43-43

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 26-27: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/e2e.yml at line 27, Update the GitHub Actions references
for checkout, setup-node, and upload-artifact to use their full immutable commit
SHAs instead of version tags, while retaining an adjacent comment identifying
each action’s version.

Source: Path instructions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it may be a follow-up but we should update all the actions - the current is v7

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll fix it now

with:
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Install dependencies
run: npm ci

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this install path we rely on post-install scripts which is insecure.
Alternative way is to disable post-install and install cypress explicitly:

npm clean-install --ignore-scripts --no-audit
npx cypress install

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


- name: Run E2E tests
run: npm run test-e2e

- name: Upload test artifacts
if: always()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it's enough to upload screenshots on failure?

uses: actions/upload-artifact@v4
with:
name: e2e-results
path: ui-tests-cy/gui-test-screenshots/
retention-days: 7
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ integration-tests/screenshots
integration-tests/.DS_Store
yarn-error.log
.DS_Store
cypress/gui-test-screenshots/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the path cypress does not exist

.env
22 changes: 22 additions & 0 deletions cleanup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

# Cleanup test resources from previous runs
# Sources .env for namespace names

source .env 2>/dev/null || true
TEST_NS="${TEST_NS:-cy-test-ns}"
UDN_NS="${UDN_NS:-udn-test-ns}"

cleanup () {
echo "Cleaning up test resources..."
oc delete --ignore-not-found=true -n "${TEST_NS}" net-attach-def network-bridge network-ovn network-localnet --wait=false
oc delete --ignore-not-found=true -n "${TEST_NS}" service example --wait=false
oc delete --ignore-not-found=true -n "${TEST_NS}" route auto-test-route --wait=false
oc delete --ignore-not-found=true -n "${TEST_NS}" ingress example --wait=false
oc delete --ignore-not-found=true -n "${TEST_NS}" networkpolicy auto-test-net-policy --wait=true --timeout=60s
oc delete --ignore-not-found=true -n "${TEST_NS}" multi-networkpolicy auto-test-multi-policy --wait=false
oc delete --ignore-not-found=true ClusterUserDefinedNetwork cluster-udn --wait=false
oc delete --ignore-not-found=true -n "${UDN_NS}" UserDefinedNetwork primary-udn --wait=false
oc delete --ignore-not-found=true nncp -l app.kubernetes.io/managed-by=networking-console-plugin-test --wait=false
echo "Cleanup done."
}
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
"start-console": "./start-console.sh",
"i18n": "./i18n-scripts/build-i18n.sh && node ./i18n-scripts/set-english-defaults.js",
"ts-node": "ts-node -O '{\"module\":\"commonjs\"}'",
"lint": "eslint ./src ./integration-tests",
"lint-fix": "eslint ./src ./integration-tests --fix",
"test-cypress": "cd integration-tests s && cypress open --env openshift=true",
"lint": "eslint ./src ./integration-tests ./ui-tests-cy",
"lint-fix": "eslint ./src ./integration-tests ./ui-tests-cy --fix",
"test-cypress": "cd integration-tests && cypress open --env openshift=true",
"test-cypress-headless": "cd integration-tests && node --max-old-space-size=4096 ../node_modules/.bin/cypress run --env openshift=true --browser ${BRIDGE_E2E_BROWSER_NAME:=electron}",
"cypress-merge": "mochawesome-merge ./integration-tests/screenshots/cypress_report*.json > ./integration-tests/screenshots/cypress.json",
"cypress-generate": "marge -o ./integration-tests/screenshots/ -f cypress-report -t 'OpenShift Console Plugin Template Cypress Test Results' -p 'OpenShift Cypress Plugin Template Test Results' --showPassed false --assetsDir ./integration-tests/screenshots/cypress/assets ./integration-tests/screenshots/cypress.json",
"test-e2e": "node --max-old-space-size=4096 node_modules/.bin/cypress run --project ui-tests-cy --env openshift=true --browser ${BRIDGE_E2E_BROWSER_NAME:=electron}",
"test-e2e-open": "cypress open --project ui-tests-cy --env openshift=true",
"cypress-merge": "mochawesome-merge ./ui-tests-cy/gui-test-screenshots/cypress_report*.json > ./ui-tests-cy/gui-test-screenshots/cypress.json",
"cypress-generate": "marge -o ./ui-tests-cy/gui-test-screenshots/ -f cypress-report -t 'Networking Console Plugin Test Results' -p 'Networking Plugin Test Results' --showPassed false --assetsDir ./ui-tests-cy/gui-test-screenshots/assets ./ui-tests-cy/gui-test-screenshots/cypress.json",
"cypress-postreport": "npm run cypress-merge && npm run cypress-generate",
"memsource-upload": "./i18n-scripts/memsource-upload.sh",
"memsource-download": "./i18n-scripts/memsource-download.sh",
Expand Down Expand Up @@ -59,6 +61,7 @@
"css-loader": "^6.7.1",
"cypress": "^15.0.0",
"cypress-multi-reporters": "^2.0.0",
"dotenv": "^17.4.2",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- package manifests and lockfiles ---'
git ls-files '*package*.json' '*lock*' | sed -n '1,120p'
printf '%s\n' '--- dotenv references ---'
rg -n -C 3 '"dotenv"|dotenv@' --glob 'package*.json' --glob '*lock*' .
printf '%s\n' '--- package.json context ---'
cat -n package.json | sed -n '50,72p'
printf '%s\n' '--- repository status ---'
git status --short

Repository: openshift/networking-console-plugin

Length of output: 2243


🏁 Script executed:

#!/bin/bash
python3 - <<'PY'
import json
from pathlib import Path

for name in ("package.json", "package-lock.json"):
    data = json.loads(Path(name).read_text())
    print(f"--- {name} ---")
    if name == "package-lock.json":
        print("lockfileVersion:", data.get("lockfileVersion"))
        root = data.get("packages", {}).get("", {})
        print("root dotenv:", root.get("dependencies", {}).get("dotenv"))
        entry = data.get("packages", {}).get("node_modules/dotenv")
        print("node_modules/dotenv:", entry)
    else:
        print("dotenv:", data.get("dependencies", {}).get("dotenv"))
PY

printf '%s\n' '--- npm registry metadata for dotenv@17.4.2 ---'
curl -fsSL 'https://registry.npmjs.org/dotenv/17.4.2' |
  python3 -c 'import json,sys; d=json.load(sys.stdin); print(json.dumps({k:d.get(k) for k in ("name","version","license","licenses","dist","deprecated")}, indent=2, sort_keys=True))'

printf '%s\n' '--- OSV advisories for npm dotenv ---'
curl -fsSL -X POST 'https://api.osv.dev/v1/query' \
  -H 'Content-Type: application/json' \
  --data '{"package":{"ecosystem":"npm","name":"dotenv"},"version":"17.4.2"}' |
  python3 -m json.tool

Repository: openshift/networking-console-plugin

Length of output: 1346


Pin dotenv to 17.4.2.

The lockfile already records the exact version, integrity hash, BSD-2-Clause license, and no OSV advisories. Update the manifest to prevent unintended upgrades.

Proposed manifest fix
-    "dotenv": "^17.4.2",
+    "dotenv": "17.4.2",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"dotenv": "^17.4.2",
"dotenv": "17.4.2",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 64, Update the dotenv dependency declaration in
package.json from the caret range to the exact version 17.4.2, preserving the
lockfile’s resolved version and metadata.

Source: Path instructions

"esbuild-loader": "^4.4.2",
"eslint": "^8.57.1",
"eslint-config-prettier": "^8.10.0",
Expand Down
120 changes: 120 additions & 0 deletions research-flakiness.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/usr/bin/env bash

set -x
set +e

PROBLEM_DIR="problem"
TOTAL_RUNS=10
PASS_COUNT=0
FAIL_COUNT=0

mkdir -p "${PROBLEM_DIR}"

for i in $(seq -w 1 ${TOTAL_RUNS}); do
echo "========================================"
echo " RUN ${i} of ${TOTAL_RUNS}"
echo "========================================"

# Clean previous test artifacts
rm -rf cypress/gui-test-screenshots/*

# Run the test suite
./test-cypress.sh
exit_code=$?

if [ ${exit_code} -ne 0 ]; then
FAIL_COUNT=$((FAIL_COUNT + 1))
RUN_DIR="${PROBLEM_DIR}/run${i}"
mkdir -p "${RUN_DIR}"

# Copy screenshots and videos
if [ -d "cypress/gui-test-screenshots/screenshots" ]; then
cp -r cypress/gui-test-screenshots/screenshots "${RUN_DIR}/"
fi
if [ -d "cypress/gui-test-screenshots/videos" ]; then
cp -r cypress/gui-test-screenshots/videos "${RUN_DIR}/"
fi
if [ -f "cypress/gui-test-screenshots/build.log" ]; then
cp cypress/gui-test-screenshots/build.log "${RUN_DIR}/"
fi

# Extract failure info into analysis.md
cat > "${RUN_DIR}/analysis.md" <<ANALYSIS
# Run ${i} — FAILED

## Failing tests

$(grep -E "^\s+\d\)" cypress/gui-test-screenshots/build.log 2>/dev/null || echo "Could not extract test names")

## Error messages

$(grep -A5 "^\s+\d\) " cypress/gui-test-screenshots/build.log 2>/dev/null | head -60 || echo "Could not extract errors")

## Observations

- The NAD creation form uses react-hook-form with auto-generated names
- The naive approach (clear + type without waits) races with React re-renders
- The form may overwrite user input after async state updates

ANALYSIS

echo " -> FAILED (evidence saved to ${RUN_DIR}/)"
else
PASS_COUNT=$((PASS_COUNT + 1))
echo " -> PASSED"
fi
done

echo ""
echo "========================================"
echo " RESULTS: ${PASS_COUNT} passed, ${FAIL_COUNT} failed out of ${TOTAL_RUNS} runs"
echo "========================================"

# Write summary
cat > "${PROBLEM_DIR}/summary.md" <<SUMMARY
# NAD Form Flakiness Research — Summary

## Results

- **Total runs:** ${TOTAL_RUNS}
- **Passed:** ${PASS_COUNT}
- **Failed:** ${FAIL_COUNT}
- **Failure rate:** $((FAIL_COUNT * 100 / TOTAL_RUNS))%

## Problem Description

The NetworkAttachmentDefinition (NAD) creation form in the networking-console-plugin
uses \`react-hook-form\` which auto-generates a random name on mount. When Cypress
types into the name field immediately after the form renders, React's asynchronous
state updates can overwrite the typed value with the auto-generated name.

### Root Cause

The form component calls \`register('name', { required: true })\` which creates a
controlled input. On initial render (and on network-type selection), the form state
resets and generates a new random name. If Cypress types before this reset completes,
the typed value is lost.

### Affected Tests

- NAD Bridge creation (name overwritten by auto-generated value)
- NAD Localnet creation (same issue)
- NAD OVN/L2 overlay creation (same issue, plus submit button stays disabled)

### Evidence

Each \`runXX/\` folder contains:
- \`screenshots/\` — failure screenshots showing the form state at time of failure
- \`videos/\` — full test recording
- \`build.log\` — Cypress console output
- \`analysis.md\` — per-run failure details

### Recommended Fix (for developers)

1. Remove the auto-generated name from the form initial state, OR
2. Add a \`data-test-ready\` attribute after the form has fully initialized, so tests can wait for it, OR
3. Debounce/stabilize the form state before allowing user input

SUMMARY

echo "Summary written to ${PROBLEM_DIR}/summary.md"
43 changes: 43 additions & 0 deletions setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash

# Setup test namespaces (create if they don't exist)
# Sources .env for namespace names

source .env 2>/dev/null || true
TEST_NS="${TEST_NS:-cy-test-ns}"
UDN_NS="${UDN_NS:-udn-test-ns}"
UDN_LABEL="k8s.ovn.org/primary-user-defined-network"

setup () {
echo "Setting up test namespaces..."
oc get namespace ${TEST_NS} 2>/dev/null || oc create namespace ${TEST_NS}

# UDN namespace must have the label at creation time (admission policy prevents adding later)
if oc get namespace ${UDN_NS} 2>/dev/null; then
if ! oc get namespace ${UDN_NS} -o jsonpath='{.metadata.labels}' | grep -q "${UDN_LABEL}"; then
echo "UDN namespace exists but missing label, recreating..."
oc delete namespace ${UDN_NS} --wait=true --timeout=120s
oc wait --for=delete namespace/${UDN_NS} --timeout=120s 2>/dev/null || true
oc create -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: ${UDN_NS}
labels:
${UDN_LABEL}: ""
EOF
else
echo "UDN namespace exists with proper label."
fi
else
oc create -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: ${UDN_NS}
labels:
${UDN_LABEL}: ""
EOF
fi
echo "Setup done."
}
37 changes: 37 additions & 0 deletions test-cypress.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash

set -x -o pipefail

source ./cleanup.sh
source ./setup.sh

while getopts g:s: flag
do
case "${flag}" in
g) gui=${OPTARG};;
s) spec=${OPTARG};;
esac
done

if [ -z "${spec-}" ]; then
spec="tests/all.cy.ts"
fi

cleanup
setup

mkdir -p ui-tests-cy/gui-test-screenshots

if [ -n "${gui-}" ]; then
node_modules/.bin/cypress open --project ui-tests-cy --env openshift=true --spec "$spec"
else
node --max-old-space-size=4096 node_modules/.bin/cypress run --project ui-tests-cy --env openshift=true --browser "${BRIDGE_E2E_BROWSER_NAME:=electron}" --spec "$spec" | tee ui-tests-cy/gui-test-screenshots/build.log
test_exit_code=${PIPESTATUS[0]}
npm run cypress-postreport

if [ ${test_exit_code} -eq 0 ]; then
cleanup
fi

exit ${test_exit_code}
fi
17 changes: 17 additions & 0 deletions ui-tests-cy/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"env": {
"cypress/globals": true,
"node": true
},
"extends": ["../.eslintrc.json", "plugin:cypress/recommended"],
"plugins": ["cypress"],
"rules": {
"no-console": "off",
"no-namespace": "off",
"no-redeclare": "off",
"promise/catch-or-return": "off",
"promise/no-nesting": "off",
"@typescript-eslint/no-var-requires":"off",
"@typescript-eslint/no-namespace":"off"
}
}
Loading