test(ui-lab): add tagger readiness scenarios

Add UI Lab scenarios for uninstalled WD and JoyTag tagger states so the lightbox and AI Workspace readiness flows can be exercised directly.

Make the Settings title-bar button accessible by label to support reliable UI Lab automation.
This commit is contained in:
2026-07-02 20:22:06 +01:00
parent b7e82dbf91
commit d29a779c13
3 changed files with 75 additions and 5 deletions
+1
View File
@@ -147,6 +147,7 @@ export function TitleBar() {
<Tooltip label="Settings (right-click to switch theme)" anchorToCursor>
<button
ref={settingsBtnRef}
aria-label="Settings"
onClick={() => setSettingsOpen(true)}
onContextMenu={handleSettingsContextMenu}
className="group flex h-full w-10 items-center justify-center text-gray-600 transition-colors duration-100 hover:bg-white/6 hover:text-gray-300"
+61 -4
View File
@@ -1,5 +1,15 @@
import { emit } from "@tauri-apps/api/event";
import type { Album, ExploreTagEntry, Folder, ImageRecord, ImageTag, SortOrder } from "../store";
import type {
Album,
ExploreTagEntry,
Folder,
ImageRecord,
ImageTag,
SortOrder,
TaggerModel,
TaggerModelStatus,
TaggerRuntimeProbe,
} from "../store";
import { compareImages, createMockDb, mockExif } from "./mockFixtures";
import { getMockScenario } from "./mockScenarios";
@@ -7,6 +17,8 @@ const db = createMockDb(getMockScenario());
let nextFolderId = 100;
let nextAlbumId = 100;
let nextTagId = 10_000;
let mockTaggerModel: TaggerModel = db.scenario === "joytag-unready" ? "joytag" : "wd";
let mockTaggerReady = db.scenario !== "unready" && db.scenario !== "joytag-unready";
type AnyPayload = Record<string, any> | undefined;
type Rgb = [number, number, number];
@@ -55,6 +67,42 @@ function page<T>(items: T[], offset = 0, limit = 200) {
};
}
function mockTaggerStatus(): TaggerModelStatus {
const modelInfo =
mockTaggerModel === "joytag"
? {
model_id: "fancyfeast/joytag",
model_name: "JoyTag",
local_dir: "mock://models/joytag",
label_file: "top_tags.txt",
}
: {
model_id: "SmilingWolf/wd-swinv2-tagger-v3",
model_name: "wd-swinv2-tagger-v3",
local_dir: "mock://models/wd-swinv2-tagger-v3",
label_file: "selected_tags.csv",
};
return {
model_id: modelInfo.model_id,
model_name: modelInfo.model_name,
local_dir: modelInfo.local_dir,
ready: mockTaggerReady,
missing_files: mockTaggerReady ? [] : ["model.onnx", modelInfo.label_file],
};
}
function mockTaggerRuntimeProbe(): TaggerRuntimeProbe {
if (!mockTaggerReady) {
throw new Error(`${mockTaggerStatus().model_name} is missing required model files`);
}
return {
ready: true,
acceleration: "auto",
session: { file: "model.onnx", inputs: ["input"], outputs: ["tags"] },
};
}
function filterImages(input: ImageRecord[], payload: unknown): ImageRecord[] {
const p = params(payload);
const query = String(p.search ?? p.query ?? "").trim().toLowerCase();
@@ -463,10 +511,19 @@ export async function handleMockCommand(cmd: string, payload?: unknown): Promise
return reset;
}
case "get_tagger_model_status":
return { model_id: "SmilingWolf/wd-vit-tagger-v3", model_name: "WD ViT Tagger", local_dir: "mock://models/tagger", ready: true, missing_files: [] };
return mockTaggerStatus();
case "get_tagger_model":
return mockTaggerModel;
case "set_tagger_model":
return p.model ?? "wd";
mockTaggerModel = p.model ?? "wd";
mockTaggerReady = db.scenario !== "unready" && db.scenario !== "joytag-unready";
return mockTaggerModel;
case "prepare_tagger_model":
mockTaggerReady = true;
return mockTaggerStatus();
case "delete_tagger_model":
mockTaggerReady = false;
return mockTaggerStatus();
case "get_tagger_acceleration":
case "set_tagger_acceleration":
return p.acceleration ?? "auto";
@@ -477,7 +534,7 @@ export async function handleMockCommand(cmd: string, payload?: unknown): Promise
case "set_tagger_batch_size":
return p.batch_size ?? 8;
case "probe_tagger_runtime":
return { ready: true, acceleration: "auto", session: { file: "model.onnx", inputs: ["input"], outputs: ["tags"] } };
return mockTaggerRuntimeProbe();
case "get_tagging_queue_scope":
return "all";
case "get_tagging_queue_folder_ids":
+13 -1
View File
@@ -1,4 +1,14 @@
export type MockScenario = "rich" | "empty" | "busy" | "duplicates" | "album" | "errors" | "huge" | "extreme";
export type MockScenario =
| "rich"
| "empty"
| "busy"
| "duplicates"
| "album"
| "errors"
| "huge"
| "extreme"
| "unready"
| "joytag-unready";
const SCENARIOS = new Set<MockScenario>([
"rich",
@@ -9,6 +19,8 @@ const SCENARIOS = new Set<MockScenario>([
"errors",
"huge",
"extreme",
"unready",
"joytag-unready",
]);
export function getMockScenario(): MockScenario {