mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Add debug info
This commit is contained in:
@@ -6,7 +6,7 @@ class CenterLayoutExample(App):
|
||||
CSS_PATH = "center_layout.css"
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Static("One", id="bottom")
|
||||
yield Static("Onee", id="bottom")
|
||||
yield Static("Two", id="middle")
|
||||
yield Static("Three", id="top")
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import os
|
||||
import shlex
|
||||
from typing import Iterable
|
||||
|
||||
from textual.app import App
|
||||
from textual._import_app import import_app
|
||||
|
||||
# This module defines our "Custom Fences", powered by SuperFences
|
||||
@@ -25,7 +26,9 @@ def format_svg(source, language, css_class, options, md, attrs, **kwargs) -> str
|
||||
try:
|
||||
rows = int(attrs.get("lines", 24))
|
||||
columns = int(attrs.get("columns", 80))
|
||||
svg = take_svg_screenshot(path, press, title, terminal_size=(rows, columns))
|
||||
svg = take_svg_screenshot(
|
||||
None, path, press, title, terminal_size=(rows, columns)
|
||||
)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
@@ -39,8 +42,9 @@ def format_svg(source, language, css_class, options, md, attrs, **kwargs) -> str
|
||||
|
||||
|
||||
def take_svg_screenshot(
|
||||
app_path: str,
|
||||
press: Iterable[str] = ("_", "_"),
|
||||
app: App | None = None,
|
||||
app_path: str | None = None,
|
||||
press: Iterable[str] = ("_",),
|
||||
title: str | None = None,
|
||||
terminal_size: tuple[int, int] = (24, 80),
|
||||
) -> str:
|
||||
@@ -48,7 +52,10 @@ def take_svg_screenshot(
|
||||
|
||||
os.environ["COLUMNS"] = str(columns)
|
||||
os.environ["LINES"] = str(rows)
|
||||
app = import_app(app_path)
|
||||
|
||||
if app is None:
|
||||
app = import_app(app_path)
|
||||
|
||||
app.console.legacy_windows = False
|
||||
if title is None:
|
||||
title = app.title
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import difflib
|
||||
import os
|
||||
import pprint
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
@@ -13,15 +15,19 @@ from _pytest.fixtures import FixtureRequest
|
||||
from _pytest.main import Session
|
||||
from _pytest.terminal import TerminalReporter
|
||||
from jinja2 import Template
|
||||
from rich import inspect
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from textual.app import App
|
||||
from textual._doc import take_svg_screenshot
|
||||
from textual._import_app import import_app
|
||||
|
||||
snapshot_svg_key = pytest.StashKey[str]()
|
||||
actual_svg_key = pytest.StashKey[str]()
|
||||
snapshot_pass = pytest.StashKey[bool]()
|
||||
TEXTUAL_SNAPSHOT_SVG_KEY = pytest.StashKey[str]()
|
||||
TEXTUAL_ACTUAL_SVG_KEY = pytest.StashKey[str]()
|
||||
TEXTUAL_SNAPSHOT_PASS = pytest.StashKey[bool]()
|
||||
TEXTUAL_APP_KEY = pytest.StashKey[App]()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -36,15 +42,17 @@ def snap_compare(
|
||||
|
||||
def compare(app_path: str, snapshot: SnapshotAssertion) -> bool:
|
||||
node = request.node
|
||||
actual_screenshot = take_svg_screenshot(app_path)
|
||||
app = import_app(app_path)
|
||||
actual_screenshot = take_svg_screenshot(app=app)
|
||||
result = snapshot == actual_screenshot
|
||||
|
||||
if result is False:
|
||||
# The split and join below is a mad hack, sorry...
|
||||
node.stash[snapshot_svg_key] = "\n".join(str(snapshot).splitlines()[1:-1])
|
||||
node.stash[actual_svg_key] = actual_screenshot
|
||||
node.stash[TEXTUAL_SNAPSHOT_SVG_KEY] = "\n".join(str(snapshot).splitlines()[1:-1])
|
||||
node.stash[TEXTUAL_ACTUAL_SVG_KEY] = actual_screenshot
|
||||
node.stash[TEXTUAL_APP_KEY] = app
|
||||
else:
|
||||
node.stash[snapshot_pass] = True
|
||||
node.stash[TEXTUAL_SNAPSHOT_PASS] = True
|
||||
|
||||
return result
|
||||
|
||||
@@ -59,6 +67,8 @@ class SvgSnapshotDiff:
|
||||
file_similarity: float
|
||||
path: PathLike
|
||||
line_number: int
|
||||
app: App
|
||||
environment: dict
|
||||
|
||||
|
||||
def pytest_sessionfinish(
|
||||
@@ -73,22 +83,28 @@ def pytest_sessionfinish(
|
||||
diffs: List[SvgSnapshotDiff] = []
|
||||
num_snapshots_passing = 0
|
||||
for item in session.items:
|
||||
num_snapshots_passing += int(item.stash.get(snapshot_pass, False))
|
||||
snapshot_svg = item.stash.get(snapshot_svg_key, None)
|
||||
actual_svg = item.stash.get(actual_svg_key, None)
|
||||
if snapshot_svg and actual_svg:
|
||||
|
||||
# Grab the data our fixture attached to the pytest node
|
||||
num_snapshots_passing += int(item.stash.get(TEXTUAL_SNAPSHOT_PASS, False))
|
||||
snapshot_svg = item.stash.get(TEXTUAL_SNAPSHOT_SVG_KEY, None)
|
||||
actual_svg = item.stash.get(TEXTUAL_ACTUAL_SVG_KEY, None)
|
||||
app = item.stash.get(TEXTUAL_APP_KEY, None)
|
||||
|
||||
if snapshot_svg and actual_svg and app:
|
||||
path, line_index, name = item.reportinfo()
|
||||
diffs.append(
|
||||
SvgSnapshotDiff(
|
||||
snapshot=str(snapshot_svg),
|
||||
actual=str(actual_svg),
|
||||
file_similarity=100
|
||||
* difflib.SequenceMatcher(
|
||||
* difflib.SequenceMatcher(
|
||||
a=str(snapshot_svg), b=str(actual_svg)
|
||||
).ratio(),
|
||||
test_name=name,
|
||||
path=path,
|
||||
line_number=line_index + 1,
|
||||
app=app,
|
||||
environment=dict(os.environ),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-4" style="background-color:#F4F8F7;">
|
||||
<div class="col-8 p-4">
|
||||
@@ -51,14 +53,18 @@
|
||||
</strong>
|
||||
<span class="text-muted">({{ "%.2f"|format(diff.file_similarity) }}% source similarity)</span>
|
||||
</div>
|
||||
<span class="text-muted">{{ diff.path }}:{{ diff.line_number }}</span>
|
||||
<div class="text-muted">
|
||||
{{ diff.path }}:{{ diff.line_number }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ diff.actual }}
|
||||
<div class="w-100 d-flex justify-content-center mt-1">
|
||||
<span class="small">Output from test</span>
|
||||
<span class="small">Output from test (<a href="#" class="link-primary mb-0"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#environmentModal">More info</a>)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
@@ -69,6 +75,63 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal modal-lg fade" id="environmentModal" tabindex="-1"
|
||||
aria-labelledby="environmentModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="environmentModalLabel">More info for <span
|
||||
class="font-monospace">{{ diff.test_name }}</span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body overflow-auto">
|
||||
<h5>Textual App State</h5>
|
||||
<table class="table mb-4">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Variable</th>
|
||||
<th scope="col">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="font-monospace">app.console.legacy_windows</td>
|
||||
<td class="font-monospace">{{ diff.app.console.legacy_windows }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-monospace">app.console.size</td>
|
||||
<td class="font-monospace">{{ diff.app.console.size }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h5>Environment (<span class="font-monospace">os.environ</span>)</h5>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Variable</th>
|
||||
<th scope="col">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in diff.environment.items() %}
|
||||
<tr>
|
||||
<td class="font-monospace">{{ key }}</td>
|
||||
<td class="font-monospace">{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -78,21 +141,28 @@
|
||||
<div class="col">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<p class="card-text">If you're happy with the change, run pytest with the <span class="font-monospace text-primary">--snapshot-update</span> flag to update the snapshot.</p>
|
||||
<p class="card-text">If you're happy with the test output, run pytest with the <span
|
||||
class="font-monospace text-primary">--snapshot-update</span> flag to update the snapshot.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="w-100 d-flex p-4 justify-content-center">
|
||||
<p class="text-muted">Report generated at UTC {{ now }}.</p>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="w-100 d-flex p-4 justify-content-center">
|
||||
<p class="text-muted">Report generated at UTC {{ now }}.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user