项目文件夹

文件
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:37:56 +08:00

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`