From 5d579090881a0a6265aae8f7e791cc8c07c69eb1 Mon Sep 17 00:00:00 2001 From: Elisa Date: Tue, 11 Aug 2026 21:34:02 +0200 Subject: [PATCH 1/2] ci: run UI e2e tests in CI --- .github/workflows/test-e2e.yaml | 36 +++++++++++++++ Makefile | 8 ++-- docs/developer-guide/frontend-development.md | 48 +++++++++++--------- hack/dev-cluster.sh | 36 +++++++++++++-- web/e2e/pipeline-journey.spec.ts | 16 +++++-- web/package.json | 1 + web/playwright.config.ts | 4 +- web/pnpm-lock.yaml | 45 ++++++++++++------ web/tsconfig.json | 2 +- 9 files changed, 148 insertions(+), 48 deletions(-) diff --git a/.github/workflows/test-e2e.yaml b/.github/workflows/test-e2e.yaml index bdf54813..df9ac850 100644 --- a/.github/workflows/test-e2e.yaml +++ b/.github/workflows/test-e2e.yaml @@ -48,3 +48,39 @@ jobs: GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | make test-e2e E2E_IMAGE_SOURCE=$E2E_IMAGE_SOURCE TAG=$IMAGE_TAG REGISTRY=$REGISTRY + + test-ui-e2e: + runs-on: ubuntu-24.04 + defaults: + run: + shell: nix develop --command bash -e {0} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + persist-credentials: false + - name: Install nix + uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # v22 + with: + determinate: false + diagnostic-endpoint: '' + - name: Use opendefensecloud Cachix cache + uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + with: + name: opendefensecloud + authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} + signingKey: ${{ secrets.CACHIX_SIGNING_KEY }} + - name: Run UI E2E Test Suite + env: + E2E_IMAGE_SOURCE: ${{ inputs.is-local && 'local' || 'ghcr' }} + REGISTRY: ${{ inputs.is-local && 'localhost:5001/local' || format('ghcr.io/{0}', github.repository_owner) }} + IMAGE_TAG: ${{ inputs.image-tag }} + GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + make ui-test-e2e E2E_IMAGE_SOURCE=$E2E_IMAGE_SOURCE TAG=$IMAGE_TAG REGISTRY=$REGISTRY + - name: Upload Playwright report + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: playwright-report + path: web/playwright-report/ + retention-days: 7 diff --git a/Makefile b/Makefile index b9b23304..2fe764ae 100644 --- a/Makefile +++ b/Makefile @@ -308,9 +308,11 @@ ui-dev: ui-install ## Start Go backend + Vite dev server against the UI dev clus ui-e2e-cluster: ocm-transfer-demo ## Create a Kind cluster with Dex + SolAr for UI e2e testing WORK_DIR=$(UI_E2E_WORK_DIR) $(HACK_DIR)/generate-dex-certs.sh KIND_CONFIG=$(UI_E2E_WORK_DIR)/kind-config-oidc.yaml $(MAKE) setup-local-cluster KIND_CLUSTER=$(KIND_CLUSTER_UI_E2E) - $(MAKE) docker-build-local-images TAG=e2e - $(MAKE) kind-load-local-images TAG=e2e KIND_CLUSTER=$(KIND_CLUSTER_UI_E2E) - TAG=e2e KIND_CLUSTER=$(KIND_CLUSTER_UI_E2E) $(HACK_DIR)/dev-cluster.sh + @if [ "$(E2E_IMAGE_SOURCE)" = "local" ]; then \ + $(MAKE) docker-build-local-images TAG=$(TAG) REGISTRY=$(REGISTRY); \ + $(MAKE) kind-load-local-images TAG=$(TAG) KIND_CLUSTER=$(KIND_CLUSTER_UI_E2E) REGISTRY=$(REGISTRY); \ + fi + REGISTRY=$(REGISTRY) TAG=$(TAG) KIND_CLUSTER=$(KIND_CLUSTER_UI_E2E) $(HACK_DIR)/dev-cluster.sh KIND_CLUSTER=$(KIND_CLUSTER_UI_E2E) $(HACK_DIR)/setup-dex.sh .PHONY: ui-cleanup-e2e-cluster diff --git a/docs/developer-guide/frontend-development.md b/docs/developer-guide/frontend-development.md index c5e56bd0..f5bd4b75 100644 --- a/docs/developer-guide/frontend-development.md +++ b/docs/developer-guide/frontend-development.md @@ -42,11 +42,11 @@ If you use the provided Nix flake (`nix develop` or `direnv`), all of these are ## The three commands -| Command | When to run | What it does | -| ------------------------ | -------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -| `make ui-dev-cluster` | Once, or after `make ui-cleanup-dev-cluster` | Creates the `solar-ui-dev` Kind cluster, builds and loads dev images, installs SolAr, sets up Dex for OIDC. | -| `make ui-seed-data` | Once after creating the cluster | Seeds demo `Target`, `Release`, `Component`, etc. resources so the UI has something to render. | -| `make ui-dev` | Every dev session | Starts Dex port-forward + Vite dev server (`:5173`) + `solar-ui` BFF (`:8090`), wired together. | +| Command | When to run | What it does | +| --------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `make ui-dev-cluster` | Once, or after `make ui-cleanup-dev-cluster` | Creates the `solar-ui-dev` Kind cluster, builds and loads dev images, installs SolAr, sets up Dex for OIDC. | +| `make ui-seed-data` | Once after creating the cluster | Seeds demo `Target`, `Release`, `Component`, etc. resources so the UI has something to render. | +| `make ui-dev` | Every dev session | Starts Dex port-forward + Vite dev server (`:5173`) + `solar-ui` BFF (`:8090`), wired together. | ### Typical first-time flow @@ -68,12 +68,12 @@ make ui-dev # auto-creates the cluster if missing, otherwise just star The UI uses OIDC against the in-cluster Dex. After opening `http://localhost:8090`, click through the Dex login. Static demo users are configured in `test/fixtures/e2e/dex/dex-config.yaml` and mirror the personas in [Roles](./roles.md): -| Dex login | OIDC email (K8s identity) | Persona | Sees | -| --------- | ------------------------- | ------- | ---- | -| `admin` | `admin@solar.local` | Solar Operator (admin) | everything, plus can use "Preview as" | -| `acm` | `app-catalog-maintainer@solar.local` | App Catalog Maintainer | Components, ComponentVersions in `app-catalog-maintainer` | -| `kcp` | `k8s-cluster-provider@solar.local` | K8s Cluster Provider | Releases, Profiles, Registries, ReleaseBindings in `k8s-cluster-provider`; Targets in `k8s-cluster-user`; read-only catalog | -| `kcu` | `k8s-cluster-user@solar.local` | K8s Cluster User | Releases, Profiles, Registries, ReleaseBindings in `k8s-cluster-user`; read/update Targets; read-only catalog | +| Dex login | OIDC email (K8s identity) | Persona | Sees | +| --------- | ------------------------------------ | ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| `admin` | `admin@solar.local` | Solar Operator (admin) | everything, plus can use "Preview as" | +| `acm` | `app-catalog-maintainer@solar.local` | App Catalog Maintainer | Components, ComponentVersions in `app-catalog-maintainer` | +| `kcp` | `k8s-cluster-provider@solar.local` | K8s Cluster Provider | Releases, Profiles, Registries, ReleaseBindings in `k8s-cluster-provider`; Targets in `k8s-cluster-user`; read-only catalog | +| `kcu` | `k8s-cluster-user@solar.local` | K8s Cluster User | Releases, Profiles, Registries, ReleaseBindings in `k8s-cluster-user`; read/update Targets; read-only catalog | All passwords are the literal string `password`. Cluster RBAC bindings live in `test/fixtures/e2e/dex/dex-rbac.yaml` (inlined from `docs/developer-guide/manifests/`). @@ -86,7 +86,7 @@ The sidebar's namespace dropdown is the global scope for every list page (Target ### How the dropdown is populated -The user's token is *not* used to enumerate cluster namespaces — most personas don't have that permission. Instead the BFF runs a **discovery proxy + per-user filter**: +The user's token is _not_ used to enumerate cluster namespaces — most personas don't have that permission. Instead the BFF runs a **discovery proxy + per-user filter**: ```text GET /api/namespaces @@ -124,11 +124,11 @@ Source: `pkg/ui/api/handler.go::HandleListNamespaces`. The selector hides the "All namespaces" option when the **current** identity can't satisfy a cluster-scope `list namespaces` `SelfSubjectAccessReview`. The check is cached on the session and invalidated whenever impersonation changes, so previewing as a persona correctly removes the option until the admin restores their real identity. -| Identity | Can pick "All"? | Why | -| --- | --- | --- | -| `admin@solar.local` | yes | bound to `cluster-admin` | -| Any persona | no | persona RoleBindings are namespace-scoped | -| Admin previewing as persona | no | impersonated identity has no cluster-scope perm | +| Identity | Can pick "All"? | Why | +| --------------------------- | --------------- | ----------------------------------------------- | +| `admin@solar.local` | yes | bound to `cluster-admin` | +| Any persona | no | persona RoleBindings are namespace-scoped | +| Admin previewing as persona | no | impersonated identity has no cluster-scope perm | If the persisted "All" choice becomes invalid (impersonation switch, RBAC change), the selector falls back to the first namespace the user can still see. @@ -136,12 +136,12 @@ If the persisted "All" choice becomes invalid (impersonation switch, RBAC change Log in as `admin@solar.local`. The sidebar shows a "Preview as" form (only for admins — gated by a cluster-scope `impersonate users` check). Type one of the persona emails: -| Preview as | Expected views | -| ----------- | -------------- | +| Preview as | Expected views | +| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `app-catalog-maintainer@solar.local` | Components/ComponentVersions visible only in the `app-catalog-maintainer` namespace. Targets/Releases/Profiles all 403 → the page shows the lock-screen explainer. Selector hides "All namespaces". | -| `k8s-cluster-provider@solar.local` | Releases, Profiles, Registries, ReleaseBindings in `k8s-cluster-provider`; Targets in `k8s-cluster-user`; catalog read-only. | -| `k8s-cluster-user@solar.local` | Targets read/update in `k8s-cluster-user`; Releases / Profiles / Registries in `k8s-cluster-user`; catalog read-only. | -| Any string K8s doesn't recognise | Form succeeds (BFF accepts whatever you type), but every subsequent list 403s — that's RBAC working as designed. | +| `k8s-cluster-provider@solar.local` | Releases, Profiles, Registries, ReleaseBindings in `k8s-cluster-provider`; Targets in `k8s-cluster-user`; catalog read-only. | +| `k8s-cluster-user@solar.local` | Targets read/update in `k8s-cluster-user`; Releases / Profiles / Registries in `k8s-cluster-user`; catalog read-only. | +| Any string K8s doesn't recognise | Form succeeds (BFF accepts whatever you type), but every subsequent list 403s — that's RBAC working as designed. | What happens under the hood on each switch: @@ -206,4 +206,8 @@ cd web && pnpm test # Vitest unit tests make ui-test-e2e # Playwright e2e (uses a separate `solar-test-e2e-ui` cluster) ``` +In CI the Playwright suite runs as the `test-ui-e2e` job in `.github/workflows/test-e2e.yaml`, in parallel with the Go e2e job and gated by the same trigger (push to `main`, a release, or a PR labelled `ok-to-e2e` / `ok-to-image`). It reuses the images `docker.yaml` pushed to GHCR instead of building them, failures are annotated in the PR diff by Playwright's `github` reporter, and the HTML report is attached to the run as the `playwright-report` artifact. + +The two e2e suites keep separate Kind clusters on purpose: `solar-test-e2e-ui` is created with an OIDC-enabled apiserver plus Dex and needs one stable SolAr install for the whole run, while the Go suite installs and uninstalls SolAr repeatedly. Sharing a cluster would serialise both suites and couple their failures. + See also: [UI Architecture ADR](./adrs/010-UI-Architecture.md). diff --git a/hack/dev-cluster.sh b/hack/dev-cluster.sh index c2537c45..cec7523b 100755 --- a/hack/dev-cluster.sh +++ b/hack/dev-cluster.sh @@ -5,6 +5,8 @@ set -euo pipefail KIND_CLUSTER="${KIND_CLUSTER:-solar-dev}" SKIP_SOLAR="${SKIP_SOLAR:-false}" TAG="${TAG:-latest}" +REGISTRY="${REGISTRY:-localhost/local}" +GHCR_TOKEN="${GHCR_TOKEN:-}" FLUX="${FLUX:-flux}" HELM="${HELM:-helm}" @@ -190,17 +192,39 @@ setup_flux() { --timeout 5m } -# setup_solar installs the Solar Helm chart into the solar-system namespace and applies the Zot deployment authorization manifest, setting component image tags to the current TAG. +# create_pull_secret creates the ghcr-pull-secret docker-registry secret in the +# given namespace. No-op without a GHCR_TOKEN +create_pull_secret() { + local namespace="$1" + [[ -n "$GHCR_TOKEN" ]] || return 0 + $KUBECTL create secret docker-registry ghcr-pull-secret \ + --namespace "$namespace" \ + --docker-server=ghcr.io \ + --docker-username=x-access-token \ + --docker-password="$GHCR_TOKEN" \ + --dry-run=client -o yaml | $KUBECTL apply -f - +} + +# setup_solar installs the Solar Helm chart into the solar-system namespace and applies the Zot deployment authorization manifest, setting component image repositories/tags to the current REGISTRY/TAG. setup_solar() { echo -e "\nSETTING UP SOLAR:\n" + local pull_secret_args=() + if [[ -n "$GHCR_TOKEN" ]]; then + create_pull_secret solar-system + pull_secret_args=(--set 'global.imagePullSecrets[0].name=ghcr-pull-secret') + fi $HELM upgrade --install \ --create-namespace \ --namespace=solar-system \ solar charts/solar \ -f test/fixtures/solar.values.yaml \ + --set apiserver.image.repository="$REGISTRY/solar-apiserver" \ + --set controller.image.repository="$REGISTRY/solar-controller-manager" \ + --set renderer.image.repository="$REGISTRY/solar-renderer" \ --set apiserver.image.tag="$TAG" \ --set controller.image.tag="$TAG" \ - --set renderer.image.tag="$TAG" + --set renderer.image.tag="$TAG" \ + "${pull_secret_args[@]}" $KUBECTL apply --namespace=solar-system \ -f test/fixtures/e2e/zot-deploy-auth.yaml $KUBECTL label namespace solar-system trust=enabled --overwrite @@ -236,12 +260,18 @@ setup_discovery() { echo -e "\nSETTING UP SOLAR-DISCOVERY:\n" $KUBECTL apply --namespace=solar-system -f test/fixtures/e2e/zot-discovery-auth.yaml $KUBECTL apply --namespace=solar-system -f test/fixtures/e2e/zot-discovery-registry-scan.yaml + local pull_secret_args=() + if [[ -n "$GHCR_TOKEN" ]]; then + pull_secret_args=(--set 'imagePullSecrets[0].name=ghcr-pull-secret') + fi $HELM upgrade --install \ --namespace=solar-system \ solar-discovery charts/solar-discovery \ -f test/fixtures/solar-discovery-scan.values.yaml \ + --set image.repository="$REGISTRY/solar-discovery" \ --set image.tag="$TAG" \ - --set namespace=solar-system + --set namespace=solar-system \ + "${pull_secret_args[@]}" $KUBECTL wait deployment \ --namespace solar-system \ -l app.kubernetes.io/instance=solar-discovery \ diff --git a/web/e2e/pipeline-journey.spec.ts b/web/e2e/pipeline-journey.spec.ts index 31fab10e..92fccb26 100644 --- a/web/e2e/pipeline-journey.spec.ts +++ b/web/e2e/pipeline-journey.spec.ts @@ -114,6 +114,15 @@ const RELEASE_BINDING = { name: "production-cluster-1-binding", namespace: NS, creationTimestamp: NOW, + ownerReferences: [ + { + apiVersion: "solar.opendefense.cloud/v1alpha1", + kind: "Profile", + name: PROFILE.metadata.name, + uid: "00000000-0000-0000-0000-0000000000p1", + controller: true, + }, + ], }, spec: { releaseRef: { name: RELEASE.metadata.name }, @@ -181,12 +190,11 @@ async function setupMocks(page: Page) { if (p === `/api/namespaces/${ns}/rendertasks`) return route.fulfill({ json: list([RENDER_TASK]) }); - // All-namespace list routes → 403: the UI must pick a namespace before - // fetching, so these should never succeed in normal operation. + if (p === "/api/targets") return route.fulfill({ json: list([TARGET]) }); + if ( p === "/api/profiles" || p === "/api/releases" || - p === "/api/targets" || p === "/api/releasebindings" || p === "/api/rendertasks" ) @@ -236,7 +244,7 @@ test.describe("Profile → Release → Target click journey", () => { ).toBeVisible(); // Target selector labels - await expect(page.getByText("env=prod")).toBeVisible(); + await expect(page.getByText(/env\s*=\s*prod/)).toBeVisible(); // Matched target appears in the target list await expect( diff --git a/web/package.json b/web/package.json index d30a191d..54fdd3dc 100644 --- a/web/package.json +++ b/web/package.json @@ -36,6 +36,7 @@ "@tailwindcss/vite": "^4.3.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", + "@types/node": "^26.2.0", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", diff --git a/web/playwright.config.ts b/web/playwright.config.ts index 14a272ac..38c312d9 100644 --- a/web/playwright.config.ts +++ b/web/playwright.config.ts @@ -8,7 +8,9 @@ export default defineConfig({ }, fullyParallel: false, retries: 1, - reporter: "html", + reporter: process.env.CI + ? [["github"], ["html", { open: "never" }]] + : "html", use: { baseURL: "http://localhost:8090", trace: "on-first-retry", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 407e9fab..83d371f2 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -65,13 +65,16 @@ importers: version: 1.61.1 '@tailwindcss/vite': specifier: ^4.3.1 - version: 4.3.2(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0)) + version: 4.3.2(vite@8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0)) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 '@testing-library/react': specifier: ^16.3.2 version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@types/node': + specifier: ^26.2.0 + version: 26.2.0 '@types/react': specifier: ^19.2.17 version: 19.2.17 @@ -80,7 +83,7 @@ importers: version: 19.2.3(@types/react@19.2.17) '@vitejs/plugin-react': specifier: ^6.0.2 - version: 6.0.3(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0)) + version: 6.0.3(vite@8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0)) concurrently: specifier: ^10.0.3 version: 10.0.3 @@ -113,10 +116,10 @@ importers: version: 8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@7.0.2) vite: specifier: ^8.0.16 - version: 8.0.16(jiti@2.7.0)(yaml@2.9.0) + version: 8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0) vitest: specifier: ^4.1.9 - version: 4.1.10(jsdom@29.1.1)(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0)) + version: 4.1.10(@types/node@26.2.0)(jsdom@29.1.1)(vite@8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0)) packages: @@ -995,6 +998,9 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/node@26.2.0': + resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -2005,6 +2011,9 @@ packages: engines: {node: '>=16.20.0'} hasBin: true + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + undici@7.28.0: resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} @@ -2934,12 +2943,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 '@tailwindcss/oxide-win32-x64-msvc': 4.3.2 - '@tailwindcss/vite@4.3.2(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.2(vite@8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.2 '@tailwindcss/oxide': 4.3.2 tailwindcss: 4.3.2 - vite: 8.0.16(jiti@2.7.0)(yaml@2.9.0) + vite: 8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0) '@tanstack/history@1.162.0': {} @@ -3025,6 +3034,10 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/node@26.2.0': + dependencies: + undici-types: 8.3.0 + '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: '@types/react': 19.2.17 @@ -3184,10 +3197,10 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true - '@vitejs/plugin-react@6.0.3(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0))': + '@vitejs/plugin-react@6.0.3(vite@8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.0.16(jiti@2.7.0)(yaml@2.9.0) + vite: 8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0) '@vitest/expect@4.1.10': dependencies: @@ -3198,13 +3211,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.10(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(vite@8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.16(jiti@2.7.0)(yaml@2.9.0) + vite: 8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0) '@vitest/pretty-format@4.1.10': dependencies: @@ -3928,6 +3941,8 @@ snapshots: '@typescript/typescript-win32-arm64': 7.0.2 '@typescript/typescript-win32-x64': 7.0.2 + undici-types@8.3.0: {} + undici@7.28.0: {} update-browserslist-db@1.2.3(browserslist@4.28.2): @@ -3959,7 +3974,7 @@ snapshots: dependencies: react: 19.2.7 - vite@8.0.16(jiti@2.7.0)(yaml@2.9.0): + vite@8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -3967,14 +3982,15 @@ snapshots: rolldown: 1.0.3 tinyglobby: 0.2.17 optionalDependencies: + '@types/node': 26.2.0 fsevents: 2.3.3 jiti: 2.7.0 yaml: 2.9.0 - vitest@4.1.10(jsdom@29.1.1)(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0)): + vitest@4.1.10(@types/node@26.2.0)(jsdom@29.1.1)(vite@8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.10(vite@8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -3991,9 +4007,10 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.0.16(jiti@2.7.0)(yaml@2.9.0) + vite: 8.0.16(@types/node@26.2.0)(jiti@2.7.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: + '@types/node': 26.2.0 jsdom: 29.1.1 transitivePeerDependencies: - msw diff --git a/web/tsconfig.json b/web/tsconfig.json index 507eda58..4e431468 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -21,5 +21,5 @@ "@/*": ["./src/*"] } }, - "include": ["src"] + "include": ["src", "vite.config.ts", "playwright.config.ts"] } From ee2118ca0e77c66b1d9afae1494275b5f4945661 Mon Sep 17 00:00:00 2001 From: Elisa Date: Fri, 14 Aug 2026 14:30:37 +0200 Subject: [PATCH 2/2] refactor: drop redundant namespace setup from setup_solar --- hack/dev-cluster.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hack/dev-cluster.sh b/hack/dev-cluster.sh index cec7523b..da0f7f57 100755 --- a/hack/dev-cluster.sh +++ b/hack/dev-cluster.sh @@ -205,7 +205,7 @@ create_pull_secret() { --dry-run=client -o yaml | $KUBECTL apply -f - } -# setup_solar installs the Solar Helm chart into the solar-system namespace and applies the Zot deployment authorization manifest, setting component image repositories/tags to the current REGISTRY/TAG. +# setup_solar installs the Solar Helm chart into the solar-system namespace, setting component image repositories/tags to the current REGISTRY/TAG. The namespace and its prerequisites (trust label, Zot deploy auth secret) are set up by main beforehand. setup_solar() { echo -e "\nSETTING UP SOLAR:\n" local pull_secret_args=() @@ -214,7 +214,6 @@ setup_solar() { pull_secret_args=(--set 'global.imagePullSecrets[0].name=ghcr-pull-secret') fi $HELM upgrade --install \ - --create-namespace \ --namespace=solar-system \ solar charts/solar \ -f test/fixtures/solar.values.yaml \ @@ -225,9 +224,6 @@ setup_solar() { --set controller.image.tag="$TAG" \ --set renderer.image.tag="$TAG" \ "${pull_secret_args[@]}" - $KUBECTL apply --namespace=solar-system \ - -f test/fixtures/e2e/zot-deploy-auth.yaml - $KUBECTL label namespace solar-system trust=enabled --overwrite # Wait for the aggregated apiserver to be ready before returning. Without # this, callers (e.g. the UI e2e tests) can hit solar.opendefense.cloud @@ -292,6 +288,8 @@ main() { if [[ "$SKIP_SOLAR" != "true" ]]; then $KUBECTL create namespace solar-system 2>/dev/null || true $KUBECTL label namespace solar-system trust=enabled --overwrite + $KUBECTL apply --namespace=solar-system \ + -f test/fixtures/e2e/zot-deploy-auth.yaml setup_solar setup_discovery fi