Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp integration tests #2503

Merged
merged 31 commits into from Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 105 additions & 29 deletions Examples/Integration/Integration.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions Examples/Integration/Integration/BasicsTestCase.swift
@@ -0,0 +1,48 @@
@_spi(Logging) import ComposableArchitecture
import SwiftUI

struct BasicsView: View {
@State var store = Store(initialState: Feature.State()) {
Feature()
}

var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
let _ = Logger.shared.log("\(Self.self).body")
Text(viewStore.count.description)
Button("Decrement") { self.store.send(.decrementButtonTapped) }
Button("Increment") { self.store.send(.incrementButtonTapped) }
Button("Dismiss") { self.store.send(.dismissButtonTapped) }
}
}

struct Feature: Reducer {
struct State: Equatable, Identifiable {
let id = UUID()
var count = 0
init(count: Int = 0) {
self.count = count
}
}
enum Action {
case decrementButtonTapped
case dismissButtonTapped
case incrementButtonTapped
}
@Dependency(\.dismiss) var dismiss
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .decrementButtonTapped:
state.count -= 1
return .none
case .dismissButtonTapped:
return .run { _ in await self.dismiss() }
case .incrementButtonTapped:
state.count += 1
return .none
}
}
}
}
}
142 changes: 142 additions & 0 deletions Examples/Integration/Integration/EnumTestCase.swift
@@ -0,0 +1,142 @@
@_spi(Logging) import ComposableArchitecture
import SwiftUI

struct EnumView: View {
@State var store = Store(initialState: Feature.State()) {
Feature()
}

struct ViewState: Equatable {
enum Tag { case feature1, feature2, none }
let tag: Tag
init(state: Feature.State) {
switch state.destination {
case .feature1:
self.tag = .feature1
case .feature2:
self.tag = .feature2
case .none:
self.tag = .none
}
}
}

var body: some View {
Form {
WithViewStore(self.store, observe: ViewState.init) { viewStore in
let _ = Logger.shared.log("\(Self.self).body")
Section {
switch viewStore.tag {
case .feature1:
Button("Toggle feature 1 off") {
self.store.send(.toggle1ButtonTapped)
}
Button("Toggle feature 2 on") {
self.store.send(.toggle2ButtonTapped)
}
case .feature2:
Button("Toggle feature 1 on") {
self.store.send(.toggle1ButtonTapped)
}
Button("Toggle feature 2 off") {
self.store.send(.toggle2ButtonTapped)
}
case .none:
Button("Toggle feature 1 on") {
self.store.send(.toggle1ButtonTapped)
}
Button("Toggle feature 2 on") {
self.store.send(.toggle2ButtonTapped)
}
}
}
}
}
IfLetStore(
self.store.scope(state: \.$destination, action: { .destination($0) }),
state: /Feature.Destination.State.feature1,
action: { .feature1($0) }
) { store in
Section {
Form {
BasicsView(store: store)
}
} header: {
Text("Feature 1")
}
}
IfLetStore(
self.store.scope(state: \.$destination, action: { .destination($0) }),
state: /Feature.Destination.State.feature2,
action: { .feature2($0) }
) { store in
Section {
Form {
BasicsView(store: store)
}
} header: {
Text("Feature 2")
}
}
}

struct Feature: Reducer {
struct State: Equatable {
@PresentationState var destination: Destination.State?
}
enum Action {
case destination(PresentationAction<Destination.Action>)
case toggle1ButtonTapped
case toggle2ButtonTapped
}
struct Destination: Reducer {
enum State: Equatable {
case feature1(BasicsView.Feature.State)
case feature2(BasicsView.Feature.State)
}
enum Action {
case feature1(BasicsView.Feature.Action)
case feature2(BasicsView.Feature.Action)
}
var body: some ReducerOf<Self> {
Scope(state: /State.feature1, action: /Action.feature1) {
BasicsView.Feature()
}
Scope(state: /State.feature2, action: /Action.feature2) {
BasicsView.Feature()
}
}
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .destination:
return .none
case .toggle1ButtonTapped:
switch state.destination {
case .feature1:
state.destination = nil
case .feature2:
state.destination = .feature1(BasicsView.Feature.State())
case .none:
state.destination = .feature1(BasicsView.Feature.State())
}
return .none
case .toggle2ButtonTapped:
switch state.destination {
case .feature1:
state.destination = .feature2(BasicsView.Feature.State())
case .feature2:
state.destination = nil
case .none:
state.destination = .feature2(BasicsView.Feature.State())
}
return .none
}
}
.ifLet(\.$destination, action: /Action.destination) {
Destination()
}
}
}
}
86 changes: 86 additions & 0 deletions Examples/Integration/Integration/IdentifiedListTestCase.swift
@@ -0,0 +1,86 @@
@_spi(Logging) import ComposableArchitecture
import SwiftUI

struct IdentifiedListView: View {
@State var store = Store(initialState: Feature.State()) {
Feature()
}

struct ViewState: Equatable {
var firstCount: Int?
init(state: Feature.State) {
self.firstCount = state.rows.first?.count
}
}

var body: some View {
WithViewStore(self.store, observe: ViewState.init) { viewStore in
List {
Section {
if let firstCount = viewStore.firstCount {
HStack {
Button("Increment First") {
self.store.send(.incrementFirstButtonTapped)
}
Spacer()
Text("Count: \(firstCount)")
}
}
}
ForEachStore(self.store.scope(state: \.rows, action: { .row(id: $0, action: $1) })) { store in
WithViewStore(store.scope(state: \.id, action: { $0 }), observe: { $0 }) { viewStore in
Section {
HStack {
VStack {
BasicsView(store: store)
}
Spacer()
Button(action: { self.store.send(.removeButtonTapped(id: viewStore.state)) }) {
Image(systemName: "trash")
}
}
}
.buttonStyle(.borderless)
}
}
}
.toolbar {
ToolbarItem {
Button("Add") { self.store.send(.addButtonTapped) }
}
}
}
}

struct Feature: Reducer {
struct State: Equatable {
var rows: IdentifiedArrayOf<BasicsView.Feature.State> = []
}
enum Action {
case addButtonTapped
case incrementFirstButtonTapped
case removeButtonTapped(id: BasicsView.Feature.State.ID)
case row(id: BasicsView.Feature.State.ID, action: BasicsView.Feature.Action)
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .addButtonTapped:
state.rows.append(BasicsView.Feature.State())
return .none
case .incrementFirstButtonTapped:
state.rows[id: state.rows.ids[0]]?.count += 1
return .none
case let .removeButtonTapped(id: id):
state.rows.remove(id: id)
return .none
case .row:
return .none
}
}
.forEach(\.rows, action: /Action.row) {
BasicsView.Feature()
}
}
}
}
21 changes: 21 additions & 0 deletions Examples/Integration/Integration/Info.plist
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLIconFile</key>
<string></string>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>integration</string>
</array>
</dict>
</array>
</dict>
</plist>