mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-03 20:51:58 +00:00
## Summary Add guidance to `docs/guidance/playwright.md` that new node-specific assertions should be methods on page objects/helpers rather than new `comfyExpect` custom matchers. ## Changes - **What**: New "Custom Assertions" section in Playwright guidance documenting that existing `comfyExpect` matchers are fine to use, but new assertions should go on the page object for IntelliSense discoverability. ## Review Focus Documentation-only change. No code refactoring — this is a convention for new code only. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10660-docs-add-convention-for-new-assertions-prefer-page-objects-over-custom-matchers-3316d73d3650816d97a8fbbdc33f6b75) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
205 lines
7.2 KiB
Markdown
205 lines
7.2 KiB
Markdown
---
|
|
globs:
|
|
- '**/*.spec.ts'
|
|
---
|
|
|
|
# Playwright E2E Test Conventions
|
|
|
|
See `docs/testing/*.md` for detailed patterns.
|
|
|
|
## Best Practices
|
|
|
|
- Follow [Playwright Best Practices](https://playwright.dev/docs/best-practices)
|
|
- Do NOT use `waitForTimeout` — use Locator actions and retrying assertions
|
|
- Prefer specific selectors (role, label, test-id)
|
|
- Test across viewports
|
|
|
|
## Window Globals
|
|
|
|
Browser tests access `window.app`, `window.graph`, and `window.LiteGraph` which are
|
|
optional in the main app types. Use non-null assertions (`!`) in E2E tests only:
|
|
|
|
```typescript
|
|
window.app!.graph!.nodes
|
|
window.LiteGraph!.registered_node_types
|
|
```
|
|
|
|
TODO: Consolidate into a central utility (e.g., `getApp()`) with runtime type checking.
|
|
|
|
## Type Assertions
|
|
|
|
Use specific type assertions when needed, never `as any`.
|
|
|
|
Acceptable:
|
|
|
|
```typescript
|
|
window.app!.extensionManager
|
|
id: 'TestSetting' as TestSettingId
|
|
type TestSettingId = keyof Settings
|
|
```
|
|
|
|
Forbidden:
|
|
|
|
```typescript
|
|
settings: testData as any
|
|
data as unknown as SomeType
|
|
```
|
|
|
|
Access internal state via `page.evaluate` and stores directly — don't change public API types to expose internals.
|
|
|
|
## Assertion Best Practices
|
|
|
|
Assert preconditions explicitly with a custom message so failures point to the broken assumption:
|
|
|
|
```typescript
|
|
expect(node.widgets, 'Widget count changed — update test fixture').toHaveLength(
|
|
4
|
|
)
|
|
await node.move(100, 200)
|
|
|
|
expect.soft(menuItem1).toBeVisible()
|
|
expect.soft(menuItem2).toBeVisible()
|
|
|
|
// Bad — bare expect on a precondition gives no context when it fails
|
|
expect(node.widgets).toHaveLength(4)
|
|
```
|
|
|
|
- `expect(x, 'reason')` for precondition checks unrelated to the test's purpose
|
|
- `expect.soft()` to verify multiple invariants without aborting on the first failure
|
|
|
|
## Test Structure: Arrange/Act/Assert
|
|
|
|
1. All mock setup, state resets, and fixture arrangement belongs in `test.beforeEach()` or Playwright fixtures
|
|
2. Inside `test()`, only act (user actions) and assert
|
|
3. Never call `clearAllMocks` or reset mock state mid-test
|
|
|
|
```typescript
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.workflow.loadWorkflow('test.json')
|
|
})
|
|
test('should do something', async ({ comfyPage }) => {
|
|
await comfyPage.menu.topbar.click()
|
|
await expect(comfyPage.menu.nodeLibraryTab.root).toBeVisible()
|
|
})
|
|
```
|
|
|
|
## Creating New Test Helpers
|
|
|
|
New domain-specific test helpers (e.g., `AssetHelper`, `JobHelper`) should be
|
|
registered as Playwright fixtures via `base.extend()` rather than attached as
|
|
properties on `ComfyPage`. This enables automatic setup/teardown.
|
|
|
|
### Extend `base` from Playwright
|
|
|
|
Keep each fixture self-contained by extending `@playwright/test` directly.
|
|
Compose fixtures together with `mergeTests` when a test needs multiple helpers.
|
|
|
|
```typescript
|
|
// browser_tests/fixtures/assetFixture.ts
|
|
import { test as base } from '@playwright/test'
|
|
|
|
export const test = base.extend<{
|
|
assetHelper: AssetHelper
|
|
}>({
|
|
assetHelper: async ({ page }, use) => {
|
|
const helper = new AssetHelper(page)
|
|
await helper.setup()
|
|
await use(helper)
|
|
await helper.cleanup() // automatic teardown
|
|
}
|
|
})
|
|
```
|
|
|
|
### Rules
|
|
|
|
- **Do NOT** add new helpers as properties on `ComfyPage`
|
|
- Each fixture gets automatic cleanup via the callback after `use()`
|
|
- Keep fixtures modular — extend `@playwright/test` base, not
|
|
`comfyPageFixture`, so they can be composed via `mergeTests`
|
|
|
|
## Custom Assertions
|
|
|
|
Add assertion methods directly on the page object or helper class instead of extending `comfyExpect`. Page object methods are discoverable via IntelliSense without special imports.
|
|
|
|
```typescript
|
|
// ✅ Page object assertions
|
|
await node.expectPinned()
|
|
await node.expectBypassed()
|
|
|
|
// ❌ Do not add custom matchers to comfyExpect
|
|
```
|
|
|
|
## Test Tags
|
|
|
|
- `@mobile` — Mobile viewport tests
|
|
- `@2x` — High DPI tests
|
|
|
|
## Test Data
|
|
|
|
- Check `browser_tests/assets/` for fixtures
|
|
- Use realistic ComfyUI workflows
|
|
- When multiple nodes share the same title, use `vueNodes.getNodeByTitle(name).nth(n)` — Playwright strict mode will fail on ambiguous locators
|
|
|
|
## Fixture Data & Schemas
|
|
|
|
When creating test fixture data, import or reference existing Zod schemas and TypeScript
|
|
types from `src/` instead of inventing ad-hoc shapes. This keeps test data in sync with
|
|
production types.
|
|
|
|
Key schema locations:
|
|
|
|
- `src/schemas/apiSchema.ts` — API response types (`PromptResponse`, `SystemStats`, `User`, `UserDataFullInfo`, WebSocket messages)
|
|
- `src/schemas/nodeDefSchema.ts` — Node definition schema (`ComfyNodeDef`, `InputSpec`, `ComboInputSpec`)
|
|
- `src/schemas/nodeDef/nodeDefSchemaV2.ts` — V2 node definition schema
|
|
- `src/platform/remote/comfyui/jobs/jobTypes.ts` — Jobs API Zod schemas (`zJobDetail`, `zJobsListResponse`, `zRawJobListItem`)
|
|
- `src/platform/workflow/validation/schemas/workflowSchema.ts` — Workflow validation (`ComfyWorkflowJSON`, `ComfyApiWorkflow`)
|
|
- `src/types/metadataTypes.ts` — Asset metadata types
|
|
|
|
## Typed API Mocks
|
|
|
|
When mocking API responses with `route.fulfill()`, **always** type the response body
|
|
using existing schemas or generated types — never use untyped inline JSON objects.
|
|
This catches shape mismatches at compile time instead of through flaky runtime failures.
|
|
|
|
All three generated-type packages (`ingest-types`, `registry-types`, `generatedManagerTypes`)
|
|
are auto-generated from their respective OpenAPI specs. Prefer these as the single
|
|
source of truth for any mock that targets their endpoints.
|
|
|
|
### Sources of truth
|
|
|
|
| Endpoint category | Type source |
|
|
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
| Cloud-only (hub, billing, workflows) | `@comfyorg/ingest-types` (`packages/ingest-types`, auto-generated from OpenAPI) |
|
|
| Registry (releases, nodes, publishers) | `@comfyorg/registry-types` (`packages/registry-types`, auto-generated from OpenAPI) |
|
|
| Manager (queue tasks, packages) | `generatedManagerTypes.ts` (`src/workbench/extensions/manager/types/`, auto-generated from OpenAPI) |
|
|
| Python backend (queue, history, settings, features) | Manual Zod schemas in `src/schemas/apiSchema.ts` |
|
|
| Node definitions | `src/schemas/nodeDefSchema.ts` |
|
|
| Templates | `src/platform/workflow/templates/types/template.ts` |
|
|
|
|
### Patterns
|
|
|
|
```typescript
|
|
// ✅ Import the type and annotate mock data
|
|
import type { ReleaseNote } from '@/platform/updates/common/releaseService'
|
|
|
|
const mockRelease: ReleaseNote = {
|
|
id: 1,
|
|
project: 'comfyui',
|
|
version: 'v0.3.44',
|
|
attention: 'medium',
|
|
content: '## New Features',
|
|
published_at: new Date().toISOString()
|
|
}
|
|
body: JSON.stringify([mockRelease])
|
|
|
|
// ❌ Untyped inline JSON — schema drift goes unnoticed
|
|
body: JSON.stringify([{ id: 1, project: 'comfyui', version: 'v0.3.44', ... }])
|
|
```
|
|
|
|
## Running Tests
|
|
|
|
```bash
|
|
pnpm test:browser:local # Run all E2E tests
|
|
pnpm test:browser:local -- --ui # Interactive UI mode
|
|
```
|