Skip to content

Commit 931a71b

Browse files
Revamp integration tests (pointfreeco#2503)
* More integration tests. * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * comment out all but one test * wip * try ios 17 * see if this works in ios 16 * wip * wip * wip * bring back tests * wip * wip * wip * fixes * fixes * re-enable tests * wip * Update Sources/ComposableArchitecture/Internal/Logger.swift * wip --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent 77dee2d commit 931a71b

40 files changed

+2825
-980
lines changed

Diff for: .github/workflows/ci.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
name: Library (evolution)
3434
runs-on: macos-13
3535
steps:
36-
- uses: actions/checkout@v3
36+
- uses: actions/checkout@v4
3737
- name: Select Xcode 14.3
3838
run: sudo xcode-select -s /Applications/Xcode_14.3.app
3939
- name: Build for library evolution
@@ -43,7 +43,7 @@ jobs:
4343
name: Benchmarks
4444
runs-on: macos-13
4545
steps:
46-
- uses: actions/checkout@v3
46+
- uses: actions/checkout@v4
4747
- name: Select Xcode 14.3
4848
run: sudo xcode-select -s /Applications/Xcode_14.3.app
4949
- name: Run benchmark
@@ -53,7 +53,7 @@ jobs:
5353
name: Examples
5454
runs-on: macos-13
5555
steps:
56-
- uses: actions/checkout@v3
56+
- uses: actions/checkout@v4
5757
- name: Select Xcode 14.3
5858
run: sudo xcode-select -s /Applications/Xcode_14.3.app
5959
- name: Run tests

Diff for: ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved

+17
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,23 @@
108108
"version" : "1.0.0"
109109
}
110110
},
111+
{
112+
"identity" : "swift-snapshot-testing",
113+
"kind" : "remoteSourceControl",
114+
"location" : "https://github.com/pointfreeco/swift-snapshot-testing.git",
115+
"state" : {
116+
"revision" : "bbc9ec6e41ba964a5d04371af0acfb574cadafbd"
117+
}
118+
},
119+
{
120+
"identity" : "swift-syntax",
121+
"kind" : "remoteSourceControl",
122+
"location" : "https://github.com/apple/swift-syntax.git",
123+
"state" : {
124+
"revision" : "74203046135342e4a4a627476dd6caf8b28fe11b",
125+
"version" : "509.0.0"
126+
}
127+
},
111128
{
112129
"identity" : "swift-tagged",
113130
"kind" : "remoteSourceControl",

Diff for: Examples/Integration/Integration.xcodeproj/project.pbxproj

+131-29
Large diffs are not rendered by default.
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
@_spi(Logging) import ComposableArchitecture
2+
import SwiftUI
3+
4+
struct BasicsView: View {
5+
@State var store = Store(initialState: Feature.State()) {
6+
Feature()
7+
}
8+
9+
var body: some View {
10+
WithViewStore(self.store, observe: { $0 }) { viewStore in
11+
let _ = Logger.shared.log("\(Self.self).body")
12+
Text(viewStore.count.description)
13+
Button("Decrement") { self.store.send(.decrementButtonTapped) }
14+
Button("Increment") { self.store.send(.incrementButtonTapped) }
15+
Button("Dismiss") { self.store.send(.dismissButtonTapped) }
16+
}
17+
}
18+
19+
struct Feature: Reducer {
20+
struct State: Equatable, Identifiable {
21+
let id = UUID()
22+
var count = 0
23+
init(count: Int = 0) {
24+
self.count = count
25+
}
26+
}
27+
enum Action {
28+
case decrementButtonTapped
29+
case dismissButtonTapped
30+
case incrementButtonTapped
31+
}
32+
@Dependency(\.dismiss) var dismiss
33+
var body: some ReducerOf<Self> {
34+
Reduce { state, action in
35+
switch action {
36+
case .decrementButtonTapped:
37+
state.count -= 1
38+
return .none
39+
case .dismissButtonTapped:
40+
return .run { _ in await self.dismiss() }
41+
case .incrementButtonTapped:
42+
state.count += 1
43+
return .none
44+
}
45+
}
46+
}
47+
}
48+
}

Diff for: Examples/Integration/Integration/EnumTestCase.swift

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
@_spi(Logging) import ComposableArchitecture
2+
import SwiftUI
3+
4+
struct EnumView: View {
5+
@State var store = Store(initialState: Feature.State()) {
6+
Feature()
7+
}
8+
9+
struct ViewState: Equatable {
10+
enum Tag { case feature1, feature2, none }
11+
let tag: Tag
12+
init(state: Feature.State) {
13+
switch state.destination {
14+
case .feature1:
15+
self.tag = .feature1
16+
case .feature2:
17+
self.tag = .feature2
18+
case .none:
19+
self.tag = .none
20+
}
21+
}
22+
}
23+
24+
var body: some View {
25+
Form {
26+
WithViewStore(self.store, observe: ViewState.init) { viewStore in
27+
let _ = Logger.shared.log("\(Self.self).body")
28+
Section {
29+
switch viewStore.tag {
30+
case .feature1:
31+
Button("Toggle feature 1 off") {
32+
self.store.send(.toggle1ButtonTapped)
33+
}
34+
Button("Toggle feature 2 on") {
35+
self.store.send(.toggle2ButtonTapped)
36+
}
37+
case .feature2:
38+
Button("Toggle feature 1 on") {
39+
self.store.send(.toggle1ButtonTapped)
40+
}
41+
Button("Toggle feature 2 off") {
42+
self.store.send(.toggle2ButtonTapped)
43+
}
44+
case .none:
45+
Button("Toggle feature 1 on") {
46+
self.store.send(.toggle1ButtonTapped)
47+
}
48+
Button("Toggle feature 2 on") {
49+
self.store.send(.toggle2ButtonTapped)
50+
}
51+
}
52+
}
53+
}
54+
IfLetStore(
55+
self.store.scope(state: \.$destination, action: { .destination($0) }),
56+
state: /Feature.Destination.State.feature1,
57+
action: { .feature1($0) }
58+
) { store in
59+
Section {
60+
BasicsView(store: store)
61+
} header: {
62+
Text("Feature 1")
63+
}
64+
}
65+
IfLetStore(
66+
self.store.scope(state: \.$destination, action: { .destination($0) }),
67+
state: /Feature.Destination.State.feature2,
68+
action: { .feature2($0) }
69+
) { store in
70+
Section {
71+
BasicsView(store: store)
72+
} header: {
73+
Text("Feature 2")
74+
}
75+
}
76+
}
77+
}
78+
79+
struct Feature: Reducer {
80+
struct State: Equatable {
81+
@PresentationState var destination: Destination.State?
82+
}
83+
enum Action {
84+
case destination(PresentationAction<Destination.Action>)
85+
case toggle1ButtonTapped
86+
case toggle2ButtonTapped
87+
}
88+
struct Destination: Reducer {
89+
enum State: Equatable {
90+
case feature1(BasicsView.Feature.State)
91+
case feature2(BasicsView.Feature.State)
92+
}
93+
enum Action {
94+
case feature1(BasicsView.Feature.Action)
95+
case feature2(BasicsView.Feature.Action)
96+
}
97+
var body: some ReducerOf<Self> {
98+
Scope(state: /State.feature1, action: /Action.feature1) {
99+
BasicsView.Feature()
100+
}
101+
Scope(state: /State.feature2, action: /Action.feature2) {
102+
BasicsView.Feature()
103+
}
104+
}
105+
}
106+
var body: some ReducerOf<Self> {
107+
Reduce { state, action in
108+
switch action {
109+
case .destination:
110+
return .none
111+
case .toggle1ButtonTapped:
112+
switch state.destination {
113+
case .feature1:
114+
state.destination = nil
115+
case .feature2:
116+
state.destination = .feature1(BasicsView.Feature.State())
117+
case .none:
118+
state.destination = .feature1(BasicsView.Feature.State())
119+
}
120+
return .none
121+
case .toggle2ButtonTapped:
122+
switch state.destination {
123+
case .feature1:
124+
state.destination = .feature2(BasicsView.Feature.State())
125+
case .feature2:
126+
state.destination = nil
127+
case .none:
128+
state.destination = .feature2(BasicsView.Feature.State())
129+
}
130+
return .none
131+
}
132+
}
133+
.ifLet(\.$destination, action: /Action.destination) {
134+
Destination()
135+
}
136+
}
137+
}
138+
}
139+
140+
struct EnumTestCase_Previews: PreviewProvider {
141+
static var previews: some View {
142+
let _ = Logger.shared.isEnabled = true
143+
EnumView()
144+
}
145+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
@_spi(Logging) import ComposableArchitecture
2+
import SwiftUI
3+
4+
struct IdentifiedListView: View {
5+
@State var store = Store(initialState: Feature.State()) {
6+
Feature()
7+
}
8+
9+
struct ViewState: Equatable {
10+
var firstCount: Int?
11+
init(state: Feature.State) {
12+
self.firstCount = state.rows.first?.count
13+
}
14+
}
15+
16+
var body: some View {
17+
WithViewStore(self.store, observe: ViewState.init) { viewStore in
18+
let _ = Logger.shared.log("\(Self.self).body")
19+
List {
20+
Section {
21+
if let firstCount = viewStore.firstCount {
22+
HStack {
23+
Button("Increment First") {
24+
self.store.send(.incrementFirstButtonTapped)
25+
}
26+
Spacer()
27+
Text("Count: \(firstCount)")
28+
}
29+
}
30+
}
31+
ForEachStore(self.store.scope(state: \.rows, action: { .row(id: $0, action: $1) })) { store in
32+
let _ = Logger.shared.log("\(Self.self).body.ForEachStore")
33+
let idStore = store.scope(state: \.id, action: { $0 })
34+
WithViewStore(idStore, observe: { $0 }) { viewStore in
35+
let _ = Logger.shared.log("\(type(of: idStore))")
36+
Section {
37+
HStack {
38+
VStack {
39+
BasicsView(store: store)
40+
}
41+
Spacer()
42+
Button(action: { self.store.send(.removeButtonTapped(id: viewStore.state)) }) {
43+
Image(systemName: "trash")
44+
}
45+
}
46+
}
47+
.buttonStyle(.borderless)
48+
}
49+
}
50+
}
51+
.toolbar {
52+
ToolbarItem {
53+
Button("Add") { self.store.send(.addButtonTapped) }
54+
}
55+
}
56+
}
57+
}
58+
59+
struct Feature: Reducer {
60+
struct State: Equatable {
61+
var rows: IdentifiedArrayOf<BasicsView.Feature.State> = []
62+
}
63+
enum Action {
64+
case addButtonTapped
65+
case incrementFirstButtonTapped
66+
case removeButtonTapped(id: BasicsView.Feature.State.ID)
67+
case row(id: BasicsView.Feature.State.ID, action: BasicsView.Feature.Action)
68+
}
69+
var body: some ReducerOf<Self> {
70+
Reduce { state, action in
71+
switch action {
72+
case .addButtonTapped:
73+
state.rows.append(BasicsView.Feature.State())
74+
return .none
75+
case .incrementFirstButtonTapped:
76+
state.rows[id: state.rows.ids[0]]?.count += 1
77+
return .none
78+
case let .removeButtonTapped(id: id):
79+
state.rows.remove(id: id)
80+
return .none
81+
case .row:
82+
return .none
83+
}
84+
}
85+
.forEach(\.rows, action: /Action.row) {
86+
BasicsView.Feature()
87+
}
88+
}
89+
}
90+
}

Diff for: Examples/Integration/Integration/Info.plist

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleURLTypes</key>
6+
<array>
7+
<dict>
8+
<key>CFBundleTypeRole</key>
9+
<string>Editor</string>
10+
<key>CFBundleURLIconFile</key>
11+
<string></string>
12+
<key>CFBundleURLName</key>
13+
<string></string>
14+
<key>CFBundleURLSchemes</key>
15+
<array>
16+
<string>integration</string>
17+
</array>
18+
</dict>
19+
</array>
20+
</dict>
21+
</plist>

0 commit comments

Comments
 (0)