diff --git a/.github/workflows/test-renovate-ui.yml b/.github/workflows/test-renovate-ui.yml new file mode 100644 index 0000000..07a9a5f --- /dev/null +++ b/.github/workflows/test-renovate-ui.yml @@ -0,0 +1,96 @@ +name: Test UI on Renovate PRs + +on: + workflow_dispatch: + inputs: + debug_shell: + description: "Debug shell" + required: true + type: boolean + pull_request: + branches: + - main + paths: + - "ui/**" + - "build-images.sh" + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + check-author: + runs-on: ubuntu-latest + permissions: {} + outputs: + is_renovate: ${{ steps.check.outputs.is_renovate }} + steps: + - id: check + env: + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + run: | + # On workflow_dispatch, PR_AUTHOR is empty — allow the run. + # On pull_request, only allow renovate[bot]. + if [[ -z "$PR_AUTHOR" || "$PR_AUTHOR" == "renovate[bot]" ]]; then + echo "is_renovate=true" >> "$GITHUB_OUTPUT" + else + echo "is_renovate=false" >> "$GITHUB_OUTPUT" + fi + + publish-images: + needs: check-author + if: needs.check-author.outputs.is_renovate == 'true' + uses: NethServer/ns8-github-actions/.github/workflows/publish-branch.yml@v1 + permissions: + packages: write + actions: read + contents: write + + module: + needs: publish-images + permissions: {} + uses: NethServer/ns8-github-actions/.github/workflows/module-info.yml@v1 + + chooser: + needs: check-author + if: needs.check-author.outputs.is_renovate == 'true' + runs-on: ubuntu-latest + permissions: {} + outputs: + node_a: ${{ steps.pick.outputs.node_a }} + node_b: ${{ steps.pick.outputs.node_b }} + steps: + - id: pick + run: | + if (( $GITHUB_RUN_NUMBER % 2 )); then + echo "node_a=rl1" >> "$GITHUB_OUTPUT" + echo "node_b=dn1" >> "$GITHUB_OUTPUT" + else + echo "node_a=dn1" >> "$GITHUB_OUTPUT" + echo "node_b=rl1" >> "$GITHUB_OUTPUT" + fi + + run_ui_tests: + needs: [module, chooser] + permissions: {} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + scenario: [install, update] + uses: NethServer/ns8-github-actions/.github/workflows/test-on-digitalocean-infra.yml@v1 + with: + script: test-ui.sh + path: ui + coremodules: ${{ matrix.scenario == 'install' && format('ghcr.io/{0}/{1}:{2}', needs.module.outputs.owner, needs.module.outputs.name, needs.module.outputs.tag) || '' }} + leader_nodes: >- + ${{ + matrix.scenario == 'install' + && needs.chooser.outputs.node_a + || needs.chooser.outputs.node_b + }} + args: ${{ format('ghcr.io/{0}/{1}:{2} -v SCENARIO:{3}', needs.module.outputs.owner, needs.module.outputs.name, needs.module.outputs.tag, matrix.scenario) }} + repo_ref: ${{needs.module.outputs.sha}} + debug_shell: ${{ github.event.inputs.debug_shell == 'true' || false }} + secrets: + do_token: ${{ secrets.do_token }} diff --git a/ui/.gitignore b/ui/.gitignore index 403adbc..a7f0426 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -21,3 +21,6 @@ pnpm-debug.log* *.njsproj *.sln *.sw? + +# tests outputs +tests/outputs diff --git a/ui/test-ui.sh b/ui/test-ui.sh new file mode 100755 index 0000000..74b3e7a --- /dev/null +++ b/ui/test-ui.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# +# Copyright (C) 2026 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +set -e + +SSH_KEYFILE=${SSH_KEYFILE:-$HOME/.ssh/id_rsa} + +LEADER_NODE="${1:?missing LEADER_NODE argument}" +IMAGE_URL="${2:?missing IMAGE_URL argument}" + +ssh_key="$(< $SSH_KEYFILE)" + +cleanup() { + set +e + podman cp rf-core-runner:/home/pwuser/outputs tests/ + podman stop rf-core-runner + podman rm rf-core-runner +} + +trap cleanup EXIT + +podman run -i \ + --network=host \ + --volume=.:/home/pwuser/ns8-module:z \ + --name rf-core-runner ghcr.io/marketsquare/robotframework-browser/rfbrowser-stable:19.11.0 \ + bash -l -s < /home/pwuser/ns8-key +pip install -q -r /home/pwuser/ns8-module/tests/pythonreq.txt +cd /home/pwuser/ns8-module/ui +mkdir -vp tests/outputs +exec robot \ + -v NODE_ADDR:${LEADER_NODE} \ + -v IMAGE_URL:${IMAGE_URL} \ + -v SSH_KEYFILE:/home/pwuser/ns8-key \ + --name ui-tests \ + -d tests/outputs tests/ +EOF diff --git a/ui/tests/pythonreq.txt b/ui/tests/pythonreq.txt new file mode 100644 index 0000000..279c2b3 --- /dev/null +++ b/ui/tests/pythonreq.txt @@ -0,0 +1,3 @@ +robotframework +robotframework-sshlibrary +robotframework-browser diff --git a/ui/tests/test_ui.robot b/ui/tests/test_ui.robot new file mode 100644 index 0000000..4710b70 --- /dev/null +++ b/ui/tests/test_ui.robot @@ -0,0 +1,51 @@ +*** Settings *** +Library SSHLibrary +Library Browser +Suite Setup Connect to the node + +*** Variables *** +${SSH_KEYFILE} %{HOME}/.ssh/id_ecdsa +${ADMIN_USER} admin +${ADMIN_PASSWORD} Nethesis,1234 + +*** Keywords *** +Connect to the node + Open Connection ${NODE_ADDR} + Login With Public Key root ${SSH_KEYFILE} + ${output} = Execute Command systemctl is-system-running --wait + Should Be True '${output}' == 'running' or '${output}' == 'degraded' + +Login to cluster-admin + New Page https://${NODE_ADDR}/cluster-admin/ + Fill Text text="Username" ${ADMIN_USER} + Click button >> text="Continue" + Fill Text text="Password" ${ADMIN_PASSWORD} + Click button >> text="Log in" + Wait For Elements State css=#main-content visible timeout=10s + +*** Test Cases *** +Install module + ${output} ${rc} = Execute Command add-module ${IMAGE_URL} 1 + ... return_rc=True + Should Be Equal As Integers ${rc} 0 + &{output} = Evaluate ${output} + Set Suite Variable ${module_id} ${output.module_id} + +Take screenshots + New Browser chromium headless=True + New Context ignoreHTTPSErrors=True + Login to cluster-admin + Go To https://${NODE_ADDR}/cluster-admin/#/apps/${module_id} + Wait For Elements State iframe >>> h2 >> text="Status" visible timeout=10s + Sleep 5s + Take Screenshot filename=${OUTPUT DIR}/browser/screenshot/status.png + Go To https://${NODE_ADDR}/cluster-admin/#/apps/${module_id}?page=settings + Wait For Elements State iframe >>> h2 >> text="Settings" visible timeout=10s + Sleep 5s + Take Screenshot filename=${OUTPUT DIR}/browser/screenshot/settings.png + Close Browser + +Remove module + ${rc} = Execute Command remove-module --no-preserve ${module_id} + ... return_rc=True return_stdout=False + Should Be Equal As Integers ${rc} 0