-
Notifications
You must be signed in to change notification settings - Fork 36
OCPNETUI-56: Add Cypress test specs and runner scripts #470
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: main
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 |
|---|---|---|
| @@ -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 |
| 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: | ||
| BRIDGE_BASE_ADDRESS: ${{ secrets.CONSOLE_URL }} | ||
|
Contributor
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. 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.
Contributor
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. 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 }} | ||
|
Contributor
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. this should not be needed. |
||
| BRIDGE_E2E_BROWSER_NAME: electron | ||
| TEST_NS: ${{ secrets.TEST_NS || 'cy-test-ns' }} | ||
|
Contributor
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. why we use secrets to store a namspace name? |
||
| UDN_NS: udn-test-ns | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
coderabbitai[bot] marked this conversation as resolved.
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. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Pin every GitHub Action to a full commit SHA.
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 AgentsSource: Path instructions
Contributor
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. it may be a follow-up but we should update all the actions - the current is v7
Contributor
Author
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. i'll fix it now |
||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 | ||
| cache: npm | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
Contributor
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. in this install path we rely on post-install scripts which is insecure.
Contributor
Author
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. fixed |
||
|
|
||
| - name: Run E2E tests | ||
| run: npm run test-e2e | ||
|
|
||
| - name: Upload test artifacts | ||
| if: always() | ||
|
Contributor
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. 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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,3 +6,5 @@ integration-tests/screenshots | |
| integration-tests/.DS_Store | ||
| yarn-error.log | ||
| .DS_Store | ||
| cypress/gui-test-screenshots/ | ||
|
Contributor
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. the path |
||
| .env | ||
| 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." | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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", | ||||||
|
|
@@ -59,6 +61,7 @@ | |||||
| "css-loader": "^6.7.1", | ||||||
| "cypress": "^15.0.0", | ||||||
| "cypress-multi-reporters": "^2.0.0", | ||||||
| "dotenv": "^17.4.2", | ||||||
|
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. 🔒 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 --shortRepository: 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.toolRepository: openshift/networking-console-plugin Length of output: 1346 Pin 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
Suggested change
🤖 Prompt for AI AgentsSource: Path instructions |
||||||
| "esbuild-loader": "^4.4.2", | ||||||
| "eslint": "^8.57.1", | ||||||
| "eslint-config-prettier": "^8.10.0", | ||||||
|
|
||||||
| 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" |
| 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." | ||
| } |
| 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 |
| 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" | ||
| } | ||
| } |
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.
HIDE_XHRneeds to be set to true for public env