Files
Taremin c8647cab54 fix: ComfyUIの仕様変更に伴うリスト型ノードの出力バグの修正
最新のComfyUIにおける実行エンジンのリスト自動アンラップ廃止に伴い、リスト型ノードの出力データが下流ノードで二重にラップされて正常に結合できなくなる不具合を解消するため、およびE2Eテスト環境の不整合を修正

- StringToolsRandomChoiceList、StringToolsBalancedChoiceList、StringToolsConcatList の process 戻り値から不要なリストブラケットを削除
- test_string_tools_complex_interconnection において seed と text_list の接続ポートが逆になっていた不具合を修正
- テスト内モックノード StringToolsMockStringList をテスト実行環境(COMFYUI_TEST_MODE または pytest 稼働中)のみ動的に登録するように制御
- モックノード誤露出時のフェイルセーフとして NODE_DISPLAY_NAME_MAPPINGS にテスト用である旨の表示名を設定
- conftest.py の起動オプションにホワイトリストを追加しテスト実行速度を高速化
2026-07-04 09:08:02 +09:00

176 lines
6.2 KiB
Python

import pytest
import time
import json
import urllib.request
from playwright.sync_api import Page, expect
def get_latest_history(base_url: str):
"""ComfyUIのAPIから最新の実行履歴を取得する"""
req = urllib.request.Request(f"{base_url}/history", method="GET")
try:
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode('utf-8'))
if not data:
return None
latest_id = list(data.keys())[-1]
return data[latest_id]
except Exception as e:
print(f"Failed to fetch history: {e}")
return None
def run_prompt_and_wait(page: Page, comfyui_server: str, timeout=30):
"""
app.graphToPrompt() を取得し、requests で POST して実行完了を待機する。
"""
import requests
# graphToPrompt を取得
prompt_data = page.evaluate('''async () => {
const p = await app.graphToPrompt();
return { output: p.output, workflow: p.workflow };
}''')
# POST
resp = requests.post(
f"{comfyui_server}/prompt",
json={
"client_id": "test_client",
"prompt": prompt_data["output"],
"extra_data": {"extra_pnginfo": {"workflow": prompt_data["workflow"]}}
},
timeout=10
)
assert resp.status_code == 200, f"Prompt failed: {resp.text}"
prompt_id = resp.json().get("prompt_id")
assert prompt_id, "No prompt_id returned"
# 完了待機
start_time = time.time()
while time.time() - start_time < timeout:
try:
resp = requests.get(f"{comfyui_server}/history", timeout=5).json()
if prompt_id in resp:
return resp[prompt_id]
except: pass
time.sleep(0.5)
raise TimeoutError("Prompt execution timed out")
def test_concat_no_separator_success(page: Page, comfyui_server: str, wait_for_comfyui):
"""
StringToolsConcat ノードの separator が未接続の場合、エラーにならずに実行でき、空文字で結合されることを確認。
"""
page.goto(comfyui_server)
wait_for_comfyui(page)
page.evaluate('''() => {
app.graph.clear();
const strA = LiteGraph.createNode("StringToolsString");
app.graph.add(strA);
strA.widgets[0].value = "Hello";
const strB = LiteGraph.createNode("StringToolsString");
app.graph.add(strB);
strB.widgets[0].value = "World";
const concat = LiteGraph.createNode("StringToolsConcat");
app.graph.add(concat);
// separator(Slot 0) は接続しない
strA.connect(0, concat, 1);
if (concat.onConnectionsChange) {
concat.onConnectionsChange(1, 1, true, null, concat.inputs[1]);
}
strB.connect(0, concat, 2);
if (concat.onConnectionsChange) {
concat.onConnectionsChange(1, 2, true, null, concat.inputs[2]);
}
let preview = LiteGraph.createNode("PreviewAny");
app.graph.add(preview);
concat.connect(0, preview, 0);
window.concatNodeId = concat.id;
window.tgtNodeId = preview.id;
}''')
history = run_prompt_and_wait(page, comfyui_server)
assert history is not None
tgt_id = page.evaluate("window.tgtNodeId")
outputs = history.get("outputs", {})
assert str(tgt_id) in outputs
output = outputs[str(tgt_id)]
# PreviewAny の出力形式に合わせて text を取得
out_str = str(output.get("text", output[0] if isinstance(output, list) else output))
print(f"Concat No Separator Output: {out_str}")
# デフォルトは空文字なので HelloWorld になるはず
assert "HelloWorld" in out_str
def test_concat_list_no_separator_success(page: Page, comfyui_server: str, wait_for_comfyui):
"""
StringToolsConcatList ノードの separator が未接続の場合、エラーにならずに実行でき、改行で結合されることを確認。
"""
page.goto(comfyui_server)
wait_for_comfyui(page)
page.evaluate('''() => {
if (!LiteGraph.getNodeType("StringToolsMockStringList")) {
class MockStringList extends LiteGraph.LGraphNode {
constructor() {
super();
this.addInput("str1", "STRING");
this.addInput("str2", "STRING");
this.addOutput("list", "STRING");
this.comfyClass = "StringToolsMockStringList";
}
}
MockStringList.title = "MockStringList";
MockStringList.comfyClass = "StringToolsMockStringList";
LiteGraph.registerNodeType("StringToolsMockStringList", MockStringList);
}
app.graph.clear();
const s1 = LiteGraph.createNode("StringToolsString");
app.graph.add(s1);
s1.widgets[0].value = "ABC";
const s2 = LiteGraph.createNode("StringToolsString");
app.graph.add(s2);
s2.widgets[0].value = "DEF";
const mock = LiteGraph.createNode("StringToolsMockStringList");
app.graph.add(mock);
s1.connect(0, mock, 0);
s2.connect(0, mock, 1);
const concatList = LiteGraph.createNode("StringToolsConcatList");
app.graph.add(concatList);
mock.connect(0, concatList, 0); // text_list
// separator(Slot 1) は接続しない
const preview = LiteGraph.createNode("PreviewAny");
app.graph.add(preview);
concatList.connect(0, preview, 0);
window.tgtNodeId = preview.id;
}''')
history = run_prompt_and_wait(page, comfyui_server)
assert history is not None
tgt_id = page.evaluate("window.tgtNodeId")
outputs = history.get("outputs", {})
assert str(tgt_id) in outputs
output = outputs[str(tgt_id)]
out_str = str(output.get("text", output[0] if isinstance(output, list) else output))
print(f"ConcatList No Separator Output: {out_str}")
# デフォルトは改行なので ABC\nDEF になるはず
assert "ABC" in out_str and "DEF" in out_str
# ABCDEF ではなく改行を挟んでいるか
assert "ABC" in out_str and "DEF" in out_str and "ABCDEF" not in out_str