Files
Taremin-webui-monaco-prompt/tests/e2e/multitext/test_multitext_search_highlight.py
Taremin f84fea75b6 refactor: テストディレクトリの構成整理とプリセット機能のモジュール化
- テストファイルを e2e, unit, debug などのカテゴリ別ディレクトリに移動し、構成を整理
- 不要な一時デバッグ用スクリプトを削除し、テスト環境をクリーンアップ
- 言語機能やプリセット定義を src/languages/ ディレクトリに分離・モジュール化
- 言語プリセット管理ダイアログ (preset_dialog.ts) の追加と関連スタイルの更新
- __init__.py での MultiText ノード入力値パース処理を堅牢化 (リスト入力や不正JSONへの対応)
- .gitignore に .agents および debug_dumps の除外設定を追加
2026-03-18 19:37:00 +09:00

157 lines
7.1 KiB
Python

import pytest
import time
from playwright.sync_api import Page, expect
def setup_console_log(page: Page):
page.on("console", lambda msg: print(f"BROWSER CONSOLE: {msg.type}: {msg.text}"))
page.on("pageerror", lambda err: print(f"BROWSER ERROR: {err}"))
def add_node_api_force(page: Page, wmp_helpers):
"""LiteGraphにノードが登録されるのを待ってから、APIを使用してMultiTextノードを直接追加する"""
wmp_helpers.wait_for_graph_clear(page)
# Wait for node type to be registered
page.wait_for_function("""
() => {
const lg = window.LiteGraph;
return lg && lg.registered_node_types && Object.keys(lg.registered_node_types).some(k => k.includes("MultiText"));
}
""", timeout=30000)
type_name = page.evaluate("""
() => Object.keys(window.LiteGraph.registered_node_types).find(k => k.includes("MultiText"))
""")
wmp_helpers.create_node(page, type_name, [300, 300])
# multitext_widget が生成されるまで待機
page.wait_for_function("""
() => {
const getApp = () => window.app || (window.comfyAPI && window.comfyAPI.app) || window.ComfyApp;
const app = getApp();
const node = app.graph._nodes.find(n => n.multitext_widget);
return node && node.multitext_widget && !!node.multitext_widget.editorInstance;
}
""", timeout=30000)
wmp_helpers.wait_for_ui_stabilize(page, 2000)
def test_multitext_search_highlight_visibility(page: Page, comfyui_server, wait_for_comfyui, wmp_helpers):
"""マルチテキストノード内での検索ハイライトが表示されることを確認する"""
setup_console_log(page)
wmp_helpers.load_comfyui(page, comfyui_server, wait_for_comfyui)
add_node_api_force(page, wmp_helpers)
# ファイルにテキストを入力
page.evaluate("""
() => {
const getApp = () => window.app || (window.comfyAPI && window.comfyAPI.app) || window.ComfyApp;
const app = getApp();
const node = app.graph._nodes.find(n => n.type && n.type.includes("MultiText"));
const controller = node.multitext_widget;
const firstFile = controller.data.tree.find(item => item.type === 'file');
const model = controller.models[firstFile.id];
model.setValue("hello world\\nthis is a test\\nsearching for keyword");
}
""")
wmp_helpers.wait_for_ui_stabilize(page)
# 検索バーを開く
page.locator("button[title='Search']").first.click()
page.locator("input[class*='search-input']").first.fill("keyword")
wmp_helpers.wait_for_ui_stabilize(page, 2000)
# Monaco エディタ内のハイライトを確認
highlight_exists = page.evaluate("""
() => {
const app = window.app || (window.comfyAPI && window.comfyAPI.app) || window.ComfyApp;
const node = app.graph._nodes.find(n => n.type && n.type.includes("MultiText"));
if (!node || !node.multitext_widget || !node.multitext_widget.editor) return false;
const editor = node.multitext_widget.editor.monaco;
if (!editor) return false;
// 1. Monaco のデコレーションを確認 (より確実)
const model = editor.getModel();
if (!model) return false;
// モデルから全てのデコレーションを取得
const decorations = model.getAllDecorations();
const utils = window.WebuiMonacoPrompt && window.WebuiMonacoPrompt.utils;
const targetClass = (utils && utils.getThemeClassName) ? utils.getThemeClassName() : "webui-monaco-prompt-findmatch";
// 検索ハイライトに関連するクラスを持つデコレーションを探す
const hasSearchDecoration = decorations.some(d => {
const options = d.options;
return (options.inlineClassName && options.inlineClassName.includes(targetClass)) ||
(options.className && options.className.includes("findMatch"));
});
if (hasSearchDecoration) return true;
// 2. フォールバック: DOM スキャン
const scan = (root) => {
if (!root) return false;
if (root.querySelectorAll) {
if (root.querySelectorAll(`.${targetClass}, [class*="findMatch"]`).length > 0) return true;
}
const children = Array.from(root.children || []);
for (const child of children) {
if (scan(child)) return true;
if (child.shadowRoot && scan(child.shadowRoot)) return true;
}
return false;
};
return scan(document.body);
}
""")
assert highlight_exists, "Search highlight not found in Monaco editor"
def test_multitext_preview_highlight_visibility(page: Page, comfyui_server, wait_for_comfyui, wmp_helpers):
"""検索結果のプレビューツールチップ内でもハイライトが表示されることを確認する"""
setup_console_log(page)
wmp_helpers.load_comfyui(page, comfyui_server, wait_for_comfyui)
add_node_api_force(page, wmp_helpers)
# テキスト設定
page.evaluate("""
() => {
const getApp = () => window.app || (window.comfyAPI && window.comfyAPI.app) || window.ComfyApp;
const app = getApp();
const node = app.graph._nodes.find(n => n.type && n.type.includes("MultiText"));
const controller = node.multitext_widget;
const firstFile = controller.data.tree.find(item => item.type === 'file');
const model = controller.models[firstFile.id];
model.setValue("find me in preview keywords");
}
""")
wmp_helpers.wait_for_ui_stabilize(page)
# 検索
page.locator("button[title='Search']").first.click()
page.locator("input[class*='search-input']").first.fill("preview")
wmp_helpers.wait_for_ui_stabilize(page, 2000)
# 検索結果アイテムをホバー
result_item = page.locator("[class*='multitext-search-result-item']").first
result_item.hover()
wmp_helpers.wait_for_ui_stabilize(page, 3000)
# ツールチップのハイライトを確認
preview_highlight_exists = page.evaluate("""
() => {
const utils = window.WebuiMonacoPrompt && window.WebuiMonacoPrompt.utils;
const targetClass = (utils && utils.getThemeClassName) ? utils.getThemeClassName() : "webui-monaco-prompt-findmatch";
const tooltip = document.querySelector("[class*='monaco-prompt-search-tooltip']");
if (!tooltip) return false;
const matches = tooltip.querySelectorAll(`.${targetClass}, [class*="findMatch"]`);
if (matches.length > 0) {
const style = window.getComputedStyle(matches[0]);
return style.backgroundColor !== 'rgba(0, 0, 0, 0)' && style.backgroundColor !== 'transparent';
}
return false;
}
""")
assert preview_highlight_exists, "Search highlight not found or not styled in preview tooltip"