Files
divkit/client/ios/LayoutKit/LayoutKitTests/UI/Blocks/Array+UIViewRenderableTests.swift
pkurchatov 0663870bf9 Swiftformat
commit_hash:52ce6e4a9f35a44ddef28565cec76ddaabdc2d08
2025-09-18 12:40:09 +03:00

289 lines
7.3 KiB
Swift

@testable import LayoutKit
import VGSL
import XCTest
final class Array_UIViewRenderableTests: XCTestCase {
func test_AttachesViewsToParent() {
let parent = UIView(frame: .zero)
let views = [].reused(
with: [TestBlock(), TestBlock(), TestBlock()],
attachTo: parent,
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
XCTAssertEqual(views, parent.subviews)
}
func test_BlocksOrderDefinesViewsOrder() {
let blocks = [TestBlock(), TestBlock(), TestBlock()]
let views = [].reused(
with: blocks,
attachTo: UIView(frame: .zero),
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
let viewsBlocks = (views as! [TestView]).compactMap(\.block)
XCTAssertTrue(viewsBlocks.elementsEqual(blocks, by: ===))
}
func test_ReusesSuitableViews() {
let oldView0 = TestView(frame: .zero)
let oldView1 = TestView(frame: .zero)
let views = [oldView0, oldView1].reused(
with: [TestBlock(), OtherTestBlock(), TestBlock()],
attachTo: UIView(frame: .zero),
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
XCTAssertEqual(3, views.count)
XCTAssertEqual(oldView0, views[0])
XCTAssertEqual(oldView1, views[2])
}
func test_NotReusesUnsuitableViews() {
let oldView0 = OtherTestView(frame: .zero)
let oldView1 = TestView(frame: .zero)
let oldView2 = TestView(frame: .zero)
let views = [oldView0, oldView1, oldView2].reused(
with: [TestBlock(), TestBlock(), TestBlock()],
attachTo: UIView(frame: .zero),
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
XCTAssertEqual(3, views.count)
XCTAssertEqual(oldView1, views[0])
XCTAssertEqual(oldView2, views[1])
}
func test_ReordersReusedViews() {
let oldView0 = OtherTestView(frame: .zero)
let oldView1 = TestView(frame: .zero)
let oldView2 = TestView(frame: .zero)
let views = [oldView0, oldView1, oldView2].reused(
with: [TestBlock(), OtherTestBlock(), TestBlock()],
attachTo: UIView(frame: .zero),
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
XCTAssertEqual([oldView1, oldView0, oldView2], views)
}
func test_ReconfiguresReusedViews() {
let block0 = TestBlock()
let block1 = TestBlock()
let block2 = TestBlock()
let oldView0 = TestView(frame: .zero)
oldView0.block = TestBlock()
let oldView1 = TestView(frame: .zero)
oldView1.block = TestBlock()
let oldView2 = TestView(frame: .zero)
oldView2.block = TestBlock()
let views = [oldView0, oldView1, oldView2].reused(
with: [block0, block1, block2],
attachTo: UIView(frame: .zero),
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
XCTAssertEqual([oldView0, oldView1, oldView2], views)
XCTAssertTrue(block0 === oldView0.block)
XCTAssertTrue(block1 === oldView1.block)
XCTAssertTrue(block2 === oldView2.block)
}
func test_ReusesBestViewForReuse() {
let block0 = TestBlock()
let oldView0 = TestView(frame: .zero)
let oldView1 = TestView(frame: .zero)
let oldView2 = TestView(frame: .zero)
oldView2.block = block0
let views = [oldView0, oldView1, oldView2].reused(
with: [block0, TestBlock(), TestBlock()],
attachTo: UIView(frame: .zero),
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
XCTAssertEqual([oldView2, oldView0, oldView1], views)
}
func test_BlocksReuseOrder() {
let blocks = [TestBlock(), TestBlock()]
let views = [TestView(frame: .zero), TestView(frame: .zero)]
for (block, view) in zip(blocks, views) {
view.block = block
}
let newBlocks = [TestBlock(), TestBlock()]
newBlocks[0].valuableContent = 2
let newViews = (views as [BlockView]).reused(
with: newBlocks,
attachTo: UIView(frame: .zero),
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
XCTAssertEqual(newViews as! [TestView], views.reversed())
}
func test_PathHolderBlocksReuseOrder() {
let blocks = [TestBlock("path1"), TestBlock("path2")]
let views = [TestView(frame: .zero), TestView(frame: .zero)]
for (block, view) in zip(blocks, views) {
view.block = block
}
let newBlocks = [TestBlock("path1"), TestBlock("path2")]
newBlocks[0].valuableContent = 2
let newViews = (views as [BlockView]).reused(
with: newBlocks,
attachTo: UIView(frame: .zero),
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
XCTAssertEqual(views, newViews as! [TestView])
}
func test_DecoratingBlockIsBestViewForReuse() {
let oldBlock = DecoratingBlock(
child: DecoratingBlock(
child: TestBlock("path1")
)
)
let view = DecoratingBlock.makeBlockView()
oldBlock.configureBlockView(
view,
observer: nil,
overscrollDelegate: nil,
renderingDelegate: nil
)
let newBlock = DecoratingBlock(
child: DecoratingBlock(
child: modified(TestBlock("path1")) { $0.valuableContent = 20 }
)
)
XCTAssertTrue(newBlock.isBestViewForReuse(view))
}
}
private class TestBlock: BlockWithTraits {
var widthTrait: LayoutKit.LayoutTrait = .resizable
var heightTrait: LayoutKit.LayoutTrait = .resizable
var intrinsicContentWidth: CGFloat = 0.0
var valuableContent = 1
let path: UIElementPath?
var debugDescription = ""
init(_ path: UIElementPath? = nil) {
self.path = path
}
static func makeBlockView() -> BlockView { TestView() }
func intrinsicContentHeight(forWidth _: CGFloat) -> CGFloat {
0.0
}
func equals(_ other: LayoutKit.Block) -> Bool {
guard let other = other as? TestBlock else {
return false
}
return other.valuableContent == valuableContent
}
func getImageHolders() -> [VGSLUI.ImageHolder] {
[]
}
func laidOut(for _: CGFloat) -> LayoutKit.Block {
self
}
func laidOut(for _: CGSize) -> LayoutKit.Block {
self
}
func updated(withStates _: LayoutKit.BlocksState) throws -> Self {
self
}
func isBestViewForReuse(_ view: BlockView) -> Bool {
if let path, path == ((view as? TestView)?.block as? TestBlock)?.path {
true
} else {
(view as? TestView)?.block == self
}
}
func canConfigureBlockView(_ view: BlockView) -> Bool {
view is TestView
}
func configureBlockView(
_ view: BlockView,
observer _: ElementStateObserver?,
overscrollDelegate _: ScrollDelegate?,
renderingDelegate _: RenderingDelegate?
) {
(view as! TestView).block = self
}
}
private final class TestView: BlockView, VisibleBoundsTrackingLeaf {
var block: TestBlock?
var effectiveBackgroundColor: UIColor? { backgroundColor }
}
private final class OtherTestBlock: UIViewRenderable {
init() {}
static func makeBlockView() -> BlockView { OtherTestView() }
func canConfigureBlockView(_ view: BlockView) -> Bool {
view is OtherTestView
}
func configureBlockView(
_: BlockView,
observer _: ElementStateObserver?,
overscrollDelegate _: ScrollDelegate?,
renderingDelegate _: RenderingDelegate?
) {}
}
private final class OtherTestView: BlockView, VisibleBoundsTrackingLeaf {
var effectiveBackgroundColor: UIColor? { backgroundColor }
}