portico/Tests/PorticoTests/ForEachSpreadTests.swift

197 lines
7 KiB
Swift

import Testing
@_spi(SGTKInternal) import Adw
@_spi(SGTKInternal) import Gtk
@_spi(Portico) import Portico
private enum AccentKey: EnvironmentKey {
static let defaultValue = "default-accent"
}
extension EnvironmentValues {
fileprivate var accent: String {
get { self[AccentKey.self] }
set { self[AccentKey.self] = newValue }
}
}
private struct AccentLabel: View {
@Environment(\.accent) private var accent
var body: some View {
Label(str: "").label { accent }
}
}
@MainActor @Suite(.serialized) struct ForEachSpreadTests {
private func directLabels(_ widget: Gtk.Widget) -> [String] {
var result: [String] = []
var child = widget.getFirstChild()
while let current = child {
if current.cssName == "label" {
result.append(Gtk.Label(retaining: current.pointer).getText())
} else {
result.append("<\(current.cssName)>")
}
child = current.getNextSibling()
}
return result
}
private func wrappedLabels(_ widget: Gtk.Widget) -> [String] {
var result: [String] = []
var child = widget.getFirstChild()
while let current = child {
if let inner = current.getFirstChild() {
result.append(Gtk.Label(retaining: inner.pointer).getText())
}
child = current.getNextSibling()
}
return result
}
@Test func spreadsIntoWrapBoxWithoutWrapper() {
guard Gtk.initCheck() else { return }
let items = StateBox([(1, "Nachos"), (2, "Tacos")])
let view = WrapBox {
Label(str: "Header")
ForEach(Binding(items), id: \.0) { Label(str: $0.1) }
}
let wrap = AnyView(view).makeWidget(MountContext()) as! Adw.WrapBox
#expect(directLabels(wrap) == ["Header", "Nachos", "Tacos"])
}
@Test func wrapBoxDiffRespectsLeadingStaticChild() {
guard Gtk.initCheck() else { return }
let items = StateBox([(1, "Nachos"), (2, "Tacos")])
let view = WrapBox {
Label(str: "Header")
ForEach(Binding(items), id: \.0) { Label(str: $0.1) }
}
let wrap = AnyView(view).makeWidget(MountContext()) as! Adw.WrapBox
items.set([(1, "Nachos"), (2, "Tacos"), (3, "Enchiladas")])
#expect(directLabels(wrap) == ["Header", "Nachos", "Tacos", "Enchiladas"])
items.set([(2, "Tacos"), (3, "Enchiladas")])
#expect(directLabels(wrap) == ["Header", "Tacos", "Enchiladas"])
items.set([(3, "Enchiladas"), (2, "Tacos")])
#expect(directLabels(wrap) == ["Header", "Enchiladas", "Tacos"])
}
@Test func spreadsIntoListBoxAndRemovesWrappedRow() {
guard Gtk.initCheck() else { return }
let items = StateBox([(1, "One"), (2, "Two"), (3, "Three")])
let view = ListBox {
ForEach(Binding(items), id: \.0) { Label(str: $0.1) }
}
let list = AnyView(view).makeWidget(MountContext()) as! Gtk.ListBox
#expect(wrappedLabels(list) == ["One", "Two", "Three"])
items.set([(1, "One"), (3, "Three")])
#expect(wrappedLabels(list) == ["One", "Three"])
}
@Test func spreadsIntoCarousel() {
guard Gtk.initCheck() else { return }
let items = StateBox([(1, "One"), (2, "Two"), (3, "Three")])
let view = Carousel {
ForEach(Binding(items), id: \.0) { Label(str: $0.1) }
}
let carousel = AnyView(view).makeWidget(MountContext()) as! Adw.Carousel
#expect(carousel.getNPages() == 3)
items.set([(1, "One"), (3, "Three")])
#expect(carousel.getNPages() == 2)
}
@Test func twoForEachRegionsStayIndependent() {
guard Gtk.initCheck() else { return }
let first = StateBox([(1, "A1"), (2, "A2")])
let second = StateBox([(3, "B1"), (4, "B2")])
let view = WrapBox {
ForEach(Binding(first), id: \.0) { Label(str: $0.1) }
ForEach(Binding(second), id: \.0) { Label(str: $0.1) }
}
let wrap = AnyView(view).makeWidget(MountContext()) as! Adw.WrapBox
first.set([(1, "A1"), (2, "A2"), (5, "A3")])
#expect(directLabels(wrap) == ["A1", "A2", "A3", "B1", "B2"])
}
@Test func groupSurvivesEnvironmentModifier() {
guard Gtk.initCheck() else { return }
let items = StateBox([1, 2])
let view = WrapBox {
ForEach(Binding(items), id: \.self) { _ in AccentLabel() }
.environment(\.accent, "scoped")
}
let wrap = AnyView(view).makeWidget(MountContext()) as! Adw.WrapBox
#expect(directLabels(wrap) == ["scoped", "scoped"])
}
@Test func ledgerDownwardRangeMoveOnIndexHost() {
guard Gtk.initCheck() else { return }
let list = Gtk.ListBox()
let ledger = ChildLedger(host: list)
let region = ledger.addRegion()
for name in ["A", "B", "C", "D"] {
region.insert(Gtk.Label(str: name), at: region.count)
}
#expect(wrappedLabels(list) == ["A", "B", "C", "D"])
region.move(from: 1, count: 2, to: 2)
#expect(wrappedLabels(list) == ["A", "D", "B", "C"])
region.move(from: 0, count: 1, to: 3)
#expect(wrappedLabels(list) == ["D", "B", "C", "A"])
}
@Test func ledgerDownwardRangeMoveOnSiblingHost() {
guard Gtk.initCheck() else { return }
let box = Gtk.Box(orientation: .vertical, spacing: 0)
let ledger = ChildLedger(host: box)
let region = ledger.addRegion()
for name in ["A", "B", "C", "D"] {
region.insert(Gtk.Label(str: name), at: region.count)
}
#expect(directLabels(box) == ["A", "B", "C", "D"])
region.move(from: 1, count: 2, to: 2)
#expect(directLabels(box) == ["A", "D", "B", "C"])
region.move(from: 0, count: 1, to: 3)
#expect(directLabels(box) == ["D", "B", "C", "A"])
}
@Test func ledgerUpwardRangeMoveOnSiblingHost() {
guard Gtk.initCheck() else { return }
let box = Gtk.Box(orientation: .vertical, spacing: 0)
let ledger = ChildLedger(host: box)
let region = ledger.addRegion()
for name in ["A", "B", "C", "D"] {
region.insert(Gtk.Label(str: name), at: region.count)
}
region.move(from: 2, count: 2, to: 0)
#expect(directLabels(box) == ["C", "D", "A", "B"])
region.move(from: 3, count: 1, to: 1)
#expect(directLabels(box) == ["C", "B", "D", "A"])
}
@Test func fallsBackToBoxInSingleWidgetPosition() {
guard Gtk.initCheck() else { return }
let items = StateBox([(1, "One"), (2, "Two")])
let view = ScrolledWindow {
ForEach(Binding(items), id: \.0) { Label(str: $0.1) }
}
let scrolled = AnyView(view).makeWidget(MountContext()) as! Gtk.ScrolledWindow
var child = scrolled.getChild()!
while child.cssName != "box" {
child = child.getFirstChild()!
}
let box = Gtk.Box(retaining: child.pointer)
#expect(directLabels(box) == ["One", "Two"])
}
}