- Delete cli/.env.e2e.example (root-level duplicate).
The canonical template is cli/test/e2e/.env.e2e.example.
- Add missing @ts-expect-error before the early-return project.provide()
call in global-setup.ts (line 167). The second call at line 290 already
had the suppressor; the first one was accidentally left without it,
causing TS2345 in the CLI Tests CI.
When CI runs parallel suite jobs, each job's vitest globalSetup calls
provisionApps() independently. Simultaneous findAppByName() calls all
return 'not found' (race condition), causing each job to importFromDsl
and create duplicate apps — especially in auto_test1 (ws2-workflow.yml).
Fix:
1. Check if all DIFY_E2E_*_APP_ID env vars are pre-set (from CI provision
job outputs). If so, skip provisionApps entirely and reuse those IDs.
2. Fall back to env-sourced app IDs in capabilities assignment so that
provision job outputs are always honoured even if provisionApps runs.
Workflow (cli-e2e.yml):
- Plan B: split into parallel jobs after a shared provision step
- provision job: mints token + provisions DSL apps, outputs IDs to downstream jobs
- suite-run: matrix of 5 parallel jobs (basic/streaming/conversation/file/hitl)
- suite-last: serial, waits for all parallel jobs; runs use/devices/logout/agent
- Add DIFY_E2E_NO_KEYRING=1 globally (Linux CI has no keychain)
- timeout-minutes: 30 → 60; smoke filter: --testNamePattern → -t
New script (cli/scripts/e2e-provision.ts):
- Standalone Bun script: login + mint token + discover workspaces + provision apps
- Outputs to GITHUB_OUTPUT + .provision-output.json
Test fixes:
- run-app-streaming: DIFY_E2E_NO_KEYRING=1 in spawn-based tests
- run-app-basic: fix cache test (DIFY_CACHE_DIR + app-info.yml path)
- run-app-conversation/file: SSO injectAuth uses new hosts.yml format
- get-app-list: tag filter auto-provisions e2e-test tag
- vitest.e2e.config.ts: DIFY_E2E_SINGLE_FILE override + agent suite
Both augmentation approaches fail under tsgo in the Main CI pipeline:
- 'vitest' augmentation → TS2300 (re-exported ProvidedContext in @0.1.22)
- '@voidzero-dev/vite-plus-test' augmentation → TS2664 (module not found;
tsgo scans cli/ tree but cannot resolve pnpm virtual-store symlinks)
Use @ts-expect-error at the three call sites (global-setup, devices, logout)
as the only option that satisfies both tsgo and the ESLint ban-ts-comment
rule (which requires @ts-expect-error over @ts-ignore).
Root cause: keyof ProvidedContext = never in @0.1.22 because the interface
is empty. inject() / project.provide() have T extends keyof ProvidedContext,
so any string literal — including 'as any' casts — is TS2345 under tsgo
which scans the whole cli/ tree (not just the src/ include in tsconfig.json).
Fix: augment '@voidzero-dev/vite-plus-test' directly (where ProvidedContext
is defined as an empty interface meant for user extension via declaration
merging) instead of 'vitest'. This avoids TS2300 duplicate identifier in
@0.1.22 and restores full type safety for inject/provide without any cast.
Revert all previous workaround casts ('as any', 'as unknown as') now that
the augmentation path is correct.
run-app-basic:
Widen stderr pattern for non-existent app test — new env returns
'server_5xx: Internal Server Error' (HTTP 500) instead of 404/not-found.
describe-app:
Wrap ANSI colour test with withRetry(3) to handle transient 500s on
cold-start against console-platform-dev.
run-app-streaming:
Workflow streaming test was passing only x='wf-stream-val'; the workflow
app requires num, enum_var and paragraph as required fields too.
Add all four required inputs to the --inputs payload.
cli.ts (mintFreshToken):
Increase console/api/login timeout from 10s to 20s — the dev environment
was timing out during the devices revoke test on CI.
In @0.1.22 ProvidedContext is an empty interface with no augmentation path
that avoids TS2345 (keyof ProvidedContext = never). Cast the string keys
with 'as any' (suppressed via eslint-disable) at each call site so both
project.provide() and inject() compile cleanly without module augmentation.
[email protected] exports ProvidedContext from its own module, making
any 'declare module vitest { ProvidedContext }' augmentation produce a
TS2300 duplicate identifier error and collapse the type to 'never'.
Fix: remove the module augmentation from vitest-context.ts entirely.
Callers (devices.e2e.ts, logout.e2e.ts) now cast inject() results with
'as E2ECapabilities' directly at the call site — no global type magic needed.
skip.ts:
- Return SuiteAPI/TestAPI with 'as unknown as' cast to bridge the
ChainableSuiteAPI incompatibility across vite-plus-test versions
(TS2322 / TS4058). Uses unknown intermediate to satisfy no-explicit-any.
vitest-context.ts:
- Change 'export type ProvidedContext' to 'interface ProvidedContext'
augmentation to avoid TS2300 duplicate identifier now that
[email protected] re-exports ProvidedContext from its own module.
TS4058: exported functions in skip.ts return vitest-internal types that
cannot be named by the CI type-checker ([email protected]).
Annotate with 'typeof describe' and 'typeof it' — both are stable,
publicly addressable types — to satisfy the type-check pipeline.
- Add .env.e2e.example as a template listing all required and optional
DIFY_E2E_* variables with empty values — safe to commit, helps new
contributors set up their local environment quickly.
- Remove .env.e2e.local (was a 0-byte placeholder with no practical use).
Current dev environment (console-platform-dev.dify.dev) has no enforced
file size limit, so the 20 MB upload completes successfully instead of
returning an error, causing the test to time out rather than fail cleanly.
Remove until a fixture with a known size cap is available.
- Override pnpm to v11 (packageManager field) before CLI install step to
resolve ERR_PNPM_BAD_PM_VERSION conflict with setup-web's pnpm@9
- Add per-suite dedicated tokens (logoutToken / devicesToken) in global-setup
via device flow to prevent token cross-contamination between suites
- Translate all Chinese comments and test names to English across e2e suites
- Fix injectAuth: add tokenId field so devices revoke correctly detects selfHit
- Fix HITL test: read action id dynamically from pause response instead of
hardcoding 'submit'
Covers auth, config, run, and CLI framework scenarios against a live
staging server using vitest + real difyctl binary.
Suite layout:
test/e2e/
├── helpers/
│ ├── cli.ts — run(), withAuthFixture(), mintFreshToken()
│ ├── assert.ts — assertExitCode, assertJson, assertErrorEnvelope
│ ├── cleanup-registry.ts — staging data teardown
│ └── retry.ts — withRetry() for flaky network assertions
├── setup/
│ ├── global-setup.ts — health-check, disposable token mint
│ └── global-teardown.ts — conversation cleanup
└── suites/
├── auth/ — status, use, whoami, devices, logout
├── config/ — path, get/set/unset/view, env override
└── run/ — basic, streaming, conversation, file, HITL
Key design decisions:
- Each test uses an isolated temp configDir via withAuthFixture()
- Logout and devices-revoke tests run last to avoid invalidating
the shared E.token used by all other suites
- mintFreshToken() mints a disposable dfoa_ token on demand via the
device flow API so revoke tests never touch the primary session
- Global retry is 0; flaky network calls use withRetry() locally
- test:e2e:smoke script filters to [P0] cases via testNamePattern
package.json: add test:e2e / test:e2e:smoke / test:e2e:local scripts
.gitignore: exclude .env.e2e, oclif.manifest.json, tmp/
.env.e2e.example: credential template for local setup