portico/Tests/PorticoTests/ReactiveLabelTests.swift

140 lines
4.4 KiB
Swift

import Testing
@_spi(SGTKInternal) import Gtk
@_spi(Portico) import Portico
@MainActor @Suite(.serialized) struct ReactiveLabelTests {
// MARK: - GTK-dependent tests
@Test func explicitBindingUpdatesLabel() {
guard Gtk.initCheck() else { return }
let box = StateBox("a")
let ctx = MountContext()
let w = AnyView(Label(str: "").label(Binding(box))).makeWidget(ctx)
let lbl = w as! Gtk.Label
defer { box.set("") }
#expect(lbl.getText() == "a")
box.set("b")
#expect(lbl.getText() == "b")
}
@Test func explicitBindingUpdatesMultipleTimes() {
guard Gtk.initCheck() else { return }
let box = StateBox("first")
let ctx = MountContext()
let lbl = AnyView(Label(str: "").label(Binding(box))).makeWidget(ctx) as! Gtk.Label
defer { box.set("") }
#expect(lbl.getText() == "first")
box.set("second")
#expect(lbl.getText() == "second")
box.set("third")
#expect(lbl.getText() == "third")
}
@Test func trackedClosureUpdatesLabel() {
guard Gtk.initCheck() else { return }
let box = StateBox(0)
let lbl = AnyView(Label(str: "").label { "Count: \(box.get())" })
.makeWidget(MountContext()) as! Gtk.Label
defer { box.set(0) }
#expect(lbl.getText() == "Count: 0")
box.set(1)
#expect(lbl.getText() == "Count: 1")
box.set(2)
#expect(lbl.getText() == "Count: 2")
}
@Test func reactiveNodeMountsExactlyOnce() {
guard Gtk.initCheck() else { return }
MountProbe.count = 0
let box = StateBox(0)
let ctx = MountContext()
defer { box.set(0) }
let lbl = AnyView(ProbeLabel(box: box)).makeWidget(ctx) as! Gtk.Label
#expect(MountProbe.count == 1)
#expect(lbl.getText() == "n=0")
box.set(1)
#expect(MountProbe.count == 1)
#expect(lbl.getText() == "n=1")
}
@Test func bindingNodeMountsExactlyOnce() {
guard Gtk.initCheck() else { return }
MountProbe.count = 0
let box = StateBox("a")
let ctx = MountContext()
defer { box.set("") }
let lbl = AnyView(ProbeBindingLabel(box: box)).makeWidget(ctx) as! Gtk.Label
#expect(MountProbe.count == 1)
#expect(lbl.getText() == "a")
box.set("b")
#expect(MountProbe.count == 1)
#expect(lbl.getText() == "b")
}
@Test func constantLabelStaysUnchanged() {
guard Gtk.initCheck() else { return }
let lbl = AnyView(Label(str: "static"))
.makeWidget(MountContext()) as! Gtk.Label
#expect(lbl.getText() == "static")
}
// MARK: - Regression: wrapper not retained after mount
@Test func trackedLabelUpdatesWhenWrapperNotRetained() {
guard Gtk.initCheck() else { return }
let box = StateBox(0)
let parent = Gtk.Box(orientation: .vertical, spacing: 0)
do {
let w = AnyView(Label(str: "").label { "n=\(box.get())" }).makeWidget(MountContext())
parent.append(child: w)
}
box.set(1)
let child = Gtk.Label(retaining: parent.getFirstChild()!.pointer)
#expect(child.getText() == "n=1")
}
@Test func explicitBindingUpdatesWhenWrapperNotRetained() {
guard Gtk.initCheck() else { return }
let box = StateBox("a")
let parent = Gtk.Box(orientation: .vertical, spacing: 0)
let ctx = MountContext()
do {
let w = AnyView(Label(str: "").label(Binding(box))).makeWidget(ctx)
parent.append(child: w)
}
box.set("b")
let child = Gtk.Label(retaining: parent.getFirstChild()!.pointer)
#expect(child.getText() == "b")
}
}
// MARK: - Test helpers
@MainActor fileprivate enum MountProbe { static var count = 0 }
@MainActor fileprivate struct ProbeLabel: View, Mountable {
let box: StateBox<Int>
var body: Never { fatalError() }
func mount(_ ctx: MountContext) -> Gtk.Widget {
MountProbe.count += 1
return AnyView(Label(str: "").label { "n=\(box.get())" }).makeWidget(ctx)
}
}
@MainActor fileprivate struct ProbeBindingLabel: View, Mountable {
let box: StateBox<String>
var body: Never { fatalError() }
func mount(_ ctx: MountContext) -> Gtk.Widget {
MountProbe.count += 1
return AnyView(Label(str: "").label(Binding(box))).makeWidget(ctx)
}
}