The real-host config waited on the /system_stats URL, which any server on
the port answers. When two runs started on 8189 within a few seconds of
each other, the second run's ComfyUI failed to bind ("Port 8189 is already
in use"), but Playwright had already seen the first run's host answer and
ran the second run's tests against it. When the first run finished and
stopped its host, the second run failed mid-test with ECONNREFUSED,
ECONNRESET or ERR_CONNECTION_REFUSED, and passed when rerun alone.
The launcher now refuses a port that is in use, and reports ready only
when /system_stats names its own base directory. The config waits for
that line instead of the URL, and every test checks again that the host
answering the port is the one its run started.
53 lines
3.2 KiB
JavaScript
53 lines
3.2 KiB
JavaScript
import { expect } from '../../tools/vendor/node_modules/@playwright/test/index.mjs';
|
|
import runtime from '../../tools/vendor/comfyui-runtime.json' with { type: 'json' };
|
|
import { hostBaseDirectory } from '../../tools/vendor/comfyui-host.mjs';
|
|
|
|
export function installHostHooks(test, expectedTypes = ['PromptManager', 'PromptManagerText', 'PromptSearchList']) {
|
|
test.beforeEach(async ({ page, context, request }, testInfo) => {
|
|
const response = await request.get('/system_stats');
|
|
expect(response.ok()).toBe(true);
|
|
const stats = await response.json();
|
|
// comfyui.config.mjs's launcher reports the host it owns. The execution suite has no
|
|
// web server: it starts its own host per worker behind a port refusal.
|
|
if (testInfo.config.webServer) {
|
|
const ownedBase = process.env.PM_COMFY_HOST_BASE;
|
|
expect(ownedBase, 'the launcher reported the host it started').toBeTruthy();
|
|
expect(hostBaseDirectory(stats), 'the host answering the port is the one this run started').toBe(ownedBase);
|
|
}
|
|
const info = stats.system;
|
|
expect(info.comfyui_version).toBe(runtime.serverVersion);
|
|
expect(info.pytorch_version).toBe(`${runtime.torchVersion}+cpu`);
|
|
expect(info.required_frontend_version).toBe(runtime.frontendVersion);
|
|
expect(info.comfy_package_versions.find(item => item.name === 'comfyui-frontend-package')?.installed).toBe(runtime.frontendVersion);
|
|
await testInfo.attach('host-versions.json', { body: JSON.stringify(info, null, 2), contentType: 'application/json' });
|
|
page.hostErrors = [];
|
|
page.on('pageerror', error => page.hostErrors.push(error.message));
|
|
await context.route('**/*', async route => {
|
|
const request = route.request();
|
|
const path = new URL(request.url()).pathname.replace(/^\/api\//, '/');
|
|
if (path === '/settings' && request.method() === 'GET') {
|
|
const response = await route.fetch();
|
|
return route.fulfill({ json: { ...await response.json(),
|
|
'Comfy.VueNodes.Enabled': testInfo.project.metadata.initialRenderer,
|
|
'Comfy.TutorialCompleted': true, 'Comfy.Workflow.Persist': false,
|
|
'Comfy.Notification.ShowVersionUpdates': false } });
|
|
}
|
|
if (!['GET', 'HEAD', 'OPTIONS'].includes(request.method()) && !path.startsWith('/prompt_manager/')) return route.fulfill({ json: {} });
|
|
return route.continue();
|
|
});
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
|
await expect(page.getByRole('status', { name: 'Loading ComfyUI' })).toBeHidden({ timeout: 30000 });
|
|
await page.waitForFunction(async expectedTypes => {
|
|
const { app } = await import('/scripts/app.js');
|
|
return !!app.graph && expectedTypes.every(name => globalThis.LiteGraph?.registered_node_types?.[name]);
|
|
}, expectedTypes);
|
|
await expect.poll(() => page.evaluate(() => LiteGraph.vueNodesMode)).toBe(testInfo.project.metadata.initialRenderer);
|
|
await page.evaluate(async () => { const { app } = await import('/scripts/app.js'); app.graph.clear(); });
|
|
});
|
|
|
|
test.afterEach(async ({ page }) => {
|
|
expect(page.hostErrors).toEqual([]);
|
|
});
|
|
|
|
}
|