mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
## Summary Add eslint-plugin-playwright as an oxlint JS plugin scoped to browser_tests/, enforcing Playwright best practices at lint time. ## Changes - **What**: Configure eslint-plugin-playwright@2.10.1 via oxlint's alpha `jsPlugins` field (`.oxlintrc.json` override scoped to `browser_tests/**/*.ts`). 18 recommended rules + `prefer-native-locators` + `require-to-pass-timeout` at error severity. All 173 initial violations resolved (config, auto-fix, manual fixes). `no-force-option` set to off — 28 violations need triage (canvas overlay workarounds vs unnecessary force) in a dedicated PR. - **Dependencies**: `eslint-plugin-playwright@^2.10.1` (devDependency, required by oxlint jsPlugins at runtime) ## Review Focus - `.oxlintrc.json` override structure — this is the first use of oxlint's JS plugins alpha feature in this repo - Manual fixes in spec files: `waitForSelector` → `locator.waitFor`, deprecated page methods → locator equivalents, `toPass()` timeout additions - Compound CSS selectors replaced with `.and()` (Playwright native locator composition) to avoid `prefer-native-locators` suppressions - Lint script changes in `package.json` to include `browser_tests/` in oxlint targets --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: GitHub Action <action@github.com>
191 lines
6.6 KiB
TypeScript
191 lines
6.6 KiB
TypeScript
import {
|
|
comfyExpect as expect,
|
|
comfyPageFixture as test
|
|
} from '@e2e/fixtures/ComfyPage'
|
|
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
|
|
import type { Position } from '@e2e/fixtures/types'
|
|
|
|
test.describe('Vue Node Moving', () => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', true)
|
|
await comfyPage.vueNodes.waitForNodes()
|
|
})
|
|
|
|
const getHeaderPos = async (
|
|
comfyPage: ComfyPage,
|
|
title: string
|
|
): Promise<{ x: number; y: number; width: number; height: number }> => {
|
|
const box = await comfyPage.vueNodes
|
|
.getNodeByTitle(title)
|
|
.getByTestId('node-title')
|
|
.first()
|
|
.boundingBox()
|
|
if (!box) throw new Error(`${title} header not found`)
|
|
return box
|
|
}
|
|
|
|
const getLoadCheckpointHeaderPos = async (comfyPage: ComfyPage) =>
|
|
getHeaderPos(comfyPage, 'Load Checkpoint')
|
|
|
|
const expectPosChanged = async (pos1: Position, pos2: Position) => {
|
|
const diffX = Math.abs(pos2.x - pos1.x)
|
|
const diffY = Math.abs(pos2.y - pos1.y)
|
|
expect(diffX).toBeGreaterThan(0)
|
|
expect(diffY).toBeGreaterThan(0)
|
|
}
|
|
|
|
const deltaBetween = (before: Position, after: Position) => ({
|
|
x: after.x - before.x,
|
|
y: after.y - before.y
|
|
})
|
|
|
|
const expectSameDelta = (a: Position, b: Position, tol = 2) => {
|
|
expect(Math.abs(a.x - b.x)).toBeLessThanOrEqual(tol)
|
|
expect(Math.abs(a.y - b.y)).toBeLessThanOrEqual(tol)
|
|
}
|
|
|
|
test('should allow moving nodes by dragging', async ({ comfyPage }) => {
|
|
const loadCheckpointHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
|
await comfyPage.canvasOps.dragAndDrop(loadCheckpointHeaderPos, {
|
|
x: 256,
|
|
y: 256
|
|
})
|
|
|
|
const newHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
|
await expectPosChanged(loadCheckpointHeaderPos, newHeaderPos)
|
|
})
|
|
|
|
test('should not move node when pointer moves less than drag threshold', async ({
|
|
comfyPage
|
|
}) => {
|
|
const headerPos = await getLoadCheckpointHeaderPos(comfyPage)
|
|
|
|
// Move only 2px — below the 3px drag threshold in useNodePointerInteractions
|
|
await comfyPage.page.mouse.move(headerPos.x, headerPos.y)
|
|
await comfyPage.page.mouse.down()
|
|
await comfyPage.page.mouse.move(headerPos.x + 2, headerPos.y + 1, {
|
|
steps: 5
|
|
})
|
|
await comfyPage.page.mouse.up()
|
|
await comfyPage.nextFrame()
|
|
|
|
const afterPos = await getLoadCheckpointHeaderPos(comfyPage)
|
|
expect(afterPos.x).toBeCloseTo(headerPos.x, 0)
|
|
expect(afterPos.y).toBeCloseTo(headerPos.y, 0)
|
|
|
|
// The small movement should have selected the node, not dragged it
|
|
await expect(comfyPage.vueNodes.selectedNodes).toHaveCount(1)
|
|
})
|
|
|
|
test('should move node when pointer moves beyond drag threshold', async ({
|
|
comfyPage
|
|
}) => {
|
|
const headerPos = await getLoadCheckpointHeaderPos(comfyPage)
|
|
|
|
// Move 50px — well beyond the 3px drag threshold
|
|
await comfyPage.page.mouse.move(headerPos.x, headerPos.y)
|
|
await comfyPage.page.mouse.down()
|
|
await comfyPage.page.mouse.move(headerPos.x + 50, headerPos.y + 50, {
|
|
steps: 20
|
|
})
|
|
await comfyPage.page.mouse.up()
|
|
await comfyPage.nextFrame()
|
|
|
|
const afterPos = await getLoadCheckpointHeaderPos(comfyPage)
|
|
await expectPosChanged(headerPos, afterPos)
|
|
})
|
|
|
|
test('should move all selected nodes together when dragging one with Meta held', async ({
|
|
comfyPage
|
|
}) => {
|
|
const checkpointBefore = await getHeaderPos(comfyPage, 'Load Checkpoint')
|
|
const ksamplerBefore = await getHeaderPos(comfyPage, 'KSampler')
|
|
const latentBefore = await getHeaderPos(comfyPage, 'Empty Latent Image')
|
|
|
|
const dx = 120
|
|
const dy = 80
|
|
|
|
const clickNodeTitleWithMeta = async (title: string) => {
|
|
await comfyPage.vueNodes
|
|
.getNodeByTitle(title)
|
|
.getByTestId('node-title')
|
|
.first()
|
|
.click({ modifiers: ['Meta'] })
|
|
}
|
|
|
|
await comfyPage.page.keyboard.down('Meta')
|
|
try {
|
|
await clickNodeTitleWithMeta('Load Checkpoint')
|
|
await clickNodeTitleWithMeta('KSampler')
|
|
await clickNodeTitleWithMeta('Empty Latent Image')
|
|
await expect(comfyPage.vueNodes.selectedNodes).toHaveCount(3)
|
|
|
|
// Re-fetch drag source after clicks in case the header reflowed.
|
|
const dragSrc = await getHeaderPos(comfyPage, 'Load Checkpoint')
|
|
const centerX = dragSrc.x + dragSrc.width / 2
|
|
const centerY = dragSrc.y + dragSrc.height / 2
|
|
|
|
await comfyPage.page.mouse.move(centerX, centerY)
|
|
await comfyPage.page.mouse.down()
|
|
await comfyPage.nextFrame()
|
|
await comfyPage.page.mouse.move(centerX + dx, centerY + dy, {
|
|
steps: 20
|
|
})
|
|
await comfyPage.page.mouse.up()
|
|
await comfyPage.nextFrame()
|
|
} finally {
|
|
await comfyPage.page.keyboard.up('Meta')
|
|
await comfyPage.nextFrame()
|
|
}
|
|
|
|
await expect(comfyPage.vueNodes.selectedNodes).toHaveCount(3)
|
|
|
|
const checkpointAfter = await getHeaderPos(comfyPage, 'Load Checkpoint')
|
|
const ksamplerAfter = await getHeaderPos(comfyPage, 'KSampler')
|
|
const latentAfter = await getHeaderPos(comfyPage, 'Empty Latent Image')
|
|
|
|
// All three nodes should have moved together by the same delta.
|
|
// We don't assert the exact screen delta equals the dragged pixel delta,
|
|
// because canvas scaling and snap-to-grid can introduce offsets.
|
|
const checkpointDelta = deltaBetween(checkpointBefore, checkpointAfter)
|
|
const ksamplerDelta = deltaBetween(ksamplerBefore, ksamplerAfter)
|
|
const latentDelta = deltaBetween(latentBefore, latentAfter)
|
|
|
|
// Confirm an actual drag happened (not zero movement).
|
|
expect(Math.abs(checkpointDelta.x)).toBeGreaterThan(10)
|
|
expect(Math.abs(checkpointDelta.y)).toBeGreaterThan(10)
|
|
|
|
// Confirm all selected nodes moved by the same delta.
|
|
expectSameDelta(checkpointDelta, ksamplerDelta)
|
|
expectSameDelta(checkpointDelta, latentDelta)
|
|
|
|
await comfyPage.canvasOps.moveMouseToEmptyArea()
|
|
})
|
|
|
|
test(
|
|
'@mobile should allow moving nodes by dragging on touch devices',
|
|
{ tag: '@screenshot' },
|
|
async ({ comfyPage }) => {
|
|
// Disable minimap (gets in way of the node on small screens)
|
|
await comfyPage.settings.setSetting('Comfy.Minimap.Visible', false)
|
|
|
|
const loadCheckpointHeaderPos =
|
|
await getLoadCheckpointHeaderPos(comfyPage)
|
|
await comfyPage.canvasOps.panWithTouch(
|
|
{
|
|
x: 64,
|
|
y: 64
|
|
},
|
|
loadCheckpointHeaderPos
|
|
)
|
|
|
|
const newHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
|
await expectPosChanged(loadCheckpointHeaderPos, newHeaderPos)
|
|
|
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
'vue-node-moved-node-touch.png'
|
|
)
|
|
}
|
|
)
|
|
})
|