Skip to content

Commit 9579c84

Browse files
committed
Allow RenderTester to recover and continue from missing AnyScreen-producing child workflows
1 parent fb20bb1 commit 9579c84

File tree

4 files changed

+97
-3
lines changed

4 files changed

+97
-3
lines changed

Package.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,14 @@ let package = Package(
9999
),
100100
.target(
101101
name: "WorkflowTesting",
102-
dependencies: ["Workflow"],
102+
dependencies: ["Workflow", "WorkflowUI"],
103103
path: "WorkflowTesting/Sources"
104104
),
105+
.testTarget(
106+
name: "WorkflowTestingTests",
107+
dependencies: ["Workflow", "WorkflowTesting", "WorkflowUI"],
108+
path: "WorkflowTesting/Tests"
109+
),
105110
.target(
106111
name: "WorkflowReactiveSwiftTesting",
107112
dependencies: ["WorkflowReactiveSwift", "WorkflowTesting"],

WorkflowTesting/Sources/Internal/RenderTester+TestContext.swift

+14-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
import XCTest
2020
@testable import Workflow
2121

22+
#if canImport(UIKit) && canImport(WorkflowUI)
23+
import WorkflowUI
24+
#endif
25+
2226
extension RenderTester {
2327
internal final class TestContext: RenderContextType {
2428
var state: WorkflowType.State
@@ -68,10 +72,18 @@
6872

6973
// We can “recover” from missing Void-rendering workflows since there’s only one possible value to return
7074
if Child.Rendering.self == Void.self {
71-
// Couldn’t find a nicer way to do this polymorphically
7275
return () as! Child.Rendering
7376
}
74-
fatalError("Unable to continue.")
77+
78+
#if canImport(UIKit) && canImport(WorkflowUI)
79+
// We can "recover" from missing AnyScreen-rendering workflows since they render an opaque type that we can construct a value of
80+
if Child.Rendering.self == AnyScreen.self {
81+
return RenderTesterPlaceholderScreen().asAnyScreen() as! Child.Rendering
82+
}
83+
#endif
84+
85+
// At this point, without an return value from an expectation, we have nothing to return from render and are unable to continue
86+
fatalError("Unable to continue; no expectation has given RenderTester a value to return from `render`.")
7587
}
7688
let (inserted, _) = usedWorkflowKeys.insert(WorkflowKey(type: ObjectIdentifier(Child.self), key: key))
7789
if !inserted {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2020 Square Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#if DEBUG && canImport(UIKit) && canImport(WorkflowUI)
18+
19+
import WorkflowUI
20+
import XCTest
21+
22+
/// Used as the stand-in value returned by RenderTester when an AnyScreen is expected but not provided
23+
struct RenderTesterPlaceholderScreen: Screen {
24+
func viewControllerDescription(environment: ViewEnvironment) -> ViewControllerDescription {
25+
ViewControllerDescription(
26+
type: UIViewController.self,
27+
build: {
28+
XCTFail("Unexpected construction of screen in RenderTester")
29+
return UIViewController()
30+
},
31+
update: { _ in }
32+
)
33+
}
34+
}
35+
36+
#endif

WorkflowTesting/Tests/WorkflowRenderTesterFailureTests.swift

+41
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@
1616

1717
import Workflow
1818
import WorkflowTesting
19+
import WorkflowUI
1920
import XCTest
2021

22+
#if canImport(UIKit)
23+
import UIKit
24+
#endif
25+
2126
/// Who tests the tester?
2227
///
2328
/// WorkflowRenderTesterFailureTests does.
@@ -132,6 +137,42 @@ final class WorkflowRenderTesterFailureTests: XCTestCase {
132137
}
133138
}
134139

140+
func test_childWorkflow_unexpected_anyScreenRendering() {
141+
#if canImport(UIKit)
142+
struct MyScreen: Screen {
143+
func viewControllerDescription(environment: ViewEnvironment) -> ViewControllerDescription {
144+
return ViewControllerDescription(
145+
type: UIViewController.self,
146+
build: { UIViewController() },
147+
update: { _ in }
148+
)
149+
}
150+
}
151+
152+
struct MyChildWorkflow: Workflow {
153+
typealias State = Void
154+
func render(state: State, context: RenderContext<Self>) -> AnyScreen {
155+
return MyScreen().asAnyScreen()
156+
}
157+
}
158+
159+
struct MyWorkflow: Workflow {
160+
typealias State = Void
161+
func render(state: State, context: RenderContext<Self>) -> AnyScreen {
162+
let childRendering = MyChildWorkflow().rendered(in: context)
163+
return childRendering
164+
}
165+
}
166+
167+
let tester = MyWorkflow()
168+
.renderTester()
169+
170+
expectingFailure(#"Unexpected workflow of type MyChildWorkflow with key """#) {
171+
tester.render { _ in }
172+
}
173+
#endif
174+
}
175+
135176
func test_childWorkflowMultipleRenders_sameKey() {
136177
let tester = ParentWorkflow()
137178
.renderTester()

0 commit comments

Comments
 (0)