Files
swift-composable-architectu…/Tests/ComposableArchitectureTests/StorePerceptionTests.swift
Stephen Celis af0a2c7408 Perception 2.0 (#3736)
* Perception 2.0

* wip

* wip

* wip

* wip

* wip

* Revert "wip"

This reverts commit c5d0a06017.

* wip
2025-07-30 11:16:38 -07:00

99 lines
2.2 KiB
Swift

@_spi(Logging) import ComposableArchitecture
import SwiftUI
import XCTest
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
final class StorePerceptionTests: BaseTCATestCase {
override func setUpWithError() throws {
try checkAvailability()
}
@MainActor
func testPerceptionCheck_SkipWhenOutsideView() {
let store = Store(initialState: Feature.State()) {
Feature()
}
store.send(.tap)
}
@MainActor
func testPerceptionCheck_SkipWhenActionClosureOfView() {
@MainActor
struct FeatureView: View {
let store = Store(initialState: Feature.State()) {
Feature()
}
var body: some View {
Text("Hi")
.onAppear { store.send(.tap) }
}
}
render(FeatureView())
}
@MainActor
@available(*, deprecated)
func testPerceptionCheck_AccessStateWithoutTracking() {
@MainActor
struct FeatureView: View {
let store = Store(initialState: Feature.State()) {
Feature()
}
var body: some View {
Text(store.count.description)
}
}
#if DEBUG && !os(visionOS)
XCTExpectFailure {
render(FeatureView())
} issueMatcher: {
$0.compactDescription.contains("Perceptible state was accessed")
}
#endif
}
@MainActor
func testPerceptionCheck_AccessStateWithTracking() {
@MainActor
struct FeatureView: View {
let store = Store(initialState: Feature.State()) {
Feature()
}
var body: some View {
WithPerceptionTracking {
Text(store.count.description)
}
}
}
render(FeatureView())
}
@MainActor
private func render(_ view: some View) {
let image = ImageRenderer(content: view).cgImage
_ = image
}
}
@Reducer
private struct Feature {
@ObservableState
struct State {
var count = 0
}
enum Action { case tap }
var body: some ReducerOf<Self> {
Reduce { state, action in
state.count += 1
return .none
}
}
}
// NB: Workaround to XCTest ignoring `@available(...)` attributes.
private func checkAvailability() throws {
guard #available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) else {
throw XCTSkip("Requires iOS 16, macOS 13, tvOS 16, or watchOS 9")
}
}