Skip to content

Commit 82d2b78

Browse files
authored
UI test to catch SwiftUI regressions (#1815)
* Add some UI tests to catch regressions. * wip * wip
1 parent f2f3a25 commit 82d2b78

File tree

17 files changed

+939
-5
lines changed

17 files changed

+939
-5
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ on:
44
push:
55
branches:
66
- main
7-
- swift-dependencies
87
pull_request:
98
branches:
109
- '*'

ComposableArchitecture.xcworkspace/contents.xcworkspacedata

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Examples/Integration/Integration.xcodeproj/project.pbxproj

Lines changed: 500 additions & 0 deletions
Large diffs are not rendered by default.

Examples/Integration/Integration.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>

Examples/Integration/Integration.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 113 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1420"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "CAA1CAF0296DEE78000665B1"
18+
BuildableName = "Integration.app"
19+
BlueprintName = "Integration"
20+
ReferencedContainer = "container:Integration.xcodeproj">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
</BuildActionEntries>
24+
</BuildAction>
25+
<TestAction
26+
buildConfiguration = "Debug"
27+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
shouldUseLaunchSchemeArgsEnv = "YES">
30+
<Testables>
31+
<TestableReference
32+
skipped = "NO">
33+
<BuildableReference
34+
BuildableIdentifier = "primary"
35+
BlueprintIdentifier = "CAA1CB0A296DEE79000665B1"
36+
BuildableName = "IntegrationUITests.xctest"
37+
BlueprintName = "IntegrationUITests"
38+
ReferencedContainer = "container:Integration.xcodeproj">
39+
</BuildableReference>
40+
</TestableReference>
41+
</Testables>
42+
</TestAction>
43+
<LaunchAction
44+
buildConfiguration = "Debug"
45+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
46+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
47+
launchStyle = "0"
48+
useCustomWorkingDirectory = "NO"
49+
ignoresPersistentStateOnLaunch = "NO"
50+
debugDocumentVersioning = "YES"
51+
debugServiceExtension = "internal"
52+
allowLocationSimulation = "YES">
53+
<BuildableProductRunnable
54+
runnableDebuggingMode = "0">
55+
<BuildableReference
56+
BuildableIdentifier = "primary"
57+
BlueprintIdentifier = "CAA1CAF0296DEE78000665B1"
58+
BuildableName = "Integration.app"
59+
BlueprintName = "Integration"
60+
ReferencedContainer = "container:Integration.xcodeproj">
61+
</BuildableReference>
62+
</BuildableProductRunnable>
63+
</LaunchAction>
64+
<ProfileAction
65+
buildConfiguration = "Release"
66+
shouldUseLaunchSchemeArgsEnv = "YES"
67+
savedToolIdentifier = ""
68+
useCustomWorkingDirectory = "NO"
69+
debugDocumentVersioning = "YES">
70+
<BuildableProductRunnable
71+
runnableDebuggingMode = "0">
72+
<BuildableReference
73+
BuildableIdentifier = "primary"
74+
BlueprintIdentifier = "CAA1CAF0296DEE78000665B1"
75+
BuildableName = "Integration.app"
76+
BlueprintName = "Integration"
77+
ReferencedContainer = "container:Integration.xcodeproj">
78+
</BuildableReference>
79+
</BuildableProductRunnable>
80+
</ProfileAction>
81+
<AnalyzeAction
82+
buildConfiguration = "Debug">
83+
</AnalyzeAction>
84+
<ArchiveAction
85+
buildConfiguration = "Release"
86+
revealArchiveInOrganizer = "YES">
87+
</ArchiveAction>
88+
</Scheme>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"colors" : [
3+
{
4+
"idiom" : "universal"
5+
}
6+
],
7+
"info" : {
8+
"author" : "xcode",
9+
"version" : 1
10+
}
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "universal",
5+
"platform" : "ios",
6+
"size" : "1024x1024"
7+
}
8+
],
9+
"info" : {
10+
"author" : "xcode",
11+
"version" : 1
12+
}
13+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import ComposableArchitecture
2+
import SwiftUI
3+
4+
struct ForEachBindingTestCase: ReducerProtocol {
5+
struct State: Equatable {
6+
var values = ["A", "B", "C"]
7+
}
8+
enum Action {
9+
case change(offset: Int, value: String)
10+
case removeLast
11+
}
12+
13+
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
14+
switch action {
15+
case let .change(offset: offset, value: value):
16+
state.values[offset] = value
17+
return .none
18+
19+
case .removeLast:
20+
guard !state.values.isEmpty
21+
else { return .none }
22+
state.values.removeLast()
23+
return .none
24+
}
25+
}
26+
}
27+
28+
struct ForEachBindingTestCaseView: View {
29+
let store: StoreOf<ForEachBindingTestCase>
30+
31+
var body: some View {
32+
WithViewStore(self.store, observe: { $0 }) { viewStore in
33+
VStack { // ⚠️ Must use VStack, not List.
34+
ForEach(Array(viewStore.values.enumerated()), id: \.offset) { offset, value in
35+
HStack { // ⚠️ Must wrap in an HStack.
36+
TextField( // ⚠️ Must use a TextField.
37+
"\(value)",
38+
text: viewStore.binding(
39+
get: { $0.values[offset] },
40+
send: { .change(offset: offset, value: $0) }
41+
)
42+
)
43+
}
44+
}
45+
}
46+
.toolbar {
47+
ToolbarItem {
48+
Button("Remove last") {
49+
viewStore.send(.removeLast)
50+
}
51+
}
52+
}
53+
}
54+
}
55+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import ComposableArchitecture
2+
import SwiftUI
3+
4+
@main
5+
struct IntegrationApp: App {
6+
@State var isNavigationStackBindingTestCasePresented = false
7+
8+
var body: some Scene {
9+
WindowGroup {
10+
NavigationStack {
11+
List {
12+
NavigationLink("ForEachBindingTestCase") {
13+
ForEachBindingTestCaseView(
14+
store: Store(
15+
initialState: ForEachBindingTestCase.State(),
16+
reducer: ForEachBindingTestCase()
17+
)
18+
)
19+
}
20+
21+
Button("NavigationStackBindingTestCase") {
22+
self.isNavigationStackBindingTestCasePresented = true
23+
}
24+
.sheet(isPresented: self.$isNavigationStackBindingTestCasePresented) {
25+
NavigationStackBindingTestCaseView(
26+
store: Store(
27+
initialState: NavigationStackBindingTestCase.State(),
28+
reducer: NavigationStackBindingTestCase()
29+
)
30+
)
31+
}
32+
}
33+
}
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)