From f71c6d21b219e760503608289f779bf11e111080 Mon Sep 17 00:00:00 2001 From: Stanislav Jakuschevskij Date: Mon, 20 Apr 2026 18:28:08 +0200 Subject: [PATCH] ci: add GitHub Actions CI/CD pipeline Add single workflow (ci.yml) that runs lint, build, and test on pull requests, then builds and publishes a container image to GHCR on master merge. Follows knative/func patterns: top-level permissions, concurrency groups, version pinning. Fix ESLint config by removing redundant rule spreads that re-enabled the base no-unused-vars rule over the TypeScript version. Add Jest globals for test files. Downgrade @console/pluginAPI shared dep to >=4.19.0. Restructure README: move deployment section to the top with OCP 4.19 prerequisite and GHCR registry link. Add separate prerequisites for deployment and development sections. Add hack/next-plan-number.sh for deterministic plan numbering based on remote PR count. Co-Authored-By: Claude --- .github/workflows/ci.yml | 161 +++++++++++ AGENTS.md | 4 + README.md | 59 +++-- docs/WORKFLOW.md | 2 +- docs/claude-progress.txt | 20 ++ docs/features.json | 28 +- docs/plans/completed/010-ci-cd-pipelines.md | 250 ++++++++++++++++++ eslint.config.mjs | 17 +- hack/next-plan-number.sh | 8 + package.json | 2 +- src/components/CreateFunctionForm.test.tsx | 24 +- src/components/EmptyState.tsx | 5 +- src/services/function/FunctionService.test.ts | 4 +- 13 files changed, 529 insertions(+), 55 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 docs/plans/completed/010-ci-cd-pipelines.md create mode 100755 hack/next-plan-number.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..bed8c10 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,161 @@ +name: CI + +permissions: + id-token: write # Required for signing + contents: read + packages: write + attestations: write + pages: write + +on: + push: + branches: + - master + pull_request: + branches: + - master + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + IMAGE: ghcr.io/twogiants/console-functions-plugin + +jobs: + # -------- + # CHECKS + # -------- + checks: + name: Lint and Test + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "22" + cache: yarn + + - name: Enable Corepack + run: corepack enable + + - name: Install Dependencies + run: yarn install --immutable + + - name: Lint + run: yarn lint + + - name: Test + run: yarn test + + # ----------- + # BUILD IMAGE + # ----------- + build-image: + name: Build Image + needs: checks + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v6 + - uses: docker/setup-qemu-action@v4 + - uses: docker/setup-buildx-action@v4 + + - name: Build Image + uses: docker/build-push-action@v7 + env: + SOURCE_DATE_EPOCH: 0 + with: + context: . + push: false + # Multi-arch is required for OCP. + platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x + cache-from: type=gha + cache-to: type=gha,mode=max + + # ------------- + # PUBLISH IMAGE + # ------------- + publish: + name: Publish Image + needs: build-image + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + timeout-minutes: 30 + outputs: + digest: ${{ steps.build-and-push.outputs.digest }} + steps: + - uses: actions/checkout@v6 + - uses: docker/setup-qemu-action@v4 + - uses: docker/setup-buildx-action@v4 + + - name: Login to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and Push Image + id: build-and-push + uses: docker/build-push-action@v7 + env: + SOURCE_DATE_EPOCH: 0 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x + tags: | + ${{ env.IMAGE }}:latest + ${{ env.IMAGE }}:sha-${{ github.sha }} + cache-from: type=gha + annotations: | + index:org.opencontainers.image.description=Serverless Functions Console Plugin for OpenShift + index:org.opencontainers.image.source=https://github.com/twoGiants/func-console + index:org.opencontainers.image.vendor=https://github.com/twoGiants/func-console + index:org.opencontainers.image.url=https://github.com/twoGiants/func-console/pkgs/container/console-functions-plugin + + # Attestation is required for OCP. + - name: Attest Build Provenance + uses: actions/attest-build-provenance@v4 + with: + subject-name: ${{ env.IMAGE }} + subject-digest: ${{ steps.build-and-push.outputs.digest }} + push-to-registry: true + + # ------------ + # DEPLOY PAGES + # ------------ + deploy-pages: + name: Deploy Pages + needs: publish + runs-on: ubuntu-latest + timeout-minutes: 5 + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/checkout@v6 + + - name: Setup Helm + uses: azure/setup-helm@v5 + + - name: Generate deployment YAML + run: | + mkdir public + helm template console-functions-plugin charts/openshift-console-plugin \ + -n console-functions-plugin \ + --set "plugin.image=${{ env.IMAGE }}@${{ needs.publish.outputs.digest }}" \ + > public/plugin.yaml + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v5 + with: + path: ./public + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/AGENTS.md b/AGENTS.md index 01dff35..052bb77 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,6 +7,10 @@ This is the project map. Read this first, every session. FaaS PoC UI for OpenShift Console — React + TypeScript + Webpack + PatternFly 6 + OCP Dynamic Plugin SDK. See `docs/design/` for full design specs. +## Writing Style + +No em dashes (`—`). Use commas, periods, or parentheses instead. + ## Knowledge Base | File | Purpose | diff --git a/README.md b/README.md index 8eef797..e48cc02 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,40 @@ A Functions-as-a-Service PoC UI for the OpenShift Web Console. Developers create Built as an [OpenShift Console dynamic plugin](https://github.com/openshift/console/tree/main/frontend/packages/console-dynamic-plugin-sdk) using React, TypeScript, and PatternFly 6. -## Prerequisites +## Deployment on cluster + +### Prerequisites + +- [oc](https://console.redhat.com/openshift/downloads) CLI +- An [OpenShift 4.19 cluster](https://console.redhat.com/openshift/create) + +### Quick install + +```shell +oc new-project console-functions-plugin +oc apply -f https://twogiants.github.io/func-console/plugin.yaml +``` + +### Manual install (requires [Helm](https://helm.sh)) + +```shell +oc new-project console-functions-plugin +helm upgrade -i console-functions-plugin charts/openshift-console-plugin \ + -n console-functions-plugin --create-namespace \ + --set "plugin.image=ghcr.io/twogiants/console-functions-plugin:latest@sha256:" +``` + +To deploy a specific build, use its git commit SHA as the tag: + +```shell +--set "plugin.image=ghcr.io/twogiants/console-functions-plugin:sha-" +``` + +Available image tags are listed in the [container registry](https://github.com/twoGiants/func-console/pkgs/container/console-functions-plugin). Consult the chart [values](charts/openshift-console-plugin/values.yaml) file for additional parameters. + +## Development + +### Prerequisites - [Node.js](https://nodejs.org/en/) (v18+) - [Yarn](https://yarnpkg.com) (v4) @@ -12,8 +45,6 @@ Built as an [OpenShift Console dynamic plugin](https://github.com/openshift/cons - [Docker](https://www.docker.com) or [podman 3.2.0+](https://podman.io) - An [OpenShift cluster](https://console.redhat.com/openshift/create) -## Development - ### Option 1: Local In one terminal window, run: @@ -93,28 +124,6 @@ NOTE: If you have a Mac with Apple silicon, you will need to add the flag `--platform=linux/amd64` when building the image to target the correct platform to run in-cluster. -## Deployment on cluster - -A [Helm](https://helm.sh) chart is available to deploy the plugin to an OpenShift environment. - -The following Helm parameters are required: - -`plugin.image`: The location of the image containing the plugin that was previously pushed - -Additional parameters can be specified if desired. Consult the chart [values](charts/openshift-console-plugin/values.yaml) file for the full set of supported parameters. - -### Installing the Helm Chart - -Install the chart using the name of the plugin as the Helm release name into a new namespace or an existing namespace as specified by the `plugin.name` parameter and providing the location of the image within the `plugin.image` parameter by using the following command: - -```shell -helm upgrade -i my-plugin charts/openshift-console-plugin -n my-namespace --create-namespace --set plugin.image=my-plugin-image-location -``` - -NOTE: When deploying on OpenShift 4.10, it is recommended to add the parameter `--set plugin.securityContext.enabled=false` which will omit configurations related to Pod Security. - -NOTE: When defining i18n namespace, adhere `plugin__` format. The name of the plugin should be extracted from the `consolePlugin` declaration within the [package.json](package.json) file. - ## i18n The plugin uses [react-i18next](https://react.i18next.com/) for translations. The i18n namespace must match diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index 2444b38..67c6751 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -31,7 +31,7 @@ For each comment: read the full text and its diff hunk context, make the fix, th ## Branching -Create a feature branch per plan: `--` where `` matches the plan number and `` the conventional commit type as per our [Git Commit Guide](references/commit-message-guide.md#conventional-commits). Example: `001-feat-function-list-empty-state`. If we're on a feature branch already do nothing. +Create a feature branch per plan: `--` where `` is determined by `./hack/next-plan-number.sh` (next PR number on the remote) and `` is the conventional commit type as per our [Git Commit Guide](references/commit-message-guide.md#conventional-commits). The plan file uses the same number. Example: `010-feat-function-list-empty-state`. If we're on a feature branch already do nothing. ## Pull Requests diff --git a/docs/claude-progress.txt b/docs/claude-progress.txt index 48b8be9..374cbec 100644 --- a/docs/claude-progress.txt +++ b/docs/claude-progress.txt @@ -1,6 +1,26 @@ # Claude Progress Log # Newest entries first. Agents: append your entry at the top after the header. +--- +## 2026-04-20 | Session: CI/CD pipeline and ESLint fixes +Worked on: GitHub Actions CI/CD pipeline, ESLint config fixes, README restructure +Completed: +- Single CI workflow (ci.yml) with three jobs: checks (lint + test), build-image (multi-arch Docker build, no push), publish (build + push to GHCR, master only) +- Multi-arch builds (amd64, arm64, ppc64le, s390x) via docker/setup-qemu-action + docker/setup-buildx-action, required for OCP +- Build attestation signing via actions/attest-build-provenance, required for OCP +- OCI annotations on image index (description, source, vendor, url), following knative/func patterns +- Docker layer caching via GHA cache (build-image writes, publish reads) +- Follows knative/func patterns: top-level permissions (id-token, contents, packages, attestations), concurrency groups, cancel-in-progress for PRs +- Fixed ESLint config: removed redundant rule spreads in src/** block that re-enabled base no-unused-vars over @typescript-eslint/no-unused-vars (all 194 lint errors were config issues, not code issues) +- Added Jest globals block for test files +- Downgraded @console/pluginAPI shared dep to >=4.19.0 +- Restructured README: deployment section moved to top with OCP 4.19 prerequisite, GHCR registry link, separate prerequisites for deployment and development +- Added hack/next-plan-number.sh for deterministic plan/branch numbering based on remote PR count +- Updated WORKFLOW.md branching convention to use the helper script +- 8 suites, 34 tests, all passing, zero lint errors +Left off: PR #10 open. User needs to enable GITHUB_TOKEN write permissions in repo Settings and add branch protection rule for master. +Blockers: None + --- ## 2026-04-17 | Session: FaaS route rename and nav restructure Worked on: Rename routes from /functions to /faas, restructure nav for both perspectives diff --git a/docs/features.json b/docs/features.json index 531d686..7e3eb2b 100644 --- a/docs/features.json +++ b/docs/features.json @@ -109,7 +109,9 @@ "UserAvatar is clickable only on the Function List Page, clicking reopens PatModal to change the PAT and associated GitHub user", "On Create and Edit pages, UserAvatar displays the GitHub user but is not clickable (PAT cannot be changed from those pages)", "When PatModal is dismissed without a PAT, the Function List Page (both empty state and table view) shows a hint text directing the user to click 'Connect to GitHub' in the top-right corner to set a PAT", - "PAT change logic is encapsulated in a useUserAvatar custom hook following layered architecture (Hook imports Service, Component imports Hook)" + "PAT change logic is encapsulated in a useUserAvatar custom hook following layered architecture (Hook imports Service, Component imports Hook)", + "If the GH PAT is not set in the session then the Create button is inactive/disabled. The tooltiop of the button should say that PAT is required.", + "The GH PAT must not be compiled/hardcode into code at compile time." ], "passes": false }, @@ -124,5 +126,29 @@ "Deployed functions without a matching repo still appear in the table with available cluster data (name, namespace, status, replicas, url)" ], "passes": false + }, + { + "category": "technical", + "description": "CI/CD: GitHub Actions pipelines for PR checks and master publish to GHCR, plus lint fixes and README deployment instructions", + "steps": [ + "PR pipeline (.github/workflows/pr.yml): triggers on pull_request to master, runs yarn install --immutable, yarn lint, yarn test — branch cannot merge without passing checks and an approval", + "Publish pipeline (.github/workflows/publish.yml): triggers on push to master (merged PRs), runs yarn install --immutable, yarn lint, yarn test, then builds multi-arch container image via docker/build-push-action using existing Dockerfile and pushes to ghcr.io/twogiants/console-functions-plugin with :latest and :sha- tags", + "Both pipelines use actions/setup-node@v4 with node-version 22, corepack enable, and cache: yarn for Yarn install caching (GitHub runners ship Node 20 by default with corepack disabled)", + "Both pipelines authenticate to GHCR using GITHUB_TOKEN secret with packages:write permission (token added to repo's Actions secrets)", + "Add yarn lint script to package.json if missing, run it, and fix all lint errors in the codebase", + "Update README.md deployment section (lines 96-117) — replace generic Helm boilerplate, placeholder commands, and obsolete OCP 4.10 / i18n notes with concrete instructions: oc new-project console-functions-plugin, helm upgrade -i console-functions-plugin charts/openshift-console-plugin -n console-functions-plugin --create-namespace --set plugin.image=ghcr.io/twogiants/console-functions-plugin:latest@sha256:" + ], + "passes": true + }, + { + "category": "functional", + "description": "Set GitHub Secret (KUBECONFIG) on created function repos so GH Actions can deploy to the cluster", + "steps": [ + "Add createSecret method to SourceControlService interface", + "Encrypt secret value with repo public key using tweetnacl or libsodium.js", + "Wire into create flow: after repo creation and file push, set KUBECONFIG secret on the repo", + "GH Actions workflow can authenticate to the cluster and run func deploy" + ], + "passes": false } ] diff --git a/docs/plans/completed/010-ci-cd-pipelines.md b/docs/plans/completed/010-ci-cd-pipelines.md new file mode 100644 index 0000000..b722ed5 --- /dev/null +++ b/docs/plans/completed/010-ci-cd-pipelines.md @@ -0,0 +1,250 @@ +# CI/CD Pipelines Implementation Plan + +> **REQUIRED SUB-SKILL:** Use the executing-plans skill to implement this plan task-by-task. + +**Goal:** Single GitHub Actions workflow that runs lint + test on PRs and additionally builds + publishes a container image to GHCR on master merges. + +**Architecture:** One workflow file following knative/func patterns (single file, dual trigger, conditional publish job). ESLint config fixed (redundant rule spreads removed, Jest globals added). README deployment section rewritten with concrete commands. + +**Tech Stack:** GitHub Actions, docker/build-push-action, actions/setup-node@v4 (Node 22, corepack, yarn cache), ESLint flat config + +--- + +### Task 1: Fix ESLint config — remove redundant rule spreads and add Jest globals + +All 194 lint errors are ESLint config issues, not code issues: +- 185 `no-undef` errors: Jest globals (`jest`, `describe`, `it`, `expect`, etc.) not declared for test files. +- 9 `no-unused-vars` false positives: the base ESLint `no-unused-vars` rule flags TypeScript interface parameter names (e.g., `onSubmit: (data: FormData) => void`). The `@typescript-eslint/no-unused-vars` rule handles these correctly and is already enabled by `tseslint.configs.recommended` at the top level. But the `src/**` block re-spreads `...eslint.configs.recommended.rules`, which re-enables the base rule. The `...tseslint.configs.recommended.rules` spread is a no-op because `tseslint.configs.recommended` is an array, not an object, so `.rules` is `undefined`. + +**Files:** +- Modify: `eslint.config.mjs` + +**Step 1: Remove redundant rule spreads from the `src/**` block** + +In the `src/**` config block, remove `...eslint.configs.recommended.rules` and `...tseslint.configs.recommended.rules` from the `rules` object. Keep only the react rules: + +```javascript + rules: { + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + }, +``` + +**Step 2: Add Jest globals for test files** + +Add a new config block after the `src/**` block and before the `integration-tests/**` block: + +```javascript + { + files: ['src/**/*.test.{ts,tsx}'], + languageOptions: { + globals: { + jest: 'readonly', + describe: 'readonly', + it: 'readonly', + expect: 'readonly', + beforeEach: 'readonly', + afterEach: 'readonly', + beforeAll: 'readonly', + afterAll: 'readonly', + }, + }, + }, +``` + +**Step 3: Run lint to verify zero errors** + +Run: `yarn lint 2>&1 | tail -5` +Expected: No errors (exit 0) + +**Step 4: Run tests to verify nothing broke** + +Run: `yarn test` +Expected: 8 suites, 34 tests, all passing + +**Step 5: Commit** + +```bash +git add eslint.config.mjs +git commit -m "fix: remove redundant ESLint rule spreads and add Jest globals" +``` + +--- + +### Task 2: Downgrade @console/pluginAPI to ^4.19.0 + +**Files:** +- Modify: `package.json:98` — change `"@console/pluginAPI": "^4.21.0"` to `"@console/pluginAPI": "^4.19.0"` + +**Step 1: Update package.json** + +Change the moduleFederation shared dependency: + +```json +"@console/pluginAPI": "^4.19.0" +``` + +**Step 2: Run tests to verify nothing broke** + +Run: `yarn test` +Expected: 8 suites, 34 tests, all passing + +**Step 3: Commit** + +```bash +git add package.json +git commit -m "fix: downgrade @console/pluginAPI shared dep to ^4.19.0" +``` + +--- + +### Task 3: Create the GitHub Actions workflow file + +**Files:** +- Create: `.github/workflows/ci.yml` + +**Step 1: Create the workflow file** + +Follow the knative/func patterns: top-level permissions, concurrency group, global env for version pins, section comments separating job groups, timeout-minutes on every job. + +```yaml +name: CI + +permissions: + contents: read + packages: write + +on: + push: + branches: + - master + pull_request: + branches: + - master + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + NODE_VERSION: "22" + +jobs: + # -------- + # CHECKS + # -------- + checks: + name: Lint and Test + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Enable Corepack + run: corepack enable + + - name: Install Dependencies + run: yarn install --immutable + + - name: Lint + run: yarn lint + + - name: Test + run: yarn test + + # ----------------- + # BUILD AND PUBLISH + # ----------------- + build-and-publish: + name: Build and Publish + needs: checks + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and Push Image + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ghcr.io/twogiants/console-functions-plugin:latest + ghcr.io/twogiants/console-functions-plugin:sha-${{ github.sha }} +``` + +**Step 2: Validate YAML syntax** + +Run: `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/ci.yml'))" && echo "Valid YAML"` +Expected: `Valid YAML` + +**Step 3: Commit** + +```bash +git add .github/workflows/ci.yml +git commit -m "ci: add GitHub Actions workflow for PR checks and master publish" +``` + +--- + +### Task 4: Update README.md deployment section + +**Files:** +- Modify: `README.md:96-117` + +**Step 1: Replace lines 96-117 with concrete deployment instructions** + +Replace the entire "Deployment on cluster" section (from `## Deployment on cluster` through the two NOTE paragraphs) with: + +```markdown +## Deployment on cluster + +```shell +oc new-project console-functions-plugin +helm upgrade -i console-functions-plugin charts/openshift-console-plugin \ + -n console-functions-plugin --create-namespace \ + --set "plugin.image=ghcr.io/twogiants/console-functions-plugin:latest@sha256:" +``` + +Consult the chart [values](charts/openshift-console-plugin/values.yaml) file for additional parameters. + +``` + +**Step 2: Verify the README renders correctly** + +Visually inspect the changed section in the file. + +**Step 3: Commit** + +```bash +git add README.md +git commit -m "docs: update deployment section with concrete GHCR instructions" +``` + +--- + +### Task 5: Final verification + +**Step 1: Run full lint + test suite** + +Run: `yarn lint && yarn test` +Expected: Zero lint errors, 8 suites, 34 tests, all passing + +**Step 2: Review all changes** + +Run: `git log --oneline master..HEAD` +Verify 4 commits covering: ESLint config fix, pluginAPI downgrade, workflow file, README update. diff --git a/eslint.config.mjs b/eslint.config.mjs index 26f5a97..30e2275 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -19,8 +19,6 @@ export default tseslint.config( react, }, rules: { - ...eslint.configs.recommended.rules, - ...tseslint.configs.recommended.rules, ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, @@ -38,6 +36,21 @@ export default tseslint.config( }, } }, + { + files: ['src/**/*.test.{ts,tsx}'], + languageOptions: { + globals: { + jest: 'readonly', + describe: 'readonly', + it: 'readonly', + expect: 'readonly', + beforeEach: 'readonly', + afterEach: 'readonly', + beforeAll: 'readonly', + afterAll: 'readonly', + }, + }, + }, { files: ['integration-tests/**/*.{ts,tsx,js}'], ...cypress.configs.recommended, diff --git a/hack/next-plan-number.sh b/hack/next-plan-number.sh new file mode 100755 index 0000000..002f814 --- /dev/null +++ b/hack/next-plan-number.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Print the next plan/branch number based on the remote PR count. +# Usage: ./hack/next-plan-number.sh +set -euo pipefail + +repo="twoGiants/func-console" +highest=$(gh pr list --repo "$repo" --state all --json number --jq '.[].number' | sort -n | tail -1) +printf '%03d\n' $(( ${highest:-0} + 1 )) diff --git a/package.json b/package.json index 9664b97..a117f7c 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "FunctionEditPage": "./views/FunctionEditPage" }, "dependencies": { - "@console/pluginAPI": "^4.21.0" + "@console/pluginAPI": ">=4.19.0" } }, "packageManager": "yarn@4.13.0", diff --git a/src/components/CreateFunctionForm.test.tsx b/src/components/CreateFunctionForm.test.tsx index b45b85c..91e74cd 100644 --- a/src/components/CreateFunctionForm.test.tsx +++ b/src/components/CreateFunctionForm.test.tsx @@ -20,9 +20,7 @@ describe('CreateFunctionForm', () => { }); it('renders all form fields', () => { - render( - , - ); + render(); expect(screen.getByRole('textbox', { name: /Owner/ })).toBeInTheDocument(); expect(screen.getByRole('textbox', { name: /Repository/ })).toBeInTheDocument(); @@ -34,26 +32,20 @@ describe('CreateFunctionForm', () => { }); it('renders Create and Cancel buttons', () => { - render( - , - ); + render(); expect(screen.getByRole('button', { name: /Create/ })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /Cancel/ })).toBeInTheDocument(); }); it('disables Create button when required fields are empty', () => { - render( - , - ); + render(); expect(screen.getByRole('button', { name: /Create/ })).toBeDisabled(); }); it('disables Create button when isSubmitting is true', () => { - render( - , - ); + render(); expect(screen.getByRole('button', { name: /Create/ })).toBeDisabled(); }); @@ -61,9 +53,7 @@ describe('CreateFunctionForm', () => { it('calls onCancel when Cancel is clicked', async () => { const user = userEvent.setup(); - render( - , - ); + render(); await user.click(screen.getByRole('button', { name: /Cancel/ })); expect(onCancel).toHaveBeenCalled(); @@ -72,9 +62,7 @@ describe('CreateFunctionForm', () => { it('calls onSubmit with form data when form is filled and Create is clicked', async () => { const user = userEvent.setup(); - render( - , - ); + render(); await user.type(screen.getByRole('textbox', { name: /Owner/ }), 'testuser'); await user.type(screen.getByRole('textbox', { name: /Repository/ }), 'my-repo'); diff --git a/src/components/EmptyState.tsx b/src/components/EmptyState.tsx index 4e08828..dc204cc 100644 --- a/src/components/EmptyState.tsx +++ b/src/components/EmptyState.tsx @@ -17,10 +17,7 @@ export function FunctionsEmptyState() { {t('Create a serverless function to get started.')} - diff --git a/src/services/function/FunctionService.test.ts b/src/services/function/FunctionService.test.ts index 0bf529c..a7b5739 100644 --- a/src/services/function/FunctionService.test.ts +++ b/src/services/function/FunctionService.test.ts @@ -46,8 +46,6 @@ describe('FunctionBackendService', () => { const service = new FunctionBackendService(); - await expect(service.generateFunction(config)).rejects.toThrow( - 'failed to initialize function', - ); + await expect(service.generateFunction(config)).rejects.toThrow('failed to initialize function'); }); });