File size: 6,101 Bytes
f4924d6 95f3ee8 f4924d6 95f3ee8 f4924d6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | # Copyright 2026 Hugging Face
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Hermetic unit tests for the Task-browser page builder (``tasks.py``).
No network: fixtures are written to a tmp dir laid out like the inputs
dataset snapshot (``<fixture>/description.yaml``), then loaded + rendered.
"""
from __future__ import annotations
import textwrap
from pathlib import Path
from tasks import load_tasks_from_dir, render_tasks_page
def _write_fixture(root: Path, name: str, body: str) -> None:
d = root / name
d.mkdir(parents=True)
(d / "description.yaml").write_text(textwrap.dedent(body))
def test_load_tasks_generation_editing_and_multi_image(tmp_path: Path) -> None:
_write_fixture(
tmp_path, "101",
"""
description: Reproduce the geometry from the drawing.
input_files:
- input.png
""",
)
_write_fixture(
tmp_path, "127",
"""
description: Reproduce from the drawings.
input_files:
- input.png
- input2.png
""",
)
_write_fixture(
tmp_path, "201",
"""
description: Bring the pocket walls inward by 6mm.
task_type: editing
input_files:
- input.step
""",
)
tasks = load_tasks_from_dir(tmp_path)
# Sorted by fixture name for a stable order.
assert [t["name"] for t in tasks] == ["101", "127", "201"]
gen, multi, edit = tasks
assert gen["task_type"] == "generation" # defaults when unset
assert gen["image_inputs"] == ["input.png"]
assert gen["wants_shape"] is False
# Both drawings are carried for multi-image generation fixtures.
assert multi["image_inputs"] == ["input.png", "input2.png"]
# Editing fixtures ship a STEP -> shape renders, no inline drawing.
assert edit["task_type"] == "editing"
assert edit["wants_shape"] is True
assert edit["image_inputs"] == []
def test_load_tasks_defaults_to_input_png_when_unlisted(tmp_path: Path) -> None:
_write_fixture(
tmp_path, "300",
"description: A part with no input_files listed.\n",
)
(task,) = load_tasks_from_dir(tmp_path)
assert task["image_inputs"] == ["input.png"]
assert task["wants_shape"] is False
def test_render_tasks_page_structure_and_urls(tmp_path: Path) -> None:
_write_fixture(
tmp_path, "201",
"""
description: Bring the pocket walls inward by 6mm.
task_type: editing
input_files:
- input.step
""",
)
_write_fixture(
tmp_path, "127",
"""
description: Reproduce from the drawings.
input_files:
- input.png
- input2.png
""",
)
tasks = load_tasks_from_dir(tmp_path)
calls: list[tuple[str, str]] = []
def asset_url(fixture: str, relpath: str) -> str:
calls.append((fixture, relpath))
return f"/task-input/{fixture}/{relpath}"
doc = render_tasks_page(tasks, asset_url)
# Report-style navigation scaffolding is present.
assert 'id="summary-view"' in doc
assert 'id="detail-view"' in doc
assert "showDetail(" in doc
assert "window._fixtureNames" in doc
# Grid summary view: grouped sections with live count badges,
# search + type segmented filter, and a card per task (clicking a
# card jumps to its detail via showDetail(idx)).
assert 'class="grid"' in doc
assert 'data-group="generation"' in doc
assert 'data-group="editing"' in doc
assert 'data-count-for="generation"' in doc
assert 'id="typeSeg"' in doc
assert 'id="search"' in doc
assert 'class="card"' in doc
# Editing fixture references its starting-shape renders; generation
# multi-image fixture references both drawings.
assert ("201", "renders/iso.png") in calls
assert ("127", "input.png") in calls
assert ("127", "input2.png") in calls
# The prompt is rendered and HTML-escaped (no raw scores anywhere).
assert "Bring the pocket walls inward by 6mm." in doc
assert "Ground Truth" not in doc
assert "CAD Score" not in doc
def test_thumbnails_are_lazy_and_typed(tmp_path: Path) -> None:
"""Every grid thumbnail lazy-loads with reserved dimensions, and the
thumbnail source differs by task type: generation uses ``input.png``,
editing uses the ``renders/iso.png`` render of the input STEP."""
_write_fixture(
tmp_path, "108",
"""
description: Reproduce the bracket.
input_files:
- input.png
""",
)
_write_fixture(
tmp_path, "204",
"""
description: Widen the slot.
task_type: editing
input_files:
- input.step
""",
)
tasks = load_tasks_from_dir(tmp_path)
def asset_url(fixture: str, relpath: str) -> str:
return f"/task-input/{fixture}/{relpath}"
doc = render_tasks_page(tasks, asset_url)
# Generation card thumbnail -> input.png; editing -> iso render.
assert (
'<img loading="lazy" decoding="async" width="180" height="135" '
'src="/task-input/108/input.png"' in doc
)
assert (
'<img loading="lazy" decoding="async" width="180" height="135" '
'src="/task-input/204/renders/iso.png"' in doc
)
# No thumbnail escapes lazy/async loading.
assert doc.count('class="card"') == 2
assert doc.count('loading="lazy" decoding="async"', ) >= 2
def test_render_tasks_page_empty(tmp_path: Path) -> None:
doc = render_tasks_page([], lambda f, r: "")
assert "No tasks found" in doc
|