conductor-oss--conductor
a9cd7750f4
CI / unit-test (push) Has been cancelled
CI / detect-changes (push) Has been cancelled
CI / build (push) Has been cancelled
Publish docs via GitHub Pages / Deploy docs (push) Has been cancelled
CI / test-harness (push) Has been cancelled
CI / generate-e2e-matrix (push) Has been cancelled
CI / e2e (push) Has been cancelled
CI / build-ui (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
UI v2 Integration CI / E2E (Integration) (push) Has been cancelled
UI v2 CI / Lint, Format & Test (push) Has been cancelled
UI v2 CI / E2E (Mocked) (push) Has been cancelled
404 行
14 KiB
Markdown
404 行
14 KiB
Markdown
# Conductor UI v2
|
|
|
|
The open-source React UI for [Conductor](https://github.com/conductor-oss/conductor). It ships as both a **standalone web application** and an **npm library** that enterprise packages can extend via a plugin system.
|
|
|
|
## Running locally
|
|
|
|
### Prerequisites
|
|
|
|
- Node.js 18+
|
|
- [pnpm](https://pnpm.io/) 10.x — we use pnpm 10 (not v11) since pnpm v11 requires Node.js v22+. The exact version is pinned via `packageManager` in `package.json`. Run once to activate it:
|
|
```bash
|
|
corepack enable
|
|
```
|
|
- A running Conductor server (default: `http://localhost:8080`)
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
pnpm install
|
|
```
|
|
|
|
Configure the backend URL in `.env` (see `.env` for defaults):
|
|
|
|
```bash
|
|
VITE_WF_SERVER=http://localhost:8080
|
|
```
|
|
|
|
### Start the dev server
|
|
|
|
```bash
|
|
pnpm dev
|
|
```
|
|
|
|
The app will be available at `http://localhost:1234`.
|
|
|
|
### Runtime configuration
|
|
|
|
The app reads runtime config from `public/context.js`, which is loaded at startup (not bundled). Copy the example and edit as needed:
|
|
|
|
```bash
|
|
cp public/context.js.example public/context.js
|
|
```
|
|
|
|
This file sets feature flags (`window.conductor`) and auth config (`window.authConfig`) without requiring a rebuild.
|
|
|
|
## Available scripts
|
|
|
|
| Script | Description |
|
|
| ---------------------------------- | -------------------------------------------------- |
|
|
| `pnpm dev` | Start dev server with HMR |
|
|
| `pnpm build` | Build standalone app to `dist/` |
|
|
| `pnpm build:lib` | Build npm library to `dist/` |
|
|
| `pnpm build:all` | Build both app and library |
|
|
| `pnpm lint` | Run ESLint |
|
|
| `pnpm lint:fix` | Run ESLint with auto-fix |
|
|
| `pnpm prettier:check` | Check formatting |
|
|
| `pnpm prettier:write` | Auto-format all files |
|
|
| `pnpm typecheck` | Type-check without emitting |
|
|
| `pnpm test` | Run Vitest unit tests (single pass) |
|
|
| `pnpm test:watch` | Run Vitest in watch mode |
|
|
| `pnpm test:coverage` | Run Vitest with v8 coverage report |
|
|
| `pnpm test:e2e` | Run Playwright UI tests (mocked backend, headless) |
|
|
| `pnpm test:e2e:ui` | Open the Playwright interactive UI |
|
|
| `pnpm test:e2e:headed` | Run UI tests in a visible browser |
|
|
| `pnpm test:e2e:debug` | Step through UI tests in the Playwright debugger |
|
|
| `pnpm test:e2e:integration` | Run integration tests against a live backend |
|
|
| `pnpm test:e2e:integration:ui` | Integration tests in Playwright interactive UI |
|
|
| `pnpm test:e2e:integration:headed` | Integration tests in a visible browser |
|
|
|
|
## Testing
|
|
|
|
### Unit tests (Vitest)
|
|
|
|
Tests live alongside source files as `*.test.{ts,tsx}` and run in jsdom.
|
|
They cover utilities, state machines, and component logic without a browser or server.
|
|
|
|
```bash
|
|
pnpm test # single run
|
|
pnpm test:watch # re-runs on file change
|
|
pnpm test:coverage # produces coverage/index.html
|
|
```
|
|
|
|
### E2E tests (Playwright)
|
|
|
|
E2E tests live in `e2e/` and are run by Playwright against a real Chromium
|
|
browser. Every test mocks the Conductor backend with `page.route()`, so **no
|
|
running Conductor server is required** — the suite works entirely against the
|
|
built-in Vite dev server.
|
|
|
|
#### First-time setup
|
|
|
|
Install the Playwright browser binaries (one-time per machine):
|
|
|
|
```bash
|
|
pnpm exec playwright install --with-deps chromium
|
|
```
|
|
|
|
#### Running locally
|
|
|
|
```bash
|
|
# Headless (fastest) — reuses a running dev server on :1234 if one exists
|
|
pnpm test:e2e
|
|
|
|
# Interactive Playwright UI — best for writing and debugging tests
|
|
pnpm test:e2e:ui
|
|
|
|
# Watch the browser run the tests
|
|
pnpm test:e2e:headed
|
|
|
|
# Step through a single test with the Playwright debugger
|
|
pnpm test:e2e:debug
|
|
|
|
# Run one file
|
|
pnpm test:e2e e2e/smoke.spec.ts
|
|
|
|
# Run tests whose name matches a pattern
|
|
pnpm test:e2e --grep "navigates to"
|
|
```
|
|
|
|
If `pnpm dev` is already running on port 1234, Playwright reuses that server.
|
|
If nothing is running, it starts a dev server automatically for the test run.
|
|
|
|
#### Running in CI
|
|
|
|
Set `CI=true` (GitHub Actions does this automatically) and run:
|
|
|
|
```bash
|
|
pnpm exec playwright install --with-deps chromium
|
|
pnpm test:e2e
|
|
```
|
|
|
|
With `CI=true` the config:
|
|
|
|
- Always starts a fresh dev server (never reuses an existing one)
|
|
- Retries each failing test up to 2 times before marking it failed
|
|
- Uses a single worker to avoid resource contention
|
|
|
|
Example GitHub Actions job:
|
|
|
|
```yaml
|
|
- name: Install Playwright browsers
|
|
run: pnpm exec playwright install --with-deps chromium
|
|
|
|
- name: Run E2E tests
|
|
run: pnpm test:e2e
|
|
|
|
- name: Upload Playwright report
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: playwright-report
|
|
path: playwright-report/
|
|
retention-days: 7
|
|
```
|
|
|
|
### Integration tests (Playwright + live backend)
|
|
|
|
Integration tests live in `e2e/integration/` and use a separate config,
|
|
`playwright.integration.config.ts`. They talk to a real Conductor server and
|
|
verify the full stack end-to-end: the API client creates test data, the
|
|
browser navigates through the UI, and assertions confirm the data is rendered
|
|
correctly. Docker is managed automatically — no manual server management is
|
|
required.
|
|
|
|
#### How it works
|
|
|
|
1. **Global setup** (`e2e/integration/global-setup.ts`) checks whether a
|
|
Conductor server is already listening on port 8000. If not, it builds the
|
|
`conductor:server` Docker image if needed (uses layer cache after first run),
|
|
then starts `docker/docker-compose-ui-e2e.yaml` and waits for the `/health`
|
|
endpoint to return 200 (up to 4 minutes to account for cold JVM starts).
|
|
2. The app is **built with `vite build`** and then **served with `vite preview`**,
|
|
with `VITE_WF_SERVER=http://localhost:8000` passed to the preview server so
|
|
its `/api` proxy forwards requests to the Docker backend. Tests run against
|
|
the production bundle — the same artifact that gets deployed.
|
|
3. Each test file uses `e2e/integration/api-client.ts` to create isolated test
|
|
data (unique names per run) and cleans up in `afterAll`.
|
|
4. **Global teardown** stops the Docker stack only if setup started it — a
|
|
backend you started yourself before running the tests is left untouched.
|
|
|
|
#### Running integration tests locally
|
|
|
|
**Prerequisites:** Docker must be running.
|
|
|
|
```bash
|
|
pnpm test:e2e:integration
|
|
```
|
|
|
|
This single command does everything automatically:
|
|
|
|
1. Builds the `conductor:server` Docker image if it does not already exist
|
|
locally — slow the first time (~5–10 min) but Docker's layer cache makes
|
|
subsequent runs fast (~30s) unless server-side code has changed
|
|
2. Starts Postgres + the Conductor server via Docker Compose
|
|
(`docker/docker-compose-ui-e2e.yaml`) and waits up to 4 minutes for the
|
|
backend `/health` endpoint to respond
|
|
3. Builds the UI (`pnpm build`) with `VITE_WF_SERVER=http://localhost:8000`
|
|
4. Starts `vite preview` to serve the production bundle on port 1234, with
|
|
its `/api` proxy forwarding to the Docker backend
|
|
5. Runs the Playwright test suite against `http://localhost:1234`
|
|
6. Stops the Docker stack when the tests finish
|
|
|
|
**Common options**
|
|
|
|
```bash
|
|
# Interactive Playwright UI — step through tests visually, great for debugging
|
|
pnpm test:e2e:integration:ui
|
|
|
|
# Watch the browser execute the tests in real time
|
|
pnpm test:e2e:integration:headed
|
|
|
|
# Run a single spec file
|
|
pnpm test:e2e:integration e2e/integration/workflows.spec.ts
|
|
|
|
# Run tests whose name matches a pattern
|
|
pnpm test:e2e:integration --grep "appears in the"
|
|
|
|
# Skip Docker management if you already have a Conductor backend running
|
|
# on port 8000 (e.g. started with docker compose separately)
|
|
SKIP_DOCKER=true pnpm test:e2e:integration
|
|
|
|
# Keep the Docker stack running after the tests finish (faster re-runs)
|
|
SKIP_DOCKER_TEARDOWN=true pnpm test:e2e:integration
|
|
|
|
# Point the tests at a backend running on a non-default URL
|
|
CONDUCTOR_SERVER_URL=http://localhost:9000 pnpm test:e2e:integration
|
|
```
|
|
|
|
**Faster iteration after the first run**
|
|
|
|
On subsequent runs, if you keep the Docker stack alive with
|
|
`SKIP_DOCKER_TEARDOWN=true`, you can skip the Docker startup wait on the next
|
|
run because the setup script detects the backend is already healthy:
|
|
|
|
```bash
|
|
# First run — starts Docker, runs tests, leaves stack running
|
|
SKIP_DOCKER_TEARDOWN=true pnpm test:e2e:integration
|
|
|
|
# Subsequent runs — backend already up, jumps straight to build + test
|
|
pnpm test:e2e:integration
|
|
```
|
|
|
|
To stop the stack manually when you are done:
|
|
|
|
```bash
|
|
docker compose -p conductor-ui-e2e -f docker/docker-compose-ui-e2e.yaml down
|
|
```
|
|
|
|
#### Running integration tests in CI
|
|
|
|
`pnpm test:e2e:integration` automatically builds the app and starts `vite preview`
|
|
before running the tests, so no explicit build step is needed in CI.
|
|
|
|
```yaml
|
|
- name: Install Playwright browsers
|
|
run: pnpm exec playwright install --with-deps chromium
|
|
|
|
- name: Run integration tests
|
|
# Global setup builds the conductor:server image automatically on first run.
|
|
# The Playwright webServer config then runs `pnpm build && pnpm preview`.
|
|
run: pnpm test:e2e:integration
|
|
|
|
- name: Upload integration report
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: playwright-integration-report
|
|
path: playwright-integration-report/
|
|
retention-days: 7
|
|
```
|
|
|
|
If you cache the Docker image between CI runs (e.g. using GitHub Actions
|
|
`docker/build-push-action` with `cache-to`/`cache-from`), the server build
|
|
step drops from ~10 minutes to ~30 seconds on cache hits.
|
|
|
|
## Using as a library
|
|
|
|
Install directly from a tagged release of this repository. The `&path:/ui-next`
|
|
argument tells the package manager to use the `ui-next/` subdirectory as the
|
|
package root:
|
|
|
|
```bash
|
|
# pnpm (recommended)
|
|
pnpm add "conductor-oss/conductor#<tag>&path:/ui-next"
|
|
|
|
# npm / yarn
|
|
npm install "conductor-oss/conductor#<tag>&path:/ui-next"
|
|
```
|
|
|
|
Or pin the version in `package.json`:
|
|
|
|
```json
|
|
"conductor-ui": "conductor-oss/conductor#v1.0.0&path:/ui-next"
|
|
```
|
|
|
|
Replace `<tag>` / `v1.0.0` with the release tag you want to consume
|
|
(e.g. `v3.2.1`). Available tags:
|
|
https://github.com/conductor-oss/conductor/releases
|
|
|
|
Import styles in your app entry point:
|
|
|
|
```tsx
|
|
import "conductor-ui/styles.css"; // component styles
|
|
import "conductor-ui/global.css"; // global body/font styles (optional)
|
|
```
|
|
|
|
### Extending with plugins
|
|
|
|
The plugin system lets you register additional routes, sidebar items, task forms, auth providers, and more without modifying the core package.
|
|
|
|
```tsx
|
|
import { pluginRegistry, App } from "conductor-ui";
|
|
|
|
// Register a custom sidebar item
|
|
pluginRegistry.registerSidebarItem({
|
|
position: { target: "root", after: "definitionsSubMenu" },
|
|
item: {
|
|
id: "myFeature",
|
|
title: "My Feature",
|
|
icon: <MyIcon />,
|
|
linkTo: "/my-feature",
|
|
shortcuts: [],
|
|
hidden: false,
|
|
position: 350,
|
|
},
|
|
});
|
|
|
|
// Register a custom route
|
|
pluginRegistry.registerRoutes([
|
|
{
|
|
path: "/my-feature",
|
|
element: <MyFeaturePage />,
|
|
},
|
|
]);
|
|
|
|
// Render the app
|
|
function Root() {
|
|
return <App />;
|
|
}
|
|
```
|
|
|
|
### Plugin extension points
|
|
|
|
| Extension | Method | Description |
|
|
| --------------- | ------------------------------ | -------------------------------------------------- |
|
|
| Routes | `registerRoutes(routes)` | Add authenticated routes |
|
|
| Public routes | `registerPublicRoutes(routes)` | Add unauthenticated routes |
|
|
| Sidebar items | `registerSidebarItem(reg)` | Inject items into the sidebar |
|
|
| Task forms | `registerTaskForm(reg)` | Custom forms for task types in the workflow editor |
|
|
| Task menu items | `registerTaskMenuItem(reg)` | Add task types to the "Add Task" menu |
|
|
| Auth provider | `registerAuthProvider(reg)` | Replace the auth implementation |
|
|
| Search provider | `registerSearchProvider(reg)` | Add results to global search |
|
|
|
|
### Sidebar item positioning
|
|
|
|
Sidebar items use numeric positions so plugins can inject between core items without collisions. The core OSS positions are exported for reference:
|
|
|
|
```tsx
|
|
import { CORE_SIDEBAR_POSITIONS } from "conductor-ui";
|
|
|
|
// CORE_SIDEBAR_POSITIONS.ROOT:
|
|
// executionsSubMenu: 100
|
|
// runWorkflow: 200
|
|
// definitionsSubMenu:300
|
|
// helpMenu: 400
|
|
// swaggerItem: 500
|
|
|
|
pluginRegistry.registerSidebarItem({
|
|
position: { target: "root" },
|
|
item: {
|
|
id: "myItem",
|
|
position: 350, // between definitionsSubMenu (300) and helpMenu (400)
|
|
// ...
|
|
},
|
|
});
|
|
```
|
|
|
|
## Project structure
|
|
|
|
```
|
|
src/
|
|
├── components/ # Shared UI components
|
|
│ └── Sidebar/ # Sidebar with plugin-injectable menu
|
|
├── pages/ # Route-level page components
|
|
├── plugins/ # Plugin registry and fetch utilities
|
|
├── shared/ # Auth state machine and context
|
|
├── theme/ # MUI theme provider
|
|
├── types/ # Shared TypeScript types
|
|
└── utils/ # Feature flags, constants, helpers
|
|
public/
|
|
├── context.js # Runtime config (gitignored, not bundled)
|
|
└── context.js.example
|
|
```
|
|
|
|
## Peer dependencies
|
|
|
|
When consuming as a library, the following must be provided by the host app:
|
|
|
|
- `react` ^18
|
|
- `react-dom` ^18
|
|
- `react-router` / `react-router-dom` ^7
|
|
- `@mui/material`, `@mui/icons-material`, `@mui/system`, `@mui/x-date-pickers`
|
|
- `@emotion/react`, `@emotion/styled`
|