Files
ComfyUI_frontend/browser_tests/tests/load3d/Load3DHelper.ts
Kelly Yang d07599024a test: add E2E tests for Load3D config persistence, settings integration, and splat/PLY model loading (levels 10-12)
Add three new E2E spec files covering the 3D node test plan:
- load3dConfiguration.spec.ts: scene/camera/model config persists through
  workflow save and reload
- load3dSettings.spec.ts: Comfy.Load3D.* settings applied to newly created
  nodes; 3DViewerEnable controls button visibility
- load3dSplatPLY.spec.ts: PLY model loads with threejs engine; splat model
  hides Light and Export categories in the controls menu

Also extend Load3DHelper with menu interaction methods (selectMenuCategory,
clickGridToggle, switchCameraType, selectMaterialMode) and export
getNodeConfig<T> for reading node properties in tests. Add
load3dVueEnabledTest fixture and minimal test assets (test_model.ply,
test_model.splat).
2026-04-17 12:14:23 -07:00

88 lines
2.3 KiB
TypeScript

import { expect } from '@playwright/test'
import type { Locator, Page } from '@playwright/test'
import { TestIds } from '@e2e/fixtures/selectors'
export class Load3DHelper {
constructor(readonly node: Locator) {}
get canvas(): Locator {
return this.node.locator('canvas')
}
get menuButton(): Locator {
return this.node.getByRole('button', { name: /show menu/i })
}
get recordingButton(): Locator {
return this.node.getByRole('button', { name: /start recording/i })
}
get colorInput(): Locator {
return this.node.locator('input[type="color"]')
}
get openViewerButton(): Locator {
return this.node.getByRole('button', { name: /open in 3d viewer/i })
}
getUploadButton(label: string): Locator {
return this.node.getByText(label)
}
getMenuCategory(name: string): Locator {
return this.node.getByText(name, { exact: true })
}
async openMenu(): Promise<void> {
await this.menuButton.click()
}
async selectMenuCategory(name: string): Promise<void> {
await this.getMenuCategory(name).click()
}
async clickGridToggle(): Promise<void> {
await this.node.getByRole('button', { name: /show grid/i }).click()
}
async switchCameraType(): Promise<void> {
await this.node.getByRole('button', { name: /switch camera/i }).click()
}
async selectMaterialMode(mode: string): Promise<void> {
await this.node.getByRole('button', { name: /material mode/i }).click()
await this.node.getByRole('button', { name: mode, exact: true }).click()
}
async setBackgroundColor(hex: string): Promise<void> {
await this.colorInput.evaluate((el, value) => {
;(el as HTMLInputElement).value = value
el.dispatchEvent(new Event('input', { bubbles: true }))
}, hex)
}
async waitForModelLoaded(): Promise<void> {
await expect(this.node.getByTestId(TestIds.loading.overlay)).toBeHidden({
timeout: 30000
})
}
}
export async function getNodeConfig<T>(
page: Page,
nodeId: string,
configKey: string
): Promise<T | null> {
return page.evaluate(
({ id, key }) => {
const node = window.app!.graph.getNodeById(Number(id))
if (!node?.properties) return null
return (
((node.properties as Record<string, unknown>)[key] as T | null) ?? null
)
},
{ id: nodeId, key: configKey }
)
}