最新の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 の起動オプションにホワイトリストを追加しテスト実行速度を高速化
152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
import pytest
|
|
import time
|
|
import json
|
|
import requests
|
|
import sys
|
|
import os
|
|
from playwright.sync_api import Page, expect
|
|
|
|
# プロジェクトルートをパスに追加して __init__.py の関数をインポート可能にする
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
from __init__ import calculate_weights_from_prompt
|
|
|
|
def get_prompt_json(page: Page):
|
|
"""ブラウザ上の現在のグラフからプロンプトJSONを取得する"""
|
|
return page.evaluate('''async () => {
|
|
const p = await app.graphToPrompt();
|
|
return p.output;
|
|
}''')
|
|
|
|
def test_balanced_weights_hell_nesting_e2e(page: Page, comfyui_server: str, wait_for_comfyui):
|
|
"""
|
|
真に複雑な「地獄のネスト」をUIで構築し、重み計算ロジックを統合検証する。
|
|
構造:
|
|
Main (BalancedChoice)
|
|
- text_0: StringsToList (中間ノード)
|
|
- text_0: RandomChoice [Leaf1, Leaf2] -> 計2
|
|
- text_1: BalancedChoice [Leaf3, Leaf4, Leaf5] -> 計3
|
|
- 合計: 5
|
|
- text_1: BalancedChoiceList (text_list接続、Leaf6相当) -> 計1
|
|
- text_2: String (Leaf7) -> 計1
|
|
理論値: text_0: 5, text_1: 1, text_2: 1
|
|
"""
|
|
page.goto(comfyui_server)
|
|
wait_for_comfyui(page)
|
|
|
|
page.evaluate('''() => {
|
|
app.graph.clear();
|
|
|
|
const createNode = (type, title = null) => {
|
|
const node = LiteGraph.createNode(type);
|
|
app.graph.add(node);
|
|
if (title) node.title = title;
|
|
return node;
|
|
};
|
|
|
|
const connect = (fromNode, fromSlot, toNode, toSlot) => {
|
|
fromNode.connect(fromSlot, toNode, toSlot);
|
|
if (toNode.onConnectionsChange) {
|
|
// slotIndex は数値、isConnected は bool
|
|
toNode.onConnectionsChange(1, toSlot, true, null, toNode.inputs[toSlot]);
|
|
}
|
|
};
|
|
|
|
// --- Branch 0 (Weight 5) ---
|
|
const toList = createNode("StringToolsStringsToList", "IntermediateList");
|
|
|
|
const rc12 = createNode("StringToolsRandomChoice", "RC_12");
|
|
const seedRC = createNode("StringToolsSeed");
|
|
seedRC.widgets[0].value = 123;
|
|
connect(seedRC, 0, rc12, 0);
|
|
|
|
const l1 = createNode("StringToolsString", "L1");
|
|
const l2 = createNode("StringToolsString", "L2");
|
|
connect(l1, 0, rc12, 1);
|
|
connect(l2, 0, rc12, 2);
|
|
|
|
const bc345 = createNode("StringToolsBalancedChoice", "BC_345");
|
|
const seedBC = createNode("StringToolsSeed");
|
|
seedBC.widgets[0].value = 456;
|
|
connect(seedBC, 0, bc345, 0);
|
|
|
|
const l3 = createNode("StringToolsString", "L3");
|
|
const l4 = createNode("StringToolsString", "L4");
|
|
const l5 = createNode("StringToolsString", "L5");
|
|
connect(l3, 0, bc345, 1);
|
|
connect(l4, 0, bc345, 2);
|
|
connect(l5, 0, bc345, 3);
|
|
|
|
// StringsToList への接続 (text_0, text_1)
|
|
connect(rc12, 0, toList, 0);
|
|
connect(bc345, 0, toList, 1);
|
|
window.bc345Id = bc345.id;
|
|
|
|
// --- Branch 1 (Weight 1) ---
|
|
const bcList = createNode("StringToolsBalancedChoiceList", "BC_List_Branch");
|
|
window.bcListId = bcList.id;
|
|
const seedList = createNode("StringToolsSeed");
|
|
seedList.widgets[0].value = 789;
|
|
connect(seedList, 0, bcList, 0);
|
|
|
|
const l6 = createNode("StringToolsString", "L6");
|
|
// BalancedChoiceList は text_list ポートを持つ (slot 1)
|
|
connect(l6, 0, bcList, 1);
|
|
|
|
// --- Branch 2 (Weight 1) ---
|
|
const l7 = createNode("StringToolsString", "L7");
|
|
|
|
// --- Main Choice ---
|
|
const main = createNode("StringToolsBalancedChoice", "MainChoice");
|
|
const seedMain = createNode("StringToolsSeed");
|
|
seedMain.widgets[0].value = 999;
|
|
connect(seedMain, 0, main, 0);
|
|
|
|
// text_0, text_1, text_2 に接続
|
|
connect(toList, 0, main, 1);
|
|
connect(bcList, 0, main, 2);
|
|
connect(l7, 0, main, 3);
|
|
|
|
// Preview
|
|
const preview = createNode("PreviewAny");
|
|
connect(main, 0, preview, 0);
|
|
|
|
window.mainNodeId = main.id;
|
|
app.graph.setDirtyCanvas(true, true);
|
|
}''')
|
|
|
|
# UIが生成したプロンプトJSONを抽出
|
|
prompt_json = get_prompt_json(page)
|
|
main_node_id = str(page.evaluate("window.mainNodeId"))
|
|
bc345_node_id = str(page.evaluate("window.bc345Id"))
|
|
bclist_node_id = str(page.evaluate("window.bcListId"))
|
|
|
|
# サーバー側のロジックにこのJSONを流し込む
|
|
classes = ["StringToolsRandomChoice", "StringToolsBalancedChoice", "StringToolsRandomChoiceList", "StringToolsBalancedChoiceList"]
|
|
|
|
# Mainノードの検証
|
|
weights_main = calculate_weights_from_prompt(prompt_json, main_node_id, classes, "text")
|
|
print(f"Main Weights: {weights_main}")
|
|
assert weights_main == {"text_0": 5, "text_1": 1, "text_2": 1}
|
|
|
|
# 中間ノード (BC_345) の検証
|
|
weights_bc345 = calculate_weights_from_prompt(prompt_json, bc345_node_id, classes, "text")
|
|
print(f"BC_345 Weights: {weights_bc345}")
|
|
assert weights_bc345 == {"text_0": 1, "text_1": 1, "text_2": 1}
|
|
|
|
# 中間ノード (BC_List_Branch) の検証
|
|
weights_bclist = calculate_weights_from_prompt(prompt_json, bclist_node_id, classes, "text")
|
|
print(f"BC_List Weights: {weights_bclist}")
|
|
assert weights_bclist == {"text_list": 1}
|
|
|
|
# 実際に応答が返ることも確認
|
|
resp = requests.post(
|
|
f"{comfyui_server}/prompt",
|
|
json={
|
|
"client_id": "test_client",
|
|
"prompt": prompt_json
|
|
},
|
|
timeout=10
|
|
)
|
|
assert resp.status_code == 200
|
|
print("E2E Hell Nesting Test Passed.")
|