Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/korvid/ui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

import yaml
from rich.text import Text
from textual.app import App, ComposeResult, SuspendNotSupported
from textual.app import App, ComposeResult, ScreenStackError, SuspendNotSupported
from textual.binding import Binding
from textual.containers import Horizontal
from textual.coordinate import Coordinate
Expand Down Expand Up @@ -2387,7 +2387,13 @@ def lookup(view: str, namespace: str) -> list[Summary] | None:
def _refresh_hierarchy(self) -> None:
"""Rebuild an open hierarchy tree from the current store state."""
ctx = self._hierarchy_ctx
screen = self.screen
try:
screen = self.screen
except ScreenStackError:
# A ResourcesUpdated dispatched during app teardown can land
# after the screen stack is emptied (flaky-CI issue #147):
# no screen simply means no tree to refresh.
return
if ctx is None or not isinstance(screen, HierarchyScreen):
return
title, refs, namespace, scope = ctx
Expand Down
21 changes: 21 additions & 0 deletions tests/ui/test_hierarchy_nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import asyncio
from collections.abc import AsyncIterator
from typing import Any
from unittest import mock

from textual.app import ScreenStackError

from korvid.core.config import KorvidConfig
from korvid.core.store import ALL_NAMESPACES, ResourceStore, Summary
Expand Down Expand Up @@ -424,6 +427,24 @@ async def navigate_then_switch(*args: object, **kwargs: object) -> None:
app._ctx_switching = False


async def test_refresh_hierarchy_survives_an_empty_screen_stack() -> None:
"""A ResourcesUpdated dispatched during app teardown reaches
_refresh_hierarchy after the screen stack is emptied: reading
App.screen then raises ScreenStackError and fails the whole run
(flaky-CI issue #147) - the refresh must treat 'no screen' as
'no tree open'."""
app, _ = make_app(_HELM_DATA, components=_WEB_COMPONENTS)
async with app.run_test() as pilot:
await pilot.pause(0.1)
# Simulate the teardown interleaving deterministically: the message
# handler runs while the screen stack is already empty.
with mock.patch.object(
type(app), "screen", property(mock.Mock(side_effect=ScreenStackError))
):
app._refresh_hierarchy() # must not raise
assert True # reaching here is the assertion: no ScreenStackError


async def test_describe_from_tree_node() -> None:
app, describe_calls = make_app(_HELM_DATA, components=_WEB_COMPONENTS)
async with app.run_test() as pilot:
Expand Down
Loading