Conditionals: EitherView and OptionalView with reactive branch visibility

This commit is contained in:
Brendan Szymanski 2026-07-23 22:35:34 -04:00
parent c309fc3d1a
commit d177a85236
6 changed files with 308 additions and 5 deletions

View file

@ -0,0 +1,168 @@
import Testing
@_spi(SGTKInternal) import Gtk
@_spi(Portico) import Portico
@MainActor @Suite(.serialized) struct ConditionalTests {
// MARK: - EitherView
@Test func eitherViewInitiallyTrueBranch() {
guard Gtk.initCheck() else { return }
ConditionProbe.reset()
let box = StateBox(true)
let binding = Binding(box)
let view = EitherView(binding,
first: { FirstProbeLabel() },
second: { SecondProbeLabel() }
)
let widget = AnyView(view).makeWidget(MountContext())
let stack = widget as! Gtk.Stack
// Initially, only the "true" branch is mounted and visible.
#expect(stack.getVisibleChildName() == "true")
#expect(ConditionProbe.firstCount == 1)
#expect(ConditionProbe.secondCount == 0)
// Toggle to false lazy-mounts the "false" branch.
box.set(false)
#expect(stack.getVisibleChildName() == "false")
#expect(ConditionProbe.firstCount == 1)
#expect(ConditionProbe.secondCount == 1)
// Toggle back to true no new mounts.
box.set(true)
#expect(stack.getVisibleChildName() == "true")
#expect(ConditionProbe.firstCount == 1)
#expect(ConditionProbe.secondCount == 1)
}
@Test func eitherViewInitiallyFalseBranch() {
guard Gtk.initCheck() else { return }
ConditionProbe.reset()
let box = StateBox(false)
let binding = Binding(box)
let view = EitherView(binding,
first: { FirstProbeLabel() },
second: { SecondProbeLabel() }
)
let widget = AnyView(view).makeWidget(MountContext())
let stack = widget as! Gtk.Stack
// Initially, only the "false" branch is mounted and visible.
#expect(stack.getVisibleChildName() == "false")
#expect(ConditionProbe.firstCount == 0)
#expect(ConditionProbe.secondCount == 1)
// Toggle to true lazy-mounts the "true" branch.
box.set(true)
#expect(stack.getVisibleChildName() == "true")
#expect(ConditionProbe.firstCount == 1)
#expect(ConditionProbe.secondCount == 1)
// Toggle back to false no new mounts.
box.set(false)
#expect(stack.getVisibleChildName() == "false")
#expect(ConditionProbe.firstCount == 1)
#expect(ConditionProbe.secondCount == 1)
}
@Test func eitherViewTracksConditionChanges() {
guard Gtk.initCheck() else { return }
ConditionProbe.reset()
let box = StateBox(true)
let binding = Binding(box)
let view = EitherView(binding,
first: { FirstProbeLabel() },
second: { SecondProbeLabel() }
)
let widget = AnyView(view).makeWidget(MountContext())
let stack = widget as! Gtk.Stack
// Three toggles: truefalsetruefalse
box.set(false)
box.set(true)
box.set(false)
#expect(stack.getVisibleChildName() == "false")
#expect(ConditionProbe.firstCount == 1)
#expect(ConditionProbe.secondCount == 1)
}
// MARK: - OptionalView
@Test func optionalViewMountsChildOnce() {
guard Gtk.initCheck() else { return }
ConditionProbe.reset()
let box = StateBox(true)
let binding = Binding(box)
let view = OptionalView(binding) { FirstProbeLabel() }
let widget = AnyView(view).makeWidget(MountContext())
let revealer = widget as! Gtk.Revealer
// Initially revealed, child mounted once.
#expect(revealer.getRevealChild() == true)
#expect(ConditionProbe.firstCount == 1)
// Toggle to false child still mounted, just hidden.
box.set(false)
#expect(revealer.getRevealChild() == false)
#expect(ConditionProbe.firstCount == 1)
// Toggle back to true still same child.
box.set(true)
#expect(revealer.getRevealChild() == true)
#expect(ConditionProbe.firstCount == 1)
}
@Test func optionalViewHidesWhenConditionFalse() {
guard Gtk.initCheck() else { return }
ConditionProbe.reset()
let box = StateBox(false)
let binding = Binding(box)
let view = OptionalView(binding) { FirstProbeLabel() }
let widget = AnyView(view).makeWidget(MountContext())
let revealer = widget as! Gtk.Revealer
// Initially hidden, but child WAS mounted (build-once).
#expect(revealer.getRevealChild() == false)
#expect(ConditionProbe.firstCount == 1)
// Toggle to true child visible, no remount.
box.set(true)
#expect(revealer.getRevealChild() == true)
#expect(ConditionProbe.firstCount == 1)
}
}
// MARK: - Test helpers
@MainActor fileprivate enum ConditionProbe {
static var firstCount = 0
static var secondCount = 0
static func reset() {
firstCount = 0
secondCount = 0
}
}
@MainActor fileprivate struct FirstProbeLabel: View, Mountable {
var body: Never { fatalError() }
func mount(_ ctx: MountContext) -> Gtk.Widget {
ConditionProbe.firstCount += 1
return Gtk.Label(str: "first")
}
}
@MainActor fileprivate struct SecondProbeLabel: View, Mountable {
var body: Never { fatalError() }
func mount(_ ctx: MountContext) -> Gtk.Widget {
ConditionProbe.secondCount += 1
return Gtk.Label(str: "second")
}
}