From 8011db092288f2986ccfe425975d969b98f27a99 Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:01:35 -0500 Subject: [PATCH 1/9] feat: add playwright skeleton Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- .env | 6 +- .github/actions/start-console/action.yml | 81 +++++++++++++ .github/workflows/ci-e2e-template.yaml | 140 +++++++++++++++++++++++ .github/workflows/ci-e2e.yaml | 79 +++++++++++++ README.md | 17 +-- docker-compose.yaml | 12 ++ e2e/.gitignore | 8 ++ e2e/package.json | 23 ++++ e2e/playwright.config.ts | 87 ++++++++++++++ e2e/tests/example.spec.ts | 18 +++ package-lock.json | 50 +++++--- package.json | 8 +- 12 files changed, 504 insertions(+), 25 deletions(-) create mode 100644 .github/actions/start-console/action.yml create mode 100644 .github/workflows/ci-e2e-template.yaml create mode 100644 .github/workflows/ci-e2e.yaml create mode 100644 e2e/.gitignore create mode 100644 e2e/package.json create mode 100644 e2e/playwright.config.ts create mode 100644 e2e/tests/example.spec.ts diff --git a/.env b/.env index d982682..6c92d11 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -CONSOLE_IMAGE=quay.io/securesign/rhtas-console@sha256:75966d60ed709af33efd48c53b96ea7b2fcd4608f90ccc56885bf224e34b55f5 -CONSOLE_UI_IMAGE=quay.io/securesign/rhtas-console-ui@sha256:c0b0b2d76548c05efadb2425baf93609cf6c40180f170cb531fbb7689a91db31 -CONSOLE_DB_IMAGE=registry.redhat.io/rhel9/mariadb-105@sha256:050dd5a7a32395b73b8680570e967e55050b152727412fdd73a25d8816e62d53 +CONSOLE_IMAGE=ghcr.io/securesign/rhtas-console:latest +CONSOLE_UI_IMAGE=ghcr.io/securesign/rhtas-console-ui:latest +CONSOLE_DB_IMAGE=docker.io/library/mariadb:10.5 diff --git a/.github/actions/start-console/action.yml b/.github/actions/start-console/action.yml new file mode 100644 index 0000000..3f8b242 --- /dev/null +++ b/.github/actions/start-console/action.yml @@ -0,0 +1,81 @@ +name: Start console +description: Start console using docker compose. +inputs: + ui_image: + description: image uri for the ui (ie. ghcr.io//:) + type: string + required: false + default: "" + server_image: + description: image uri for the server (ie. ghcr.io//:) + type: string + required: false + default: "" + server_db_image: + description: image uri for server-postgres (ie. ghcr.io//:) + type: string + required: false + default: "" + playwright_version: + description: version of the playwright image to run + type: string + required: false + default: "" +outputs: + server_port: + description: Port where the server is running + value: ${{ steps.set-output.outputs.server_port }} + ui_port: + description: Port where the UI is running + value: ${{ steps.set-output.outputs.ui_port }} + playwright_port: + description: Port where the UI is running + value: ${{ steps.set-output.outputs.playwright_port }} +runs: + using: "composite" + steps: + - name: Start console + working-directory: ${{ github.action_path }}/../../.. + shell: bash + run: | + opts="" + + if [ -n "${{ inputs.server_image }}" ]; then + opts="${opts} CONSOLE_IMAGE=${{ inputs.server_image }}" + fi + if [ -n "${{ inputs.ui_image }}" ]; then + opts="${opts} CONSOLE_UI_IMAGE=${{ inputs.ui_image }}" + fi + if [ -n "${{ inputs.server_db_image }}" ]; then + opts="${opts} POSTGRESQL_IMAGE=${{ inputs.server_db_image }}" + fi + + if [ -n "${{ inputs.playwright_version }}" ]; then + opts="${opts} PLAYWRIGHT_VERSION=${{ inputs.playwright_version }}" + fi + + echo "opts: $opts" + + eval "${opts} docker compose up -d" + + - name: Wait for services to be ready + shell: bash + run: | + # Wait for backend + until curl -s http://localhost:8087/healthz | jq -e '.status == "ok"' >/dev/null 2>&1; do + echo "Waiting for healthy service response on port 8087..." + sleep 2 + done + + # Wait for ui + until curl -s http://localhost:8088 | grep -qi "> $GITHUB_OUTPUT + echo "ui_port=8088" >> $GITHUB_OUTPUT + echo "playwright_port=5000" >> $GITHUB_OUTPUT diff --git a/.github/workflows/ci-e2e-template.yaml b/.github/workflows/ci-e2e-template.yaml new file mode 100644 index 0000000..05cbf73 --- /dev/null +++ b/.github/workflows/ci-e2e-template.yaml @@ -0,0 +1,140 @@ +name: Run e2e RHTAS Console CI tests + +on: + workflow_call: + inputs: + artifact: + description: | + The name of the component being tested, ie server etc. + Must correspond to an artifact storing the custom built image, named , + and should contain the file .tar inside. + required: false + type: string + ui_image: + description: image uri for the ui (ie. ghcr.io//:) + type: string + required: false + default: "" + server_image: + description: image uri for the server (ie. ghcr.io//:) + type: string + required: false + default: "" + server_db_image: + description: image uri for server-postgres (ie. ghcr.io//:) + type: string + required: false + default: "" + workflow_dispatch: + inputs: + artifact: + description: | + The name of the component being tested, ie server etc. + Must correspond to an artifact storing the custom built image, named , + and should contain the file .tar inside. + required: false + type: string + ui_image: + description: image uri for the ui (ie. ghcr.io//:) + type: string + required: false + default: "" + server_image: + description: image uri for the server (ie. ghcr.io//:) + type: string + required: false + default: "" + server_db_image: + description: image uri for server-postgres (ie. ghcr.io//:) + type: string + required: false + default: "" + +jobs: + check-images: + runs-on: ubuntu-latest + steps: + - name: Download artifact + if: "${{ inputs.artifact != '' }}" + uses: actions/download-artifact@v5 + with: + name: ${{ inputs.artifact }} + path: /tmp + - name: Load images + if: ${{ inputs.artifact != '' }} + run: | + docker load --input /tmp/${{ inputs.artifact }}.tar + - name: Check ui image exists + if: ${{ inputs.ui_image != '' }} + run: | + if docker image inspect ${{ inputs.ui_image }} >/dev/null 2>&1; then + echo "Image exists locally" + docker image inspect ${{ inputs.ui_image }} + else + echo "Image does not exist locally" + docker manifest inspect ${{ inputs.ui_image }} + fi + - name: Check server image exists + if: ${{ inputs.server_image != '' }} + run: | + if docker image inspect ${{ inputs.server_image }} >/dev/null 2>&1; then + echo "Image exists locally" + docker image inspect ${{ inputs.server_image }} + else + echo "Image does not exist locally" + docker manifest inspect ${{ inputs.server_image }} + fi + - name: Check server_db_image image exists + if: ${{ inputs.server_db_image != '' }} + run: | + if docker image inspect ${{ inputs.server_db_image }} >/dev/null 2>&1; then + echo "Image exists locally" + docker image inspect ${{ inputs.server_db_image }} + else + echo "Image does not exist locally" + docker manifest inspect ${{ inputs.server_db_image }} + fi + + e2e-integration-tests: + needs: check-images + runs-on: ubuntu-latest + steps: + - name: Download artifact + if: "${{ inputs.artifact != '' }}" + uses: actions/download-artifact@v5 + with: + name: ${{ inputs.artifact }} + path: /tmp + - name: Load images + if: ${{ inputs.artifact != '' }} + run: | + docker load --input /tmp/${{ inputs.artifact }}.tar + + - name: Checkout ui repo + uses: actions/checkout@v5 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: "npm" + - name: Install dependencies + run: npm ci --verbose --ignore-scripts --no-audit + + - name: Start rhtas-console + uses: ./.github/actions/start-console + with: + ui_image: ${{ inputs.ui_image }} + server_image: ${{ inputs.server_image }} + server_db_image: ${{ inputs.server_db_image }} + + - name: Run Playwright tests + run: | + PW_TEST_CONNECT_WS_ENDPOINT=ws://localhost:5000/ CONSOLE_UI_URL=http://localhost:8088 AUTH_REQUIRED=false npm run -w e2e test + + - name: Upload Playwright artifacts + if: failure() # only upload if tests failed + uses: actions/upload-artifact@v4 + with: + name: playwright-artifacts + path: | + e2e/test-results + e2e/playwright-report diff --git a/.github/workflows/ci-e2e.yaml b/.github/workflows/ci-e2e.yaml new file mode 100644 index 0000000..1b1f52e --- /dev/null +++ b/.github/workflows/ci-e2e.yaml @@ -0,0 +1,79 @@ +name: CI (e2e) + +on: + push: + branches: + - "main" + - "release/*" + pull_request: + branches: + - "main" + - "release/*" + workflow_call: + merge_group: + +concurrency: + group: ci-e2e-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-upload-for-e2e-ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: save rhtas-console-ui image + run: | + docker build . -t ghcr.io/securesign/rhtas-console-ui:pr-test -f Dockerfile + docker save -o /tmp/rhtas-console-ui.tar ghcr.io/securesign/rhtas-console-ui:pr-test + + - name: Upload console-ui image as artifact + uses: actions/upload-artifact@v4 + with: + name: rhtas-console-ui + path: /tmp/rhtas-console-ui.tar + retention-days: 1 + + discover-envs-for-e2e-ci: + runs-on: ubuntu-latest + outputs: + image_tag: ${{ steps.set-outputs.outputs.image_tag }} + steps: + - name: Extract vars for Pull Request + shell: bash + if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} + env: + base_ref: ${{ github.event.pull_request.base.ref || github.event.merge_group.base_ref }} + run: | + branch=$base_ref + branch=$(echo ${branch#refs/heads/}) + image_tag="latest" + if [[ "$branch" != "main" ]]; then + image_tag="${branch#release/}" + fi + echo "image_tag=$image_tag" >> $GITHUB_ENV + - name: Extract vars for Push + shell: bash + if: ${{ github.event_name != 'pull_request' && github.event_name != 'merge_group' }} + run: | + branch=$(echo ${GITHUB_REF#refs/heads/}) + image_tag="latest" + if [[ "$branch" != "main" ]]; then + image_tag="${branch#release/}" + fi + echo "image_tag=$image_tag" >> $GITHUB_ENV + - name: Set outputs + id: set-outputs + run: | + echo ${{ env.image_tag }} + echo "image_tag=${{ env.image_tag }}" >> "$GITHUB_OUTPUT" + + run-e2e-ci: + needs: + - build-and-upload-for-e2e-ci + - discover-envs-for-e2e-ci + uses: ./.github/workflows/ci-e2e-template.yaml + with: + artifact: rhtas-console-ui + ui_image: ghcr.io/securesign/rhtas-console-ui:pr-test + server_image: ghcr.io/securesign/rhtas-console:${{ needs.discover-envs-for-e2e-ci.outputs.image_tag }} diff --git a/README.md b/README.md index 0824b00..6fe738d 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ npm run start | --------------- | ----------------------------- | -------------------------------------- | | MOCK | Enables or disables mock data | `off` | | AUTH_REQUIRED | Enable/Disable authentication | false | +| CONSOLE_API_URL | Enable/Disable authentication | http://localhost:8080 | | OIDC_CLIENT_ID | Set Oidc Client | frontend | | OIDC_SERVER_URL | Set Oidc Server URL | `http://localhost:8090/realms/console` | | OIDC_SCOPE | Set Oidc Scope | openid | @@ -90,7 +91,7 @@ podman run -it $BASE_IMAGE cat /etc/yum.repos.d/ubi.repo > ubi.repo Make sure the `ubi.repo` file has all repositories enabled `enabled = 1` and then: -Also make sure the `ubi.repo` contains only repositories from https://github.com/release-engineering/rhtap-ec-policy/blob/main/data/known_rpm_repositories.yml . Change the repository names manually if needed. E.g. +Also make sure the `ubi.repo` contains only repositories from https://github.com/release-engineering/rhtap-ec-policy/blob/main/data/known_rpm_repositories.yml . Change the repository names manually if needed. E.g. - `ubi-9-for-baseos-rpms` change it to `ubi-9-for-x86_64-baseos-rpms` as only the latter is an accepted repository in Konflux. @@ -128,13 +129,14 @@ The `overlays/dev/` directory contains a `kustomization.yaml` for environment-sp 1. **Set TUF_REPO_URL using a ConfigMap**: Before deploying, you need to retrieve the TUF repository URL from your running RHTAS instance. This value should be stored in a ConfigMap that the console backend can consume. - - * Retrieve the TUF route URL from your running RHTAS instance: + - Retrieve the TUF route URL from your running RHTAS instance: + ```bash oc get tuf -o jsonpath='{.items[0].status.url}' ``` - - * Create a ConfigMap with the retrieved URL: + + - Create a ConfigMap with the retrieved URL: + ```bash oc create configmap tuf-repo-config \ --from-literal=TUF_REPO_URL= \ @@ -151,7 +153,7 @@ The `overlays/dev/` directory contains a `kustomization.yaml` for environment-sp oc apply -k https://github.com/securesign/rhtas-console-ui/deployment/overlays/dev?ref=v0.1.0 ``` -4. **Verify the Deployment**: +3. **Verify the Deployment**: Check the status of the deployed resources: @@ -160,11 +162,12 @@ The `overlays/dev/` directory contains a `kustomization.yaml` for environment-sp ``` You can access the console via a browser using the UI route: + ```bash oc get route console-ui -o jsonpath='https://{.spec.host}{"\n"}' ``` -5. **Deletion**: +4. **Deletion**: To delete the deployed resources: diff --git a/docker-compose.yaml b/docker-compose.yaml index e2b41cd..9f06e2e 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -43,3 +43,15 @@ services: depends_on: console: condition: service_started + + playwright: + image: mcr.microsoft.com/playwright:v1.56.1-jammy + ports: + - "5000:5000" + network_mode: host + working_dir: /home/pwuser + command: + - /bin/sh + - -c + - npx -y playwright run-server --port 5000 + diff --git a/e2e/.gitignore b/e2e/.gitignore new file mode 100644 index 0000000..335bd46 --- /dev/null +++ b/e2e/.gitignore @@ -0,0 +1,8 @@ + +# Playwright +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ diff --git a/e2e/package.json b/e2e/package.json new file mode 100644 index 0000000..92cf065 --- /dev/null +++ b/e2e/package.json @@ -0,0 +1,23 @@ +{ + "name": "@console-ui/e2e", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "clean": "rimraf ./test-results ./playwright-report", + "clean:all": "rimraf ./dist ./node_modules", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --check './src/**/*.{ts,tsx,js,json}'", + "format:fix": "prettier --write './src/**/*.{ts,tsx,js,json}'", + "test": "npx playwright test --project='chromium'", + "test:trace": "npx playwright test --project='chromium' --trace on", + "test:ui:host": "npx playwright test --ui-host 127.0.0.1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@playwright/test": "^1.56.1" + } +} diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts new file mode 100644 index 0000000..f2af38a --- /dev/null +++ b/e2e/playwright.config.ts @@ -0,0 +1,87 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +const DESKTOP_CONFIG = { + viewport: { height: 961, width: 1920 }, +}; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('')`. */ + baseURL: process.env.CONSOLE_UI_URL ?? "http://localhost:3000/", + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + + // Capture screenshot after each test failure. + screenshot: "only-on-failure", + ignoreHTTPSErrors: true, + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'], ...DESKTOP_CONFIG }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'], ...DESKTOP_CONFIG }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'], ...DESKTOP_CONFIG }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://localhost:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/e2e/tests/example.spec.ts b/e2e/tests/example.spec.ts new file mode 100644 index 0000000..54a906a --- /dev/null +++ b/e2e/tests/example.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/Playwright/); +}); + +test('get started link', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Click the get started link. + await page.getByRole('link', { name: 'Get started' }).click(); + + // Expects page to have a heading with the name of Installation. + await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); +}); diff --git a/package-lock.json b/package-lock.json index d7b4b98..98bc8d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "workspaces": [ "common", "client", - "server" + "server", + "e2e" ], "dependencies": { "@types/express": "^5.0.3", @@ -104,6 +105,14 @@ "version": "0.1.0", "license": "Apache-2.0" }, + "e2e": { + "name": "@console-ui/e2e", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.56.1" + } + }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", @@ -518,6 +527,10 @@ "resolved": "common", "link": true }, + "node_modules/@console-ui/e2e": { + "resolved": "e2e", + "link": true + }, "node_modules/@console-ui/server": { "resolved": "server", "link": true @@ -1984,6 +1997,22 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@playwright/test": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", + "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.56.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", @@ -8753,15 +8782,13 @@ "license": "MIT" }, "node_modules/playwright": { - "version": "1.55.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz", - "integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", + "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", "dev": true, "license": "Apache-2.0", - "optional": true, - "peer": true, "dependencies": { - "playwright-core": "1.55.1" + "playwright-core": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -8774,13 +8801,11 @@ } }, "node_modules/playwright-core": { - "version": "1.55.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz", - "integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", + "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", "dev": true, "license": "Apache-2.0", - "optional": true, - "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -8799,7 +8824,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } diff --git a/package.json b/package.json index 24ef43d..451bb75 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,16 @@ "start:dev:client": "npm run start:dev -w client", "start:dev": "npm run build -w common && concurrently -n common,client -c 'white.bold.inverse,green.bold.inverse,blue.bold.inverse' 'npm:start:dev:common' 'npm:start:dev:client'", "start": "npm run build -w common -w client && npm run start -w server", - "test": "npm run build -w common && npm run test -w common -w client -w server --if-present --" + "test": "npm run build -w common && npm run test -w common -w client -w server --if-present --", + "e2e:test": "npm run test -w e2e --if-present --", + "e2e:test:trace": "npm run test:trace -w e2e --if-present --", + "e2e:test:host": "npm run test:ui:host -w e2e --if-present --" }, "workspaces": [ "common", "client", - "server" + "server", + "e2e" ], "dependencies": { "@types/express": "^5.0.3", From 4f520d13859e1ae6831cbf96e78a8c2bffd93bb2 Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:49:25 -0500 Subject: [PATCH 2/9] fix: eslint complains Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- README.md | 2 +- e2e/package.json | 4 ++-- e2e/playwright.config.ts | 20 ++++++++++---------- e2e/tests/example.spec.ts | 14 +++++++------- e2e/tsconfig.json | 13 +++++++++++++ eslint.config.mjs | 2 +- 6 files changed, 34 insertions(+), 21 deletions(-) create mode 100644 e2e/tsconfig.json diff --git a/README.md b/README.md index 6fe738d..c601321 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ npm run start | --------------- | ----------------------------- | -------------------------------------- | | MOCK | Enables or disables mock data | `off` | | AUTH_REQUIRED | Enable/Disable authentication | false | -| CONSOLE_API_URL | Enable/Disable authentication | http://localhost:8080 | +| CONSOLE_API_URL | Set Console API URL | http://localhost:8080 | | OIDC_CLIENT_ID | Set Oidc Client | frontend | | OIDC_SERVER_URL | Set Oidc Server URL | `http://localhost:8090/realms/console` | | OIDC_SCOPE | Set Oidc Scope | openid | diff --git a/e2e/package.json b/e2e/package.json index 92cf065..e3c0322 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -7,8 +7,8 @@ "clean:all": "rimraf ./dist ./node_modules", "lint": "eslint .", "lint:fix": "eslint . --fix", - "format": "prettier --check './src/**/*.{ts,tsx,js,json}'", - "format:fix": "prettier --write './src/**/*.{ts,tsx,js,json}'", + "format": "prettier --check './tests/**/*.{ts,tsx,js,json}'", + "format:fix": "prettier --write './tests/**/*.{ts,tsx,js,json}'", "test": "npx playwright test --project='chromium'", "test:trace": "npx playwright test --project='chromium' --trace on", "test:ui:host": "npx playwright test --ui-host 127.0.0.1" diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index f2af38a..63188ad 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, devices } from '@playwright/test'; +import { defineConfig, devices } from "@playwright/test"; /** * Read environment variables from file. @@ -16,7 +16,7 @@ const DESKTOP_CONFIG = { * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: './tests', + testDir: "./tests", /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -26,14 +26,14 @@ export default defineConfig({ /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: "html", /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('')`. */ baseURL: process.env.CONSOLE_UI_URL ?? "http://localhost:3000/", /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + trace: "on-first-retry", // Capture screenshot after each test failure. screenshot: "only-on-failure", @@ -43,18 +43,18 @@ export default defineConfig({ /* Configure projects for major browsers */ projects: [ { - name: 'chromium', - use: { ...devices['Desktop Chrome'], ...DESKTOP_CONFIG }, + name: "chromium", + use: { ...devices["Desktop Chrome"], ...DESKTOP_CONFIG }, }, { - name: 'firefox', - use: { ...devices['Desktop Firefox'], ...DESKTOP_CONFIG }, + name: "firefox", + use: { ...devices["Desktop Firefox"], ...DESKTOP_CONFIG }, }, { - name: 'webkit', - use: { ...devices['Desktop Safari'], ...DESKTOP_CONFIG }, + name: "webkit", + use: { ...devices["Desktop Safari"], ...DESKTOP_CONFIG }, }, /* Test against mobile viewports. */ diff --git a/e2e/tests/example.spec.ts b/e2e/tests/example.spec.ts index 54a906a..839cef5 100644 --- a/e2e/tests/example.spec.ts +++ b/e2e/tests/example.spec.ts @@ -1,18 +1,18 @@ -import { test, expect } from '@playwright/test'; +import { test, expect } from "@playwright/test"; -test('has title', async ({ page }) => { - await page.goto('https://playwright.dev/'); +test("has title", async ({ page }) => { + await page.goto("https://playwright.dev/"); // Expect a title "to contain" a substring. await expect(page).toHaveTitle(/Playwright/); }); -test('get started link', async ({ page }) => { - await page.goto('https://playwright.dev/'); +test("get started link", async ({ page }) => { + await page.goto("https://playwright.dev/"); // Click the get started link. - await page.getByRole('link', { name: 'Get started' }).click(); + await page.getByRole("link", { name: "Get started" }).click(); // Expects page to have a heading with the name of Installation. - await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); + await expect(page.getByRole("heading", { name: "Installation" })).toBeVisible(); }); diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 0000000..72539eb --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022", "DOM"], + "strict": true, + "esModuleInterop": true, + "moduleResolution": "bundler", + "types": ["playwright", "node"] + }, + "include": ["playwright.config.ts", "tests/**/*"], + "exclude": ["node_modules"] +} diff --git a/eslint.config.mjs b/eslint.config.mjs index e323cd5..02123c6 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -25,7 +25,7 @@ export default tseslint.config([ ecmaVersion: 2020, globals: globals.browser, parserOptions: { - project: ["./common/tsconfig.json", "./client/tsconfig.node.json", "./client/tsconfig.app.json"], + project: ["./common/tsconfig.json", "./client/tsconfig.node.json", "./client/tsconfig.app.json", "./e2e/tsconfig.json"], tsconfigRootDir: import.meta.dirname, }, }, From db7695810c1d32137b020a1cf62c0580418c604f Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:05:45 -0500 Subject: [PATCH 3/9] fix: login to github registry Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- .github/workflows/ci-e2e-template.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci-e2e-template.yaml b/.github/workflows/ci-e2e-template.yaml index 05cbf73..6d66f3e 100644 --- a/.github/workflows/ci-e2e-template.yaml +++ b/.github/workflows/ci-e2e-template.yaml @@ -54,6 +54,13 @@ jobs: check-images: runs-on: ubuntu-latest steps: + - name: Log in to registry + uses: docker/login-action@v3 + with: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + registry: ghcr.io + - name: Download artifact if: "${{ inputs.artifact != '' }}" uses: actions/download-artifact@v5 From fc92089a4f285f6c94e5b03e7da2bc97ff91f96e Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:34:37 -0500 Subject: [PATCH 4/9] save changes Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- e2e/tests/Navigation.ts | 33 +++++++++++++++++++++++++++++++++ e2e/tests/example.spec.ts | 8 ++++++++ 2 files changed, 41 insertions(+) create mode 100644 e2e/tests/Navigation.ts diff --git a/e2e/tests/Navigation.ts b/e2e/tests/Navigation.ts new file mode 100644 index 0000000..38374b7 --- /dev/null +++ b/e2e/tests/Navigation.ts @@ -0,0 +1,33 @@ +import type { Page } from "playwright-core"; + +/** + * Used to navigate to different pages + */ +export class Navigation { + private readonly _page: Page; + + private constructor(page: Page) { + this._page = page; + } + + static async build(page: Page) { + return new Navigation(page); + } + + async goToSidebar( + menu: + | "Dashboard" + | "Search" + | "SBOMs" + | "Vulnerabilities" + | "Packages" + | "Advisories" + | "Importers" + | "Upload", + ) { + // By default, we do not initialize navigation at "/"" where the Dashboard is located + // This should help us to save some time loading pages as the Dashboard fetches too much data + await this._page.goto("/importers"); + await this._page.getByRole("link", { name: menu }).click(); + } +} diff --git a/e2e/tests/example.spec.ts b/e2e/tests/example.spec.ts index 839cef5..d38af4d 100644 --- a/e2e/tests/example.spec.ts +++ b/e2e/tests/example.spec.ts @@ -1,5 +1,13 @@ import { test, expect } from "@playwright/test"; +test.describe("Rekor Search UI", () => { + test("should properly render home page", async ({ page }) => { + await page.goto("/"); + // cy.get("body").should("contain", "Attribute"); + // cy.get("body").should("contain", "Email"); + }); +}); + test("has title", async ({ page }) => { await page.goto("https://playwright.dev/"); From b6c7ed4b7721c20620b51b0a456df717f91041f1 Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:07:07 -0500 Subject: [PATCH 5/9] feat: build backend container Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- .github/workflows/ci-e2e-template.yaml | 34 +++++++++---- .github/workflows/ci-e2e.yaml | 69 ++++++++++++++++++-------- 2 files changed, 73 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci-e2e-template.yaml b/.github/workflows/ci-e2e-template.yaml index 6d66f3e..2ab8d7c 100644 --- a/.github/workflows/ci-e2e-template.yaml +++ b/.github/workflows/ci-e2e-template.yaml @@ -5,9 +5,8 @@ on: inputs: artifact: description: | - The name of the component being tested, ie server etc. - Must correspond to an artifact storing the custom built image, named , - and should contain the file .tar inside. + The name of the artifact storing custom images to be used during the CI run. All + images stored in the artifact will be automatically loaded. required: false type: string ui_image: @@ -29,9 +28,8 @@ on: inputs: artifact: description: | - The name of the component being tested, ie server etc. - Must correspond to an artifact storing the custom built image, named , - and should contain the file .tar inside. + The name of the artifact storing custom images to be used during the CI run. All + images stored in the artifact will be automatically loaded. required: false type: string ui_image: @@ -63,14 +61,22 @@ jobs: - name: Download artifact if: "${{ inputs.artifact != '' }}" + id: download-artifact uses: actions/download-artifact@v5 with: name: ${{ inputs.artifact }} - path: /tmp + path: /container_images - name: Load images if: ${{ inputs.artifact != '' }} + env: + IMAGES_PATH: ${{ steps.download-artifact.outputs.download-path }} run: | - docker load --input /tmp/${{ inputs.artifact }}.tar + for image in $( + find "${IMAGES_PATH}" -type f -name "*.tar" + ); do + echo "Loading image \`${image}\`" >>"$GITHUB_STEP_SUMMARY" + docker load --input ${image} + done - name: Check ui image exists if: ${{ inputs.ui_image != '' }} run: | @@ -108,14 +114,22 @@ jobs: steps: - name: Download artifact if: "${{ inputs.artifact != '' }}" + id: download-artifact uses: actions/download-artifact@v5 with: name: ${{ inputs.artifact }} - path: /tmp + path: /container_images - name: Load images if: ${{ inputs.artifact != '' }} + env: + IMAGES_PATH: ${{ steps.download-artifact.outputs.download-path }} run: | - docker load --input /tmp/${{ inputs.artifact }}.tar + for image in $( + find "${IMAGES_PATH}" -type f -name "*.tar" + ); do + echo "Loading image \`${image}\`" >>"$GITHUB_STEP_SUMMARY" + docker load --input ${image} + done - name: Checkout ui repo uses: actions/checkout@v5 diff --git a/.github/workflows/ci-e2e.yaml b/.github/workflows/ci-e2e.yaml index 1b1f52e..06ab415 100644 --- a/.github/workflows/ci-e2e.yaml +++ b/.github/workflows/ci-e2e.yaml @@ -17,23 +17,6 @@ concurrency: cancel-in-progress: true jobs: - build-and-upload-for-e2e-ci: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - - name: save rhtas-console-ui image - run: | - docker build . -t ghcr.io/securesign/rhtas-console-ui:pr-test -f Dockerfile - docker save -o /tmp/rhtas-console-ui.tar ghcr.io/securesign/rhtas-console-ui:pr-test - - - name: Upload console-ui image as artifact - uses: actions/upload-artifact@v4 - with: - name: rhtas-console-ui - path: /tmp/rhtas-console-ui.tar - retention-days: 1 - discover-envs-for-e2e-ci: runs-on: ubuntu-latest outputs: @@ -66,14 +49,60 @@ jobs: id: set-outputs run: | echo ${{ env.image_tag }} - echo "image_tag=${{ env.image_tag }}" >> "$GITHUB_OUTPUT" + + build-backend-and-upload-for-e2e-ci: + runs-on: ubuntu-latest + needs: + - discover-envs-for-e2e-ci + steps: + - uses: actions/checkout@v5 + with: + repository: securesign/rhtas-console + ref: ${{ needs.discover-envs-for-e2e-ci.outputs.image_tag }} + - name: save rhtas-console image + run: | + docker build . -t ghcr.io/securesign/rhtas-console:pr-test -f Dockerfile + docker save -o /tmp/images/rhtas-console.tar ghcr.io/securesign/rhtas-console:pr-test + - name: Upload console image as artifact + uses: actions/upload-artifact@v4 + with: + name: backend-images + path: /tmp/images/**/*.tar + + build-ui-and-upload-for-e2e-ci: + runs-on: ubuntu-latest + needs: + - discover-envs-for-e2e-ci + steps: + - uses: actions/checkout@v5 + - name: save rhtas-console-ui image + run: | + docker build . -t ghcr.io/securesign/rhtas-console-ui:pr-test -f Dockerfile + docker save -o /tmp/images/rhtas-console-ui.tar ghcr.io/securesign/rhtas-console-ui:pr-test + - name: Upload console-ui image as artifact + uses: actions/upload-artifact@v4 + with: + name: ui-images + path: /tmp/images/**/*.tar + + build-and-upload-for-e2e-ci: + runs-on: ubuntu-latest + needs: + - build-backend-and-upload-for-e2e-ci + - build-ui-and-upload-for-e2e-ci + steps: + - uses: actions/upload-artifact/merge@v4 + with: + name: test-images + pattern: *-images + delete-merged: true + retention-days: 1 run-e2e-ci: needs: - build-and-upload-for-e2e-ci - - discover-envs-for-e2e-ci uses: ./.github/workflows/ci-e2e-template.yaml with: - artifact: rhtas-console-ui + artifact: test-images ui_image: ghcr.io/securesign/rhtas-console-ui:pr-test server_image: ghcr.io/securesign/rhtas-console:${{ needs.discover-envs-for-e2e-ci.outputs.image_tag }} From b1e2d851efb64b0a824257c53eb39d8945465601 Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:10:29 -0500 Subject: [PATCH 6/9] fix: dependency Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- .github/workflows/ci-e2e.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-e2e.yaml b/.github/workflows/ci-e2e.yaml index 06ab415..0e760ca 100644 --- a/.github/workflows/ci-e2e.yaml +++ b/.github/workflows/ci-e2e.yaml @@ -104,5 +104,5 @@ jobs: uses: ./.github/workflows/ci-e2e-template.yaml with: artifact: test-images - ui_image: ghcr.io/securesign/rhtas-console-ui:pr-test - server_image: ghcr.io/securesign/rhtas-console:${{ needs.discover-envs-for-e2e-ci.outputs.image_tag }} + server_image: ghcr.io/securesign/rhtas-console:pr-test + ui_image: ghcr.io/securesign/rhtas-console-ui:pr-test From 44b4ad8a816898d479ebd9598d74223c89337a3c Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:12:02 -0500 Subject: [PATCH 7/9] fix: pattern param Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- .github/workflows/ci-e2e.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-e2e.yaml b/.github/workflows/ci-e2e.yaml index 0e760ca..9f4fdcc 100644 --- a/.github/workflows/ci-e2e.yaml +++ b/.github/workflows/ci-e2e.yaml @@ -94,7 +94,7 @@ jobs: - uses: actions/upload-artifact/merge@v4 with: name: test-images - pattern: *-images + pattern: "*-images" delete-merged: true retention-days: 1 @@ -105,4 +105,4 @@ jobs: with: artifact: test-images server_image: ghcr.io/securesign/rhtas-console:pr-test - ui_image: ghcr.io/securesign/rhtas-console-ui:pr-test + ui_image: ghcr.io/securesign/rhtas-console-ui:pr-test From 22c425d2b17b69db2241cd271134272d12d23689 Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:17:43 -0500 Subject: [PATCH 8/9] fix: container tmp directory Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- .github/workflows/ci-e2e.yaml | 2 ++ e2e/tests/Navigation.ts | 12 ++---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-e2e.yaml b/.github/workflows/ci-e2e.yaml index 9f4fdcc..35e2896 100644 --- a/.github/workflows/ci-e2e.yaml +++ b/.github/workflows/ci-e2e.yaml @@ -61,6 +61,7 @@ jobs: ref: ${{ needs.discover-envs-for-e2e-ci.outputs.image_tag }} - name: save rhtas-console image run: | + mkdir -p /tmp/images/ docker build . -t ghcr.io/securesign/rhtas-console:pr-test -f Dockerfile docker save -o /tmp/images/rhtas-console.tar ghcr.io/securesign/rhtas-console:pr-test - name: Upload console image as artifact @@ -77,6 +78,7 @@ jobs: - uses: actions/checkout@v5 - name: save rhtas-console-ui image run: | + mkdir -p /tmp/images/ docker build . -t ghcr.io/securesign/rhtas-console-ui:pr-test -f Dockerfile docker save -o /tmp/images/rhtas-console-ui.tar ghcr.io/securesign/rhtas-console-ui:pr-test - name: Upload console-ui image as artifact diff --git a/e2e/tests/Navigation.ts b/e2e/tests/Navigation.ts index 38374b7..078bbf6 100644 --- a/e2e/tests/Navigation.ts +++ b/e2e/tests/Navigation.ts @@ -10,20 +10,12 @@ export class Navigation { this._page = page; } - static async build(page: Page) { + static build(page: Page) { return new Navigation(page); } async goToSidebar( - menu: - | "Dashboard" - | "Search" - | "SBOMs" - | "Vulnerabilities" - | "Packages" - | "Advisories" - | "Importers" - | "Upload", + menu: "Dashboard" | "Search" | "SBOMs" | "Vulnerabilities" | "Packages" | "Advisories" | "Importers" | "Upload" ) { // By default, we do not initialize navigation at "/"" where the Dashboard is located // This should help us to save some time loading pages as the Dashboard fetches too much data From 1eea91b2cf29c7ae5e27733fdb6d51704738e840 Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:22:57 -0500 Subject: [PATCH 9/9] fix: download image path Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- .github/workflows/ci-e2e-template.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-e2e-template.yaml b/.github/workflows/ci-e2e-template.yaml index 2ab8d7c..6064fba 100644 --- a/.github/workflows/ci-e2e-template.yaml +++ b/.github/workflows/ci-e2e-template.yaml @@ -65,7 +65,7 @@ jobs: uses: actions/download-artifact@v5 with: name: ${{ inputs.artifact }} - path: /container_images + path: /tmp/container_images - name: Load images if: ${{ inputs.artifact != '' }} env: @@ -118,7 +118,7 @@ jobs: uses: actions/download-artifact@v5 with: name: ${{ inputs.artifact }} - path: /container_images + path: /tmp/container_images - name: Load images if: ${{ inputs.artifact != '' }} env: