gtk: Create a MessagesView

The MessagesView combines two MessageViews into a split-pane card. This
lets us display stdout and stderr side-by-side to the user so they can
see what is going on. I also add a 'back' button that the user can click
to signal that they are done reviewing the output.

Signed-off-by: Anna Schumaker <anna@nowheycreamery.com>
This commit is contained in:
Anna Schumaker 2023-08-30 16:57:38 -04:00
parent 5fb9bd6221
commit ea2913429c
2 changed files with 125 additions and 0 deletions

View File

@ -4,6 +4,7 @@ import unittest
import tests.xunit import tests.xunit
import xfstestsdb.gtk.view import xfstestsdb.gtk.view
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import Adw
class TestXunitView(unittest.TestCase): class TestXunitView(unittest.TestCase):
@ -352,6 +353,85 @@ class TestMessageView(unittest.TestCase):
self.assertEqual(self.view.text, "\n".join(diff)) self.assertEqual(self.view.text, "\n".join(diff))
class MessagesView(unittest.TestCase):
"""Test the MessagesView."""
def setUp(self):
"""Set up common variables."""
self.view = xfstestsdb.gtk.view.MessagesView()
def test_init(self):
"""Check that the MessagesView was set up correctly."""
self.assertIsInstance(self.view, Gtk.Box)
self.assertIsInstance(self.view.get_first_child(), Gtk.CenterBox)
self.assertIsInstance(self.view.get_last_child(), Gtk.Paned)
self.assertTrue(self.view.get_first_child().has_css_class("toolbar"))
self.assertTrue(self.view.has_css_class("card"))
self.assertEqual(self.view.props.orientation, Gtk.Orientation.VERTICAL)
self.assertEqual(self.view.props.margin_start, 24)
self.assertEqual(self.view.props.margin_end, 24)
self.assertEqual(self.view.props.margin_top, 24)
self.assertEqual(self.view.props.margin_bottom, 24)
def test_back_button(self):
"""Check that the back button was set up correctly."""
self.assertIsInstance(self.view._back, Gtk.Button)
self.assertIsInstance(self.view._back.props.child, Adw.ButtonContent)
self.assertEqual(self.view.get_first_child().props.start_widget,
self.view._back)
self.assertEqual(self.view._back.props.child.props.icon_name,
"go-previous-symbolic")
self.assertEqual(self.view._back.props.child.props.label, "back")
self.assertTrue(self.view._back.has_css_class("suggested-action"))
self.assertTrue(self.view._back.has_css_class("pill"))
go_back = unittest.mock.Mock()
self.view.connect("go-back", go_back)
self.view._back.emit("clicked")
go_back.assert_called()
def test_title(self):
"""Check that the view title was set up correctly."""
self.assertIsInstance(self.view._title, Adw.WindowTitle)
self.assertEqual(self.view.get_first_child().props.center_widget,
self.view._title)
self.assertEqual(self.view.testcase, "")
self.view.testcase = "test/case"
self.assertEqual(self.view._title.props.title, "test/case")
self.assertEqual(self.view.xunit, "")
self.view.xunit = "xunit-1"
self.assertEqual(self.view._title.props.subtitle, "xunit-1")
def test_stdout(self):
"""Check that the stdout window was set up properly."""
self.assertIsInstance(self.view._stdout,
xfstestsdb.gtk.view.MessageView)
self.assertEqual(self.view.get_last_child().props.start_child,
self.view._stdout)
self.assertEqual(self.view._stdout.title, "stdout")
self.assertEqual(self.view.stdout, "")
self.view.stdout = "stdout text"
self.assertEqual(self.view._stdout.text, "stdout text")
def test_stderr(self):
"""Check that the stderr window was set up properly."""
self.assertIsInstance(self.view._stderr,
xfstestsdb.gtk.view.MessageView)
self.assertEqual(self.view.get_last_child().props.end_child,
self.view._stderr)
self.assertEqual(self.view._stderr.title, "stderr")
self.assertEqual(self.view.stderr, "")
self.view.stderr = "stderr text"
self.assertEqual(self.view._stderr.text, "stderr text")
class TestSummaryView(unittest.TestCase): class TestSummaryView(unittest.TestCase):
"""Tests the SummaryView.""" """Tests the SummaryView."""

View File

@ -4,6 +4,7 @@ import re
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Gio from gi.repository import Gio
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import Adw
from .model import PropertyList from .model import PropertyList
from .model import PropertyFilter from .model import PropertyFilter
from .model import TestCaseList from .model import TestCaseList
@ -231,6 +232,50 @@ class MessageView(Gtk.Box):
buffer.set_text(new_text) buffer.set_text(new_text)
class MessagesView(Gtk.Box):
"""A view for displaying stdout and stderr messages."""
testcase = GObject.Property(type=str)
xunit = GObject.Property(type=str)
stdout = GObject.Property(type=str)
stderr = GObject.Property(type=str)
def __init__(self):
"""Initialize a MessagesView."""
icon = "go-previous-symbolic"
super().__init__(orientation=Gtk.Orientation.VERTICAL, margin_top=24,
margin_start=24, margin_end=24, margin_bottom=24)
self._back = Gtk.Button(child=Adw.ButtonContent(icon_name=icon,
label="back"))
self._title = Adw.WindowTitle()
self._stdout = MessageView("stdout")
self._stderr = MessageView("stderr")
self.bind_property("testcase", self._title, "title")
self.bind_property("xunit", self._title, "subtitle")
self.bind_property("stdout", self._stdout, "text")
self.bind_property("stderr", self._stderr, "text")
self._back.connect("clicked", self.__back_clicked)
self.append(Gtk.CenterBox(start_widget=self._back,
center_widget=self._title))
self.append(Gtk.Paned(start_child=self._stdout,
end_child=self._stderr, vexpand=True))
self.get_first_child().add_css_class("toolbar")
self._back.add_css_class("suggested-action")
self._back.add_css_class("pill")
self.add_css_class("card")
def __back_clicked(self, button: Gtk.Button) -> None:
self.emit("go-back")
@GObject.Signal
def go_back(self) -> None:
"""Signal that the user wants to go back."""
class SummaryView(XunitView): class SummaryView(XunitView):
"""Displays our SummaryList model to the user.""" """Displays our SummaryList model to the user."""