profile
viewpoint

bencochran/bezelhud 44

Quicksilver Interface - Updated for Snow Leopard

bencochran/GNToggleBar 30

Custom iPhone toggle bar control. Like a UITabBar but… not. (This is very old. I don’t even know if it still works. So you should probably just move along…)

bencochran/gnarus 29

Augmented reality on the iPhone

bencochran/Kaleidoscope 22

Simple programming language written in Swift and compiled with LLVM

bencochran/CFPropertyList 18

Python module for reading/writing binary and XML property lists.

bencochran/iphonearkit 8

An Objective-C augmented reality kit for iPhone.

bencochran/gnarly 7

Gnarus server

bencochran/disciple 2

A python-based website that follows a set of github repositories...

push eventbencochran/qmk_firmware

Ben Cochran

commit sha 833f5e3500518429f9273376a88c0eaeb8132388

Turn off LEDs when usb is suspended

view details

Ben Cochran

commit sha c67bc233c23ead6eb59319e40008edc79fa58085

Sleep button

view details

Ben Cochran

commit sha 1b86ba6d21236cbd4a53d554ad1aeca4f895d347

Caps lock is escape

view details

Ben Cochran

commit sha 9674ba417da1a70500a486e49bbb8d9819def17b

Red sleep button

view details

Ben Cochran

commit sha 610b4fad6f88c3d1a211b77aa0dab1a69dcca1ce

Warmer white backlight

view details

push time in 2 days

Pull request review commentsquare/Blueprint

Label measuring and other enhancements

 public struct AttributedLabel: Element {      public var attributedText: NSAttributedString     public var numberOfLines: Int = 0-    /// The scale to which pixel measurements will be rounded. Defaults to `UIScreen.main.scale`.-    public var roundingScale: CGFloat = UIScreen.main.scale+    public var isAccessibilityElement = false++    /// An offset that will be applied to the rect used by `drawText(in:)`.+    ///+    /// This can be used to adjust the positioning of text within each line's frame, such as adjusting+    /// the way text is distributed within the line height.+    public var textRectOffset: CGPoint = .zero      public init(attributedText: NSAttributedString) {         self.attributedText = attributedText     }      public var content: ElementContent {         struct Measurer: Measurable {+            private static let prototypeLabel = LabelView() -            var attributedText: NSAttributedString-            var roundingScale: CGFloat+            var model: AttributedLabel              func measure(in constraint: SizeConstraint) -> CGSize {-                var size = attributedText.boundingRect(-                    with: constraint.maximum,-                    options: [.usesLineFragmentOrigin],-                    context: nil)-                    .size-                size.width = size.width.rounded(.up, by: roundingScale)-                size.height = size.height.rounded(.up, by: roundingScale)--                return size+                let label = Self.prototypeLabel+                model.update(label: label)+                return label.sizeThatFits(constraint.maximum)             }         } -        return ElementContent(measurable: Measurer(attributedText: attributedText, roundingScale: roundingScale))+        return ElementContent(measurable: Measurer(model: self))+    }++    private func update(label: LabelView) {+        label.attributedText = attributedText+        label.numberOfLines = numberOfLines+        label.isAccessibilityElement = isAccessibilityElement+        label.textRectOffset = textRectOffset     }      public func backingViewDescription(bounds: CGRect, subtreeExtent: CGRect?) -> ViewDescription? {-        return UILabel.describe { (config) in-            config[\.attributedText] = attributedText-            config[\.numberOfLines] = numberOfLines+        return LabelView.describe { (config) in+            config.apply(update)         }     }+} +extension AttributedLabel {+    private final class LabelView: UILabel {

Why wouldn’t something like MarketLabel be built on top of AttributedLabel rather than the underlying view?

watt

comment created time in 3 days

Pull request review commentsquare/workflow-swift

Changelog v1.0.0.alpha.1

 class SignalTests: XCTestCase {         host.update(workflow: SignalTestWorkflow(signal: signalTwo))         wait(for: [expectation], timeout: 1)     }++    func test_deprecated_signalWorker() {}

👀

dhavalshreyas

comment created time in 3 days

Pull request review commentsquare/workflow-swift

Changelog v1.0.0.alpha.1

 Change Log ========== +## Version 1.0.0-alpha.1++_2020-07-07_++* Breaking: `Worker`s have moved from the core `Workflow` library to `WorkflowReactiveSwift`. (#12)+  * You will need to add that as a dependency and import that module in any files with `Worker`s.+* Breaking: `Worker`s are now keyed based on type. ([workflow/1021](https://github.com/square/workflow/issues/1021))+  * If you were running multiple `Worker`s of the same type on a `render` call, please use a `key` while running it: `MyWorker().running(in: context, key: "key")`+* `Swift` and [`Kotlin`](https://github.com/square/workflow-kotlin) libraries now live in seperate repos.+* `Worker`s are now built on a more fundamental concept called `SideEffect`.  ([workflow/1021](https://github.com/square/workflow/issues/1021))+* Introduce `WorkflowRxSwift` to build a `RxSwift` `Worker`. (#27)+* Several APIs have been updated to be consistent. The older APIs have been deprecated and will be removed from the `beta`. (#9)+  * `MyWorkflow().rendered(with: context)` to `MyWorkflow().rendered(in: context)`+  * `context.awaitResult(for: MyWorker())` to `MyWorker().running(in: context)`+* Introduce new `RenderTester` API. (#15)

Maybe also mention here that the old APIs will be removed in beta

dhavalshreyas

comment created time in 3 days

push eventsquare/workflow-swift

Aidan Follestad

commit sha f3636135cb96c3f0aa055045324c99968092cdba

Fix two typos in Tutorial5.md

view details

Ben Cochran

commit sha 13575c3a8b48d9ce0e100097f2d83c4d534f8b77

Merge pull request #35 from afollestad/patch-1 Fix two typos in Tutorial5.md

view details

push time in 7 days

PR merged square/workflow-swift

Fix two typos in Tutorial5.md

Checklist

  • [x] Unit Tests
  • [x] UI Tests
  • [x] Snapshot Tests (iOS only)
  • [x] I have made corresponding changes to the documentation
+2 -2

1 comment

1 changed file

afollestad

pr closed time in 7 days

Pull request review commentsquare/workflow-swift

Do not use @testable import Workflow in WorkflowReactiveSwiftTests

 class WorkerTests: XCTestCase {     // A worker declared on a first `render` pass that is not on a subsequent should have the work cancelled.     func test_cancelsWorkers() {         struct WorkerWorkflow: Workflow {-            var startExpectation: XCTestExpectation-            var endExpectation: XCTestExpectation-             enum State {                 case notWorking-                case working+                case working(start: XCTestExpectation, end: XCTestExpectation)+            }++            let initialState: State++            func makeInitialState() -> State {+                initialState             } -            func makeInitialState() -> WorkerWorkflow.State {-                return .notWorking+            func workflowDidChange(from previousWorkflow: WorkerWorkflow, state: inout State) {+                state = initialState

Hah, it happens 👍

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Do not use @testable import Workflow in WorkflowReactiveSwiftTests

 class WorkerTests: XCTestCase {     // A worker declared on a first `render` pass that is not on a subsequent should have the work cancelled.     func test_cancelsWorkers() {         struct WorkerWorkflow: Workflow {-            var startExpectation: XCTestExpectation-            var endExpectation: XCTestExpectation-             enum State {                 case notWorking-                case working+                case working(start: XCTestExpectation, end: XCTestExpectation)+            }++            let initialState: State++            func makeInitialState() -> State {+                initialState             } -            func makeInitialState() -> WorkerWorkflow.State {-                return .notWorking+            func workflowDidChange(from previousWorkflow: WorkerWorkflow, state: inout State) {+                state = initialState

Any reason to use State here at all? (rather than just having that enum be a property on the workflow and using Void for state?)

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Make Signal AnyWorkflowConvertible

 import ReactiveSwift import Workflow import class Workflow.Lifetime +/// Convenience to use `SignalProducer` as a `Workflow`+///+/// `Output` of this `Workflow` can be mapped to a `WorkflowAction`.+///+/// - Important:+/// In a `render()` call, if running multiple `SignalProducer`s+/// or if a `SignalProducer` can change in-between render passes,+/// use a `Worker` instead or use an explicit `key` while `running`.+///+/// ```+/// func render(state: State, context: RenderContext<Self>) -> MyScreen {+///     signalProducer+///     .mapOutput { MyAction($0) }+///     .running(in: context, key: "UniqueKeyForSignal")

very nit: indent the method calls here and on the Signal one

otherwise looks great, thanks!

dhavalshreyas

comment created time in 8 days

push eventsquare/workflow-swift

Ben Cochran

commit sha c8061049dd8fbe4f2fad3a40c7e1d36cf4a95d93

Recover unexpected void-rendering workflows in RenderTester

view details

Ben Cochran

commit sha fa68a9f4edfe715d56d3999b18fd0dbd02459dc5

Add back test_worker_unexpected

view details

Ben Cochran

commit sha 2c550c317c22c042864f865d5638ebdee109bf1d

Merge pull request #28 from square/bc/render-tester-recovery Recover from unexpected void-rendering workflows in RenderTester

view details

push time in 8 days

delete branch square/workflow-swift

delete branch : bc/render-tester-recovery

delete time in 8 days

PR merged square/workflow-swift

Reviewers
Recover from unexpected void-rendering workflows in RenderTester

Void only has a single value, so it’s reasonable/possible for RenderTester’s RenderContext to return a rendering for unexpected workflows that render Void.

Notably, this allows us to fail but continue testing when we encounter an unexpected Worker (and thus we can bring back test_worker_unexpected).

+52 -12

0 comment

3 changed files

bencochran

pr closed time in 8 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 31790d96f5adc0814030c29f79ccdffd4dde68d2

Get rid of warnings across the project

view details

Ben Cochran

commit sha 113e541762871305edb8e67a346bfebac0931508

Merge pull request #29 from square/bc/silence-deprecations Get rid of warnings across the project

view details

Ben Cochran

commit sha c8061049dd8fbe4f2fad3a40c7e1d36cf4a95d93

Recover unexpected void-rendering workflows in RenderTester

view details

Ben Cochran

commit sha fa68a9f4edfe715d56d3999b18fd0dbd02459dc5

Add back test_worker_unexpected

view details

push time in 8 days

Pull request review commentsquare/workflow-swift

Add os_log logging to Workers

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import os.signpost++private extension OSLog {+    static let worker = OSLog(subsystem: "com.squareup.WorkflowReactiveSwift", category: "Worker")+}++// MARK: -++/// Logs Worker events to OSLog+final class WorkerLogger<WorkerType: Worker> {+    init() {}++    @available(iOS 12.0, macOS 10.14, *)+    var signpostID: OSSignpostID { OSSignpostID(log: .worker, object: self) }++    // MARK: - Workers++    func logStarted() {+        if #available(iOS 12.0, macOS 10.14, *) {+            os_signpost(+                .begin,+                log: .worker,+                name: "Running",+                signpostID: self.signpostID,+                "Worker: %{public}@",

Oops, totally missed that it was the type not the value.

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Recover from unexpected void-rendering workflows in RenderTester

                         diagnosticMessage = ""                     }                     XCTFail("Unexpected workflow of type \(Child.self) with key \"\(key)\". \(diagnosticMessage)", file: file, line: line)++                    // We can “recover” from missing Void-rendering workflows since there’ss only one possible value to return

😅

bencochran

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Recover from unexpected void-rendering workflows in RenderTester

 class WorkflowReactiveSwiftTestingTests: XCTestCase {         }     } +    func test_worker_unexpected() {+        let tester = TestWorkflow()+            .renderTester(initialState: .init(mode: .worker(input: "test"), output: ""))++        expectingFailure(#"Unexpected workflow of type WorkerWorkflow<TestWorker> with key """#) {
bencochran

comment created time in 8 days

delete branch square/workflow-swift

delete branch : bc/silence-deprecations

delete time in 8 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 31790d96f5adc0814030c29f79ccdffd4dde68d2

Get rid of warnings across the project

view details

Ben Cochran

commit sha 113e541762871305edb8e67a346bfebac0931508

Merge pull request #29 from square/bc/silence-deprecations Get rid of warnings across the project

view details

push time in 8 days

PR merged square/workflow-swift

Reviewers
Get rid of warnings across the project

Mostly, mark tests that use deprecated things as themselves deprecated. Also migrated one missing thing thing off of deprecated methods.

+6 -4

0 comment

4 changed files

bencochran

pr closed time in 8 days

Pull request review commentsquare/workflow-swift

Introduce WorkflowRxSwift

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import RxSwift+import WorkflowRxSwiftTesting+import WorkflowTesting+import XCTest+@testable import Workflow+@testable import WorkflowRxSwift++class SignalProducerTests: XCTestCase {

Name’s wrong here

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Introduce WorkflowRxSwift

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import Foundation+import RxSwift+import WorkflowRxSwift+import WorkflowRxSwiftTesting+import WorkflowTesting+import XCTest+@testable import Workflow

I’d avoid @testable importing a module other than the one under test (i.e. treat this module as a third-party module that can’t access internals of Workflow). Can we use WorkflowTesting to help us here?

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Introduce WorkflowRxSwift

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import RxSwift+import WorkflowRxSwiftTesting+import WorkflowTesting+import XCTest+@testable import Workflow+@testable import WorkflowRxSwift++class SignalProducerTests: XCTestCase {+    func test_signalProducerWorkflow_usesSideEffectWithKey() {+        let observable = Observable.just(1)+        ObservableWorkflow(observable: observable)+            .renderTester()+            .expectSideEffect(key: "")+            .render { _ in }+    }++    func test_output() {+        let observable = Observable.just(1)++        let host = WorkflowHost(+            workflow: ObservableWorkflow(observable: observable)+        )++        let expectation = XCTestExpectation()+        var outputValue: Int?+        let disposable = host.output.signal.observeValues { output in+            outputValue = output+            expectation.fulfill()+        }++        wait(for: [expectation], timeout: 1)+        XCTAssertEqual(1, outputValue)++        disposable?.dispose()+    }++    func test_multipleOutputs() {+        let observable = Observable.from([1, 2, 3])++        let host = WorkflowHost(+            workflow: ObservableWorkflow(observable: observable)+        )++        let expectation = XCTestExpectation()+        var outputValues = [Int]()+        let disposable = host.output.signal.observeValues { output in+            outputValues.append(output)+            expectation.fulfill()+        }++        wait(for: [expectation], timeout: 1)+        XCTAssertEqual([1, 2, 3], outputValues)++        disposable?.dispose()+    }++    func test_signalProducer_isDisposedIfNotUsedInWorkflow() {+        let expectation = XCTestExpectation(description: "SignalProducer should be disposed if no longer used.")

Observable not SignalProducer

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Introduce WorkflowRxSwift

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import os.signpost++private extension OSLog {+    static let worker = OSLog(subsystem: "com.squareup.WorkflowRxSwift", category: "Worker")+}++// MARK: -++/// Logs Worker events to OSLog+final class WorkerLogger<WorkerType: Worker> {+    init() {}++    @available(iOS 12.0, *)+    var signpostID: OSSignpostID { OSSignpostID(log: .worker, object: self) }++    // MARK: - Workers++    func logStarted() {+        if #available(iOS 12.0, macOS 10.14, *) {+            os_signpost(+                .begin,+                log: .worker,+                name: "Running",+                signpostID: self.signpostID,+                "Worker: %{public}@",

Same question as here

dhavalshreyas

comment created time in 8 days

PR opened square/workflow-swift

Reviewers
Get rid of warnings across the project

Mostly, mark tests that use deprecated things as themselves deprecated. Also migrated one missing thing thing off of deprecated methods.

+6 -4

0 comment

4 changed files

pr created time in 8 days

create barnchsquare/workflow-swift

branch : bc/silence-deprecations

created branch time in 8 days

Pull request review commentsquare/workflow-swift

Add os_log logging to Workers

 struct WorkerWorkflow<WorkerType: Worker>: Workflow {     }      func render(state: State, context: RenderContext<WorkerWorkflow>) -> Rendering {+        let logger = WorkerLogger<WorkerType>()+         // Start with Void to ensure `worker.run()` is called only once for a given key         SignalProducer(value: ())-            .flatMap(.latest) { self.worker.run() }+            .flatMap(.latest) {+                logger.logStarted()

Like maybe an on(started:) hanging off the .run()?

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Add os_log logging to Workers

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import os.signpost++private extension OSLog {+    static let worker = OSLog(subsystem: "com.squareup.WorkflowReactiveSwift", category: "Worker")+}++// MARK: -++/// Logs Worker events to OSLog+final class WorkerLogger<WorkerType: Worker> {+    init() {}++    @available(iOS 12.0, macOS 10.14, *)+    var signpostID: OSSignpostID { OSSignpostID(log: .worker, object: self) }++    // MARK: - Workers++    func logStarted() {+        if #available(iOS 12.0, macOS 10.14, *) {+            os_signpost(+                .begin,+                log: .worker,+                name: "Running",+                signpostID: self.signpostID,+                "Worker: %{public}@",

Should we consider these non-public? If I were implementing a Worker to do login, I’d likely put a username & password into the properties of that Worker.

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Add os_log logging to Workers

 struct WorkerWorkflow<WorkerType: Worker>: Workflow {     }      func render(state: State, context: RenderContext<WorkerWorkflow>) -> Rendering {+        let logger = WorkerLogger<WorkerType>()+         // Start with Void to ensure `worker.run()` is called only once for a given key         SignalProducer(value: ())-            .flatMap(.latest) { self.worker.run() }+            .flatMap(.latest) {+                logger.logStarted()

Any way we can avoid side-effects in a flatMap here? It’s mostly pedantic in this scenario but I really try and avoid side-effects in map/flatMap.

dhavalshreyas

comment created time in 8 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 84f033ede278c188b13bc73776b5a2498a3e995a

Recover unexpected void-rendering workflows in RenderTester

view details

Ben Cochran

commit sha 666e3919a21092d02d7ebb0b2cecc163518d0f0d

Add back test_worker_unexpected

view details

push time in 8 days

Pull request review commentsquare/workflow-swift

Recover from unexpected void-rendering workflows in RenderTester

         }     } +    func defaultRendering<Child>(for workflowType: Child.Type) -> Child.Rendering? where Child: Workflow {

Oh whoops, these were my failed attempts at avoiding the () as! Child.Rendering. Removing.

bencochran

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Make Signal AnyWorkflowConvertible

  */  import ReactiveSwift+import Workflow++extension Signal: AnyWorkflowConvertible where Error == Never {+    public func asAnyWorkflow() -> AnyWorkflow<Void, Value> {+        return SignalProducerWorkflow(signalProducer: SignalProducer(self)).asAnyWorkflow()+    }+}  /// A `Worker` that wraps a `Signal`+@available(*, deprecated, message: "Use `Signal` as `Workflow` instead`")

extreme nit: extra ` in the message

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Make Signal AnyWorkflowConvertible

  */  import ReactiveSwift+import Workflow++extension Signal: AnyWorkflowConvertible where Error == Never {

Might be good to give some documentation here (and probably on SignalProducer too) with an example of calling .running(in:) directly on a signal.

dhavalshreyas

comment created time in 8 days

Pull request review commentsquare/workflow-swift

Make Signal AnyWorkflowConvertible

 public struct SignalWorker<Key: Equatable, Value>: Worker { }  extension Signal where Error == Never {+    @available(*, deprecated, message: "Use `Signal` as `Workflow` instead`")

extra ` here too

dhavalshreyas

comment created time in 8 days

PR opened square/workflow-swift

Reviewers
Recover from unexpected void-rendering workflows in RenderTester

Void only has a single value, so it’s reasonable/possible for RenderTester’s RenderContext to return a rendering for unexpected workflows that render Void.

Notably, this allows us to fail but continue testing when we encounter an unexpected Worker (and thus we can bring back test_worker_unexpected).

+74 -12

0 comment

3 changed files

pr created time in 8 days

create barnchsquare/workflow-swift

branch : bc/render-tester-recovery

created branch time in 8 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 final class WorkflowLogger {             os_signpost(.end, log: .workflow, name: "Render", signpostID: signpostID)         }     }--    // MARK: - Workers--    static func logWorkerStartedRunning<WorkerType>(ref: AnyObject, workerType: WorkerType.Type) {

Oh yeah, it would be nice to log the start and end of side-effects for sure

dhavalshreyas

comment created time in 8 days

pull request commentsquare/workflow-swift

Allow updating the root workflow in a ContainerViewController

CLA bot looks stuck on this one…

bencochran

comment created time in 9 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 90b51ded10a5c12d9eb77e2aded0daa6a0f1a611

Update WorkflowActionTester to match style of WorkflowRenderTester * `assert` -> `verify * Pull `output` verification into its own method

view details

Ben Cochran

commit sha b8457eec7ccad6b318644882536c1c5d9b89702a

Merge pull request #17 from square/bc/new-action-tester-api Update WorkflowActionTester to match style of WorkflowRenderTester

view details

push time in 9 days

delete branch square/workflow-swift

delete branch : bc/new-action-tester-api

delete time in 9 days

PR merged square/workflow-swift

Reviewers
Update WorkflowActionTester to match style of WorkflowRenderTester
  • assert -> verify
  • Pull output verification into its own method

Checklist

  • [X] Unit Tests
  • [ ] ~UI Tests~
  • [ ] ~Snapshot Tests (iOS only)~
  • [x] I have made corresponding changes to the documentation
+344 -285

0 comment

12 changed files

bencochran

pr closed time in 9 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 2d6f57c5f5febdbd8ad44a59daf7624272bb8a5a

Make sure deprecated expectation failures get attributed to the correct file/line

view details

Ben Cochran

commit sha 1652ee16593d195ff53071d4b127de15e06ce169

Correctly mark `assert(state:)` deprecated

view details

Ben Cochran

commit sha 021014f929ae7af2cc7e605c1a10f5d27f8d8955

Merge pull request #23 from square/bc/render-tester-deprecated-file-line Add missing file/line to deprecated render expectations

view details

push time in 9 days

delete branch square/workflow-swift

delete branch : bc/render-tester-deprecated-file-line

delete time in 9 days

PR merged square/workflow-swift

Reviewers
Add missing file/line to deprecated render expectations

Make sure deprecated RenderTester expectation failures get attributed to the correct file/line.

Also, add missing deprecation marking on WorkflowRenderTester.assert(state:)

+23 -23

0 comment

2 changed files

bencochran

pr closed time in 9 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 final class SubtreeManagerTests: XCTestCase {         XCTAssertFalse(escapingContext.isValid)     } -    // A worker declared on a first `render` pass that is not on a subsequent should have the work cancelled.-    func test_cancelsWorkers() {-        struct WorkerWorkflow: Workflow {-            var startExpectation: XCTestExpectation-            var endExpectation: XCTestExpectation--            enum State {-                case notWorking-                case working-            }--            func makeInitialState() -> WorkerWorkflow.State {-                return .notWorking-            }--            func render(state: WorkerWorkflow.State, context: RenderContext<WorkerWorkflow>) -> Bool {-                switch state {-                case .notWorking:-                    return false-                case .working:-                    context.awaitResult(-                        for: ExpectingWorker(-                            startExpectation: startExpectation,-                            endExpectation: endExpectation-                        ),-                        outputMap: { output -> AnyWorkflowAction<WorkerWorkflow> in-                            AnyWorkflowAction.noAction-                        }-                    )-                    return true-                }-            }--            struct ExpectingWorker: Worker {-                var startExpectation: XCTestExpectation-                var endExpectation: XCTestExpectation--                typealias Output = Void--                func run() -> SignalProducer<Void, Never> {-                    return SignalProducer<Void, Never>({ [weak startExpectation, weak endExpectation] observer, lifetime in-                        lifetime.observeEnded {-                            endExpectation?.fulfill()-                        }--                        startExpectation?.fulfill()-                    })-                }--                func isEquivalent(to otherWorker: WorkerWorkflow.ExpectingWorker) -> Bool {-                    return true-                }-            }-        }--        let startExpectation = XCTestExpectation()-        let endExpectation = XCTestExpectation()-        let manager = WorkflowNode<WorkerWorkflow>.SubtreeManager()--        let isRunning = manager.render { context -> Bool in-            WorkerWorkflow(-                startExpectation: startExpectation,-                endExpectation: endExpectation-            )-            .render(-                state: .working,-                context: context-            )-        }--        XCTAssertEqual(true, isRunning)-        wait(for: [startExpectation], timeout: 1.0)--        let isStillRunning = manager.render { context -> Bool in-            WorkerWorkflow(-                startExpectation: startExpectation,-                endExpectation: endExpectation-            )-            .render(-                state: .notWorking,-                context: context-            )-        }--        XCTAssertFalse(isStillRunning)-        wait(for: [endExpectation], timeout: 1.0)-    }--    func test_subscriptionsUnsubscribe() {

Yeah, I guess it currently ends up being a test of SignalWorker, which should have its own independent test coverage.

dhavalshreyas

comment created time in 9 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import ReactiveSwift+import Workflow+import WorkflowReactiveSwift+import WorkflowReactiveSwiftTesting+import WorkflowTesting+import XCTest++class WorkflowReactiveSwiftTestingTests: XCTestCase {+    func test_workers() {+        let renderTester = TestWorkflow()+            .renderTester(initialState: .init(mode: .worker(input: "otherText"), output: ""))++        renderTester+            .expect(worker: TestWorker(input: "otherText"))+            .render { _ in }+    }++    func test_workerOutput_updatesState() {+        let renderTester = TestWorkflow()+            .renderTester(initialState: .init(mode: .worker(input: "otherText"), output: ""))++        renderTester+            .expect(+                worker: TestWorker(input: "otherText"),+                producingOutput: "otherText"+            )+            .render { _ in }+            .verifyState { state in+                XCTAssertEqual(state, TestWorkflow.State(mode: .worker(input: "otherText"), output: "otherText"))+            }+    }++    func test_worker_missing() {+        let tester = TestWorkflow()+            .renderTester()+            .expect(+                worker: TestWorker(input: "input")+            )++        expectingFailure(#"Expected child workflow of type: WorkerWorkflow<TestWorker>, key: """#) {+            tester.render { _ in }+        }+    }++    func test_worker_mismatch() {+        let tester = TestWorkflow()+            .renderTester(initialState: .init(mode: .worker(input: "test"), output: ""))+            .expect(+                worker: TestWorker(input: "not-test")+            )++        expectingFailures([+            #"Workers of type TestWorker not equivalent. Expected: TestWorker(input: "not-test"). Got: TestWorker(input: "test")"#,+        ]) {+            tester.render { _ in }+        }+    }++//    TODO(dhaval): This currently fatals because it's not sure what Rendering to return.

Ah, right. We can’t actually not expect Workflows and pass tests. Seems fine to remove this.

dhavalshreyas

comment created time in 9 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 import ReactiveSwift /// If there is, and if the workers are 'equivalent', the context leaves the existing worker running. /// /// If there is not an existing worker of this type, the context will kick off the new worker (via `run`).-public protocol Worker {+public protocol Worker: AnyWorkflowConvertible {     /// The type of output events returned by this worker.     associatedtype Output      /// Returns a signal producer to execute the work represented by this worker.     func run() -> SignalProducer<Output, Never>      /// Returns `true` if the other worker should be considered equivalent to `self`. Equivalence should take into-    /// account whatever data is meaninful to the task. For example, a worker that loads a user account from a server+    /// account whatever data is meaningful to the task. For example, a worker that loads a user account from a server     /// would not be equivalent to another worker with a different user ID.     func isEquivalent(to otherWorker: Self) -> Bool } +extension Worker {+    public func asAnyWorkflow() -> AnyWorkflow<Void, Output> {+        WorkerWorkflow(worker: self).asAnyWorkflow()+    }+}++struct WorkerWorkflow<WorkerType: Worker>: Workflow {+    let worker: WorkerType++    typealias Output = WorkerType.Output+    typealias Rendering = Void+    typealias State = UUID++    func makeInitialState() -> State {+        UUID()+    }++    func workflowDidChange(from previousWorkflow: WorkerWorkflow<WorkerType>, state: inout UUID) {+        if !worker.isEquivalent(to: previousWorkflow.worker) {+            state = UUID()+        }+    }++    func render(state: State, context: RenderContext<WorkerWorkflow>) -> Rendering {+        // Start with Void to ensure `worker.run()` is called only once for a given key

From my perspective that stuff’s easier to glance at and understand, whereas this I had to stare at for a second to get why it had that effect. I’m not hardline on it, but I think the subtlety is tricky.

dhavalshreyas

comment created time in 9 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

+Pod::Spec.new do |s|+  s.name         = 'WorkflowReactiveSwiftTesting'+  s.version      = '0.29.0'+  s.summary      = 'Infrastructure for Workflow-powered SwiftUI'+  s.homepage     = 'https://www.github.com/square/workflow-swift'+  s.license      = 'Apache License, Version 2.0'+  s.author       = 'Square'+  s.source       = { :git => 'https://github.com/square/workflow-swift.git', :tag => "v#{s.version}" }++  # 1.7 is needed for `swift_versions` support+  s.cocoapods_version = '>= 1.7.0'++  s.swift_versions = ['5.0']+  s.ios.deployment_target = '10.0'+  s.osx.deployment_target = '10.12'++  s.source_files = 'WorkflowReactiveSwift/Testing/**/*.swift'++  s.dependency 'Workflow', "#{s.version}"+  s.dependency 'WorkflowReactiveSwift', "#{s.version}"+  s.dependency 'WorkflowTesting', "#{s.version}"+  s.dependency 'ReactiveSwift', '~> 6.3.0'++  s.framework = 'XCTest'+end

nit: We should probably define the test_spec for the associated tests here

dhavalshreyas

comment created time in 9 days

PR opened square/workflow-swift

Reviewers
Add missing file/line to deprecated render expectations

Make sure deprecated RenderTester expectation failures get attributed to the correct file/line.

Also, add missing deprecation marking on WorkflowRenderTester.assert(state:)

+23 -23

0 comment

2 changed files

pr created time in 9 days

delete branch square/workflow-swift

delete branch : bc/render-tester-depricated-file-line

delete time in 9 days

create barnchsquare/workflow-swift

branch : bc/render-tester-deprecated-file-line

created branch time in 9 days

create barnchsquare/workflow-swift

branch : bc/render-tester-depricated-file-line

created branch time in 9 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import ReactiveSwift+import Workflow+import class Workflow.Lifetime++extension SignalProducer: AnyWorkflowConvertible where Error == Never {+    public func asAnyWorkflow() -> AnyWorkflow<Void, Value> {+        return SignalProducerWorkflow(signalProducer: self).asAnyWorkflow()+    }+}++struct SignalProducerWorkflow<Value>: Workflow {+    public typealias Output = Value+    public typealias State = Void+    public typealias Rendering = Void++    var signalProducer: SignalProducer<Value, Never>+    var key: AnyHashable++    public init(signalProducer: SignalProducer<Value, Never>, key: AnyHashable = "") {+        self.signalProducer = signalProducer+        self.key = key

Yeah, agreed. Consumers can provide a key when running this workflow itself.

dhavalshreyas

comment created time in 9 days

push eventsquare/workflow-swift

Dhaval Shreyas

commit sha f3ff097539bfe70627d9d65d538bf60c997ff9cf

Update Swiftformat style

view details

Dhaval Shreyas

commit sha 449ab9c8a8d8f72863f9bee20fa51eea018e32f1

Merge pull request #13 from square/dhaval/updateSwiftFormatStyle Update Swiftformat style - Inline case

view details

Tyler Stromberg

commit sha 8eb46cbab3a50646a74a0e6fc97b528a1c65803d

Run WorkflowTestingTests

view details

Tyler Stromberg

commit sha 61739da2a7e6454fe6c2f799ce52b9434e0d6d69

Merge pull request #20 from square/stromberg/run-workflow-testing-tests Run WorkflowTestingTests

view details

Ben Cochran

commit sha 69a909f4f3b8c23f8c94f5febf86e2f0fb4b6c8e

Introduce new RenderTester API

view details

Ben Cochran

commit sha 7bbf8974d85f6edeacfee7819075ef8cc3e86e2f

Fix WorkflowTesting podspec

view details

Ben Cochran

commit sha 9ef997a1c09ba564241d9f78366f6631b30c2dbe

Update tests to new RenderTester API

view details

Ben Cochran

commit sha 9994503a5ccbcaa13f30f3e1540adeffcb5a7dec

Add tests for RenderTester failures

view details

Ben Cochran

commit sha f1e1163d152cb3d391cbda925fa8ec4bcc88b57d

Use raw strings to reduce quote escaping in expected errors See [SE-200](https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md)

view details

Ben Cochran

commit sha d643163844e719e27f30d0ae4292b54e3d50d9d4

Make WorkflowRenderTester and RenderTesterResult structs This makes it a little more clear when mutations are happening, and makes it less weird that we mutate a shared thing while also passing it along (e.g. it’s now clear that the _only_ way to correctly use the render tester is to use the tester that results from each call as you go)

view details

Ben Cochran

commit sha 61a94f19395861d3e0db0e318b13337be5fef8ef

Use `assert` for asserting methods in RenderTester

view details

Ben Cochran

commit sha bdf9e5456ff28526efcdf548257bf5106f9ca8e7

Update tutorial tests and documentation for RenderTester change

view details

Ben Cochran

commit sha 3ee0eb872ec57278e5bde1927013c780907755b0

Fix WorkflowRenderTesterFailureTests on Xcode 11

view details

Ben Cochran

commit sha d459a9ca187efcfac2d7a1c00cb55bdec1b4b4d7

Assert expectations on the line they were expected on, not on the `render` call

view details

Ben Cochran

commit sha ea9911096704cba850948e92eec9b2b2bc89a625

Merge pull request #15 from square/bc/new-render-tester Introduce new RenderTester API Closes #16

view details

Dhaval Shreyas

commit sha 28c7a4438548e049d41ff2b7104bb5ccc5fa555d

Hookup ExpectedWorkflow Assertions

view details

Dhaval Shreyas

commit sha 2613126f1cf39c877a2b69272c72c41fdd7fe922

Merge pull request #21 from square/dhaval/hookupExpectedWorkflowAssertions Hookup ExpectedWorkflow Assertions

view details

Ben Cochran

commit sha 2f303fdacc3b51d4883226ec9d84b56bc0fbb34b

Allow updating the root workflow in a ContainerViewController To accomplish this, we now always wrap the root workflow. This allows us to keep a typed WorkflowHost instance that we can later update. Side benefit is we no longer need an extension to host an AnyWorkflowConvertible, it can be the primary initializer now.

view details

push time in 9 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 final class ConcurrencyTests: XCTestCase {         signal1.send(value: 1)         signal2.send(value: 2) -        XCTAssertEqual(0, host.rendering.value.count)+//        TODO: What does it mean that this is no longer true?+//        XCTAssertEqual(0, host.rendering.value.count)

The .observe(on: QueueScheduler.workflowExecution) when subscribing to the signal producer inside ChildWorker before meant that all worker outputs were received asynchronously on the next run loop cycle. This exposes that behavior change. I’m not entirely convinced that was intentional behavior or an unintended side-effect that got codified into this test though.

dhavalshreyas

comment created time in 9 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 final class WorkflowNodeTests: XCTestCase {          XCTAssertEqual(snapshot, expectedSnapshot)     }--    func test_handlesRepeatedWorkerOutputs() {

This to WorkflowReactiveSwitftTests too please

dhavalshreyas

comment created time in 10 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 final class SubtreeManagerTests: XCTestCase {         XCTAssertFalse(escapingContext.isValid)     } -    // A worker declared on a first `render` pass that is not on a subsequent should have the work cancelled.-    func test_cancelsWorkers() {-        struct WorkerWorkflow: Workflow {-            var startExpectation: XCTestExpectation-            var endExpectation: XCTestExpectation--            enum State {-                case notWorking-                case working-            }--            func makeInitialState() -> WorkerWorkflow.State {-                return .notWorking-            }--            func render(state: WorkerWorkflow.State, context: RenderContext<WorkerWorkflow>) -> Bool {-                switch state {-                case .notWorking:-                    return false-                case .working:-                    context.awaitResult(-                        for: ExpectingWorker(-                            startExpectation: startExpectation,-                            endExpectation: endExpectation-                        ),-                        outputMap: { output -> AnyWorkflowAction<WorkerWorkflow> in-                            AnyWorkflowAction.noAction-                        }-                    )-                    return true-                }-            }--            struct ExpectingWorker: Worker {-                var startExpectation: XCTestExpectation-                var endExpectation: XCTestExpectation--                typealias Output = Void--                func run() -> SignalProducer<Void, Never> {-                    return SignalProducer<Void, Never>({ [weak startExpectation, weak endExpectation] observer, lifetime in-                        lifetime.observeEnded {-                            endExpectation?.fulfill()-                        }--                        startExpectation?.fulfill()-                    })-                }--                func isEquivalent(to otherWorker: WorkerWorkflow.ExpectingWorker) -> Bool {-                    return true-                }-            }-        }--        let startExpectation = XCTestExpectation()-        let endExpectation = XCTestExpectation()-        let manager = WorkflowNode<WorkerWorkflow>.SubtreeManager()--        let isRunning = manager.render { context -> Bool in-            WorkerWorkflow(-                startExpectation: startExpectation,-                endExpectation: endExpectation-            )-            .render(-                state: .working,-                context: context-            )-        }--        XCTAssertEqual(true, isRunning)-        wait(for: [startExpectation], timeout: 1.0)--        let isStillRunning = manager.render { context -> Bool in-            WorkerWorkflow(-                startExpectation: startExpectation,-                endExpectation: endExpectation-            )-            .render(-                state: .notWorking,-                context: context-            )-        }--        XCTAssertFalse(isStillRunning)-        wait(for: [endExpectation], timeout: 1.0)-    }--    func test_subscriptionsUnsubscribe() {

We should test this still too in WorkflowReactiveSwitftTests

dhavalshreyas

comment created time in 10 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 final class WorkflowRenderTesterTests: XCTestCase {             .assert(output: .success)     } -    func test_workers() {

And these to WorkflowReactiveSwitftTestingTests

dhavalshreyas

comment created time in 10 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 final class SubtreeManagerTests: XCTestCase {         XCTAssertFalse(escapingContext.isValid)     } -    // A worker declared on a first `render` pass that is not on a subsequent should have the work cancelled.-    func test_cancelsWorkers() {

We should add a test to WorkflowReactiveSwitftTests that replicates this test

dhavalshreyas

comment created time in 10 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 final class WorkflowRenderTesterFailureTests: XCTestCase {         }     } -    // MARK: Workers--    func test_worker_missing() {

These too should move to a WorkflowReactiveSwitftTestingTests

dhavalshreyas

comment created time in 10 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import ReactiveSwift+import Workflow+import WorkflowReactiveSwift+import WorkflowReactiveSwiftTesting+import WorkflowTesting+import XCTest++class WorkerTests: XCTestCase {+    func testExpectedWorker() {+        SignalProducerTestWorkflow()+            .renderTester()+            .expect(worker: SignalProducerTestWorker(), producingOutput: 1)+            .render { _ in }+            .verifyState { state in+                XCTAssertEqual(state, 1)+            }+    }++    func testWorkerOutput() {+        let host = WorkflowHost(+            workflow: SignalProducerTestWorkflow()+        )++        let expectation = XCTestExpectation()+        let disposable = host.rendering.signal.observeValues { rendering in+            expectation.fulfill()+        }++        XCTAssertEqual(0, host.rendering.value)++        wait(for: [expectation], timeout: 1.0)+        XCTAssertEqual(1, host.rendering.value)++        disposable?.dispose()+    }++    func testExpectedWorkerDeprecatedTests() {+        SignalProducerTestWorkflow()+            .renderTester()+            .render(+                expectedState: ExpectedState(state: 1),+                expectedWorkers: [ExpectedWorker(worker: SignalProducerTestWorker(), output: 1)]+            )+    }+}++private struct SignalProducerTestWorkflow: Workflow {+    typealias State = Int+    typealias Rendering = Int++    func makeInitialState() -> Int {+        0+    }++    func render(state: Int, context: RenderContext<SignalProducerTestWorkflow>) -> Int {+        SignalProducerTestWorker()+            .mapOutput { output in+                AnyWorkflowAction { state in+                    state = output+                    return nil+                }+            }+            .running(in: context)+        return state+    }+}++private struct SignalProducerTestWorker: Worker {+    typealias Output = Int+    func run() -> SignalProducer<Int, Never> {+        return SignalProducer { observer, lifetime in

nit: SignalProducer(value: 1)

dhavalshreyas

comment created time in 10 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 final class WorkflowRenderTesterDeprecatedTests: XCTestCase {             )     } -    func test_workers() {

We should move these tests to a WorkflowReactiveSwitftTestingTests (phew)

dhavalshreyas

comment created time in 10 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import ReactiveSwift+import Workflow+import WorkflowReactiveSwift+import WorkflowReactiveSwiftTesting+import WorkflowTesting+import XCTest++class WorkerTests: XCTestCase {+    func testExpectedWorker() {+        SignalProducerTestWorkflow()+            .renderTester()+            .expect(worker: SignalProducerTestWorker(), producingOutput: 1)+            .render { _ in }+            .verifyState { state in+                XCTAssertEqual(state, 1)+            }+    }++    func testWorkerOutput() {+        let host = WorkflowHost(+            workflow: SignalProducerTestWorkflow()+        )++        let expectation = XCTestExpectation()+        let disposable = host.rendering.signal.observeValues { rendering in+            expectation.fulfill()+        }++        XCTAssertEqual(0, host.rendering.value)++        wait(for: [expectation], timeout: 1.0)+        XCTAssertEqual(1, host.rendering.value)++        disposable?.dispose()+    }++    func testExpectedWorkerDeprecatedTests() {+        SignalProducerTestWorkflow()+            .renderTester()+            .render(+                expectedState: ExpectedState(state: 1),+                expectedWorkers: [ExpectedWorker(worker: SignalProducerTestWorker(), output: 1)]+            )+    }+}++private struct SignalProducerTestWorkflow: Workflow {+    typealias State = Int+    typealias Rendering = Int++    func makeInitialState() -> Int {+        0+    }++    func render(state: Int, context: RenderContext<SignalProducerTestWorkflow>) -> Int {+        SignalProducerTestWorker()

These tests test signal producer via a Worker, but I think they should use a signal producer directly with running(in:) instead. Then separate tests for Workers (along with the ones called out above).

dhavalshreyas

comment created time in 10 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++#if DEBUG+    import ReactiveSwift+    import Workflow+    import class Workflow.Lifetime+    import WorkflowTesting+    import XCTest+    @testable import WorkflowReactiveSwift++    extension RenderTester {+        /// Expect the given worker. It will be checked for `isEquivalent(to:)` with the requested worker.++        /// - Parameters:+        ///   - worker: The worker to be expected+        ///   - output: An output that should be returned when this worker is requested, if any.+        public func expect<ExpectedWorkerType: Worker>(+            worker: ExpectedWorkerType,+            producingOutput output: ExpectedWorkerType.Output? = nil,+            file: StaticString = #file, line: UInt = #line+        ) -> RenderTester<WorkflowType> {+            expectWorkflow(+                type: WorkerWorkflow<ExpectedWorkerType>.self,+                key: "",

We should add the key as a parameter to this function since running(in:key:) allows for a key.

dhavalshreyas

comment created time in 10 days

Pull request review commentsquare/workflow-swift

Move Worker out of Workflow library to WorkflowReactiveSwift

 import ReactiveSwift /// If there is, and if the workers are 'equivalent', the context leaves the existing worker running. /// /// If there is not an existing worker of this type, the context will kick off the new worker (via `run`).-public protocol Worker {+public protocol Worker: AnyWorkflowConvertible {     /// The type of output events returned by this worker.     associatedtype Output      /// Returns a signal producer to execute the work represented by this worker.     func run() -> SignalProducer<Output, Never>      /// Returns `true` if the other worker should be considered equivalent to `self`. Equivalence should take into-    /// account whatever data is meaninful to the task. For example, a worker that loads a user account from a server+    /// account whatever data is meaningful to the task. For example, a worker that loads a user account from a server     /// would not be equivalent to another worker with a different user ID.     func isEquivalent(to otherWorker: Self) -> Bool } +extension Worker {+    public func asAnyWorkflow() -> AnyWorkflow<Void, Output> {+        WorkerWorkflow(worker: self).asAnyWorkflow()+    }+}++struct WorkerWorkflow<WorkerType: Worker>: Workflow {+    let worker: WorkerType++    typealias Output = WorkerType.Output+    typealias Rendering = Void+    typealias State = UUID++    func makeInitialState() -> State {+        UUID()+    }++    func workflowDidChange(from previousWorkflow: WorkerWorkflow<WorkerType>, state: inout UUID) {+        if !worker.isEquivalent(to: previousWorkflow.worker) {+            state = UUID()+        }+    }++    func render(state: State, context: RenderContext<WorkerWorkflow>) -> Rendering {+        // Start with Void to ensure `worker.run()` is called only once for a given key

That’s pretty subtle. Might be better to implement this as a straight runSideEffect?

dhavalshreyas

comment created time in 10 days

push eventsquare/workflow-swift

Dhaval Shreyas

commit sha f3ff097539bfe70627d9d65d538bf60c997ff9cf

Update Swiftformat style

view details

Dhaval Shreyas

commit sha 449ab9c8a8d8f72863f9bee20fa51eea018e32f1

Merge pull request #13 from square/dhaval/updateSwiftFormatStyle Update Swiftformat style - Inline case

view details

Tyler Stromberg

commit sha 8eb46cbab3a50646a74a0e6fc97b528a1c65803d

Run WorkflowTestingTests

view details

Tyler Stromberg

commit sha 61739da2a7e6454fe6c2f799ce52b9434e0d6d69

Merge pull request #20 from square/stromberg/run-workflow-testing-tests Run WorkflowTestingTests

view details

Ben Cochran

commit sha 69a909f4f3b8c23f8c94f5febf86e2f0fb4b6c8e

Introduce new RenderTester API

view details

Ben Cochran

commit sha 7bbf8974d85f6edeacfee7819075ef8cc3e86e2f

Fix WorkflowTesting podspec

view details

Ben Cochran

commit sha 9ef997a1c09ba564241d9f78366f6631b30c2dbe

Update tests to new RenderTester API

view details

Ben Cochran

commit sha 9994503a5ccbcaa13f30f3e1540adeffcb5a7dec

Add tests for RenderTester failures

view details

Ben Cochran

commit sha f1e1163d152cb3d391cbda925fa8ec4bcc88b57d

Use raw strings to reduce quote escaping in expected errors See [SE-200](https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md)

view details

Ben Cochran

commit sha d643163844e719e27f30d0ae4292b54e3d50d9d4

Make WorkflowRenderTester and RenderTesterResult structs This makes it a little more clear when mutations are happening, and makes it less weird that we mutate a shared thing while also passing it along (e.g. it’s now clear that the _only_ way to correctly use the render tester is to use the tester that results from each call as you go)

view details

Ben Cochran

commit sha 61a94f19395861d3e0db0e318b13337be5fef8ef

Use `assert` for asserting methods in RenderTester

view details

Ben Cochran

commit sha bdf9e5456ff28526efcdf548257bf5106f9ca8e7

Update tutorial tests and documentation for RenderTester change

view details

Ben Cochran

commit sha 3ee0eb872ec57278e5bde1927013c780907755b0

Fix WorkflowRenderTesterFailureTests on Xcode 11

view details

Ben Cochran

commit sha d459a9ca187efcfac2d7a1c00cb55bdec1b4b4d7

Assert expectations on the line they were expected on, not on the `render` call

view details

Ben Cochran

commit sha ea9911096704cba850948e92eec9b2b2bc89a625

Merge pull request #15 from square/bc/new-render-tester Introduce new RenderTester API Closes #16

view details

Ben Cochran

commit sha 90b51ded10a5c12d9eb77e2aded0daa6a0f1a611

Update WorkflowActionTester to match style of WorkflowRenderTester * `assert` -> `verify * Pull `output` verification into its own method

view details

push time in 10 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 69a909f4f3b8c23f8c94f5febf86e2f0fb4b6c8e

Introduce new RenderTester API

view details

Ben Cochran

commit sha 7bbf8974d85f6edeacfee7819075ef8cc3e86e2f

Fix WorkflowTesting podspec

view details

Ben Cochran

commit sha 9ef997a1c09ba564241d9f78366f6631b30c2dbe

Update tests to new RenderTester API

view details

Ben Cochran

commit sha 9994503a5ccbcaa13f30f3e1540adeffcb5a7dec

Add tests for RenderTester failures

view details

Ben Cochran

commit sha f1e1163d152cb3d391cbda925fa8ec4bcc88b57d

Use raw strings to reduce quote escaping in expected errors See [SE-200](https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md)

view details

Ben Cochran

commit sha d643163844e719e27f30d0ae4292b54e3d50d9d4

Make WorkflowRenderTester and RenderTesterResult structs This makes it a little more clear when mutations are happening, and makes it less weird that we mutate a shared thing while also passing it along (e.g. it’s now clear that the _only_ way to correctly use the render tester is to use the tester that results from each call as you go)

view details

Ben Cochran

commit sha 61a94f19395861d3e0db0e318b13337be5fef8ef

Use `assert` for asserting methods in RenderTester

view details

Ben Cochran

commit sha bdf9e5456ff28526efcdf548257bf5106f9ca8e7

Update tutorial tests and documentation for RenderTester change

view details

Ben Cochran

commit sha 3ee0eb872ec57278e5bde1927013c780907755b0

Fix WorkflowRenderTesterFailureTests on Xcode 11

view details

Ben Cochran

commit sha d459a9ca187efcfac2d7a1c00cb55bdec1b4b4d7

Assert expectations on the line they were expected on, not on the `render` call

view details

Ben Cochran

commit sha ea9911096704cba850948e92eec9b2b2bc89a625

Merge pull request #15 from square/bc/new-render-tester Introduce new RenderTester API Closes #16

view details

push time in 11 days

issue closedsquare/workflow-swift

Change to a chain-able RenderTester API

As described in square/workflow#702, we should move to a chain-able RenderTester API.

Benefits:

  1. Separate expectations during the render from verification of post-render state of the world

    Our current ExpectedState is the trickiest of this IMO. It’s unclear from the API if this is asserts the state before the render or after it (it’s after).

  2. Extensibility

    The current model, all expectations being passed in a single method, leaves us in an extensibility trap. When Workers become Workflows, it’s natural that ExpectedWorker is actually a wrapper around ExpectedWorkflow. We can do that from the same module without breaking the API, but if we move Worker out of the core module, we have to move worker expectations with it. By making expectations expressed as method calls, we can move the expect(worker:…) method to an extension and build it on top of expectWorkflow(type:…).

  3. Personally, I think it reads nicer

    A chained API reads more easily as a story down a single column than what we have today where the actual types being expected are nested a few layers deep in a single call. (It also throws all of its implementation detail onto the API surface)

Example

NameLoadingWorkflow()
    .renderTester(initialState: .init(state: .loading, token: "user-token"))
    .expect(
        worker: LoadingWorker(token: "user-token"),
        producingOutput: .success("Ben Cochran")
    )
    .expectSideEffect(key: "some-extra-side-effect-just-for-fun")
    .render { rendering in 
        XCTAssertEqual(rendering.title, "Loading")
    }
    .verify(
        action: MyWorkflow.Action.loadSuccess(name: "Ben Cochran")
    )
    .verify(
        output: .complete
    )

Open Questions

  1. Today, Swift’s RenderTester can be used across multiple renders (it updates its internal workflow state according to the actions produced in each render). Kotlin’s RenderTester does not allow this. We should probably decide which model we like and converge.

  2. Our WorkflowActionTester uses assert in its method names. Kotlin’s RenderTester uses verify. We should probably consolidate all of these to either assert or verify across the board.

closed time in 11 days

bencochran

delete branch square/workflow-swift

delete branch : bc/new-render-tester

delete time in 11 days

PR merged square/workflow-swift

Reviewers
Introduce new RenderTester API

Per #16 and https://github.com/square/workflow/issues/702, introduce a new RenderTester API shaped like the following:

NameLoadingWorkflow()
    .renderTester(initialState: .init(state: .loading, token: "user-token"))
    .expect(
        worker: LoadingWorker(token: "user-token"),
        producingOutput: .success("Ben Cochran")
    )
    .expectSideEffect(key: "some-extra-side-effect-just-for-fun")
    .render { rendering in 
        XCTAssertEqual(rendering.title, "Loading")
    }
    .verify(
        action: MyWorkflow.Action.loadSuccess(name: "Ben Cochran")
    )
    .verify(
        output: .complete
    )

The expect methods come in the following shapes:

  • Workflows:
    • expectWorkflow(workflowType: WorkflowType.Type, key: String, producingRendering: WorkflowType.Rendering, producingOutput: WorkflowType.Output?, assertions: ((WorkflowType) -> Void)?)
  • Workers
    • expect(worker: WorkerType, producingOutput: WorkerType.Output?)
  • Side effects
    • expectSideEffect(key: AnyHashable, producingAction: ActionType?)

The verify methods come in the following:

  • Actions
    • verifyAction(assertions: (Action) -> Void)
    • verify(action: Action) (when Equatable)
    • verifyNoAction()
  • Output
    • verifyOutput(assertions: (Output) -> Void)
    • verify(output: Output) (when Equatable)
    • verifyNoOutput()
  • Resulting state
    • verifyState(assertions: (State) -> Void)
    • verify(state: State) (when Equatable)

This also deprecates, but keeps around, the old API (you’ll see some type erasure and contortions in this PR to reimplement the old API on top of the new one).

Checklist

  • [X] Unit Tests
  • [ ] ~UI Tests~
  • [ ] ~Snapshot Tests (iOS only)~
  • [ ] I have made corresponding changes to the documentation
+2386 -1195

4 comments

22 changed files

bencochran

pr closed time in 11 days

push eventsquare/workflow-swift

Dhaval Shreyas

commit sha f3ff097539bfe70627d9d65d538bf60c997ff9cf

Update Swiftformat style

view details

Dhaval Shreyas

commit sha 449ab9c8a8d8f72863f9bee20fa51eea018e32f1

Merge pull request #13 from square/dhaval/updateSwiftFormatStyle Update Swiftformat style - Inline case

view details

Tyler Stromberg

commit sha 8eb46cbab3a50646a74a0e6fc97b528a1c65803d

Run WorkflowTestingTests

view details

Tyler Stromberg

commit sha 61739da2a7e6454fe6c2f799ce52b9434e0d6d69

Merge pull request #20 from square/stromberg/run-workflow-testing-tests Run WorkflowTestingTests

view details

Ben Cochran

commit sha 69a909f4f3b8c23f8c94f5febf86e2f0fb4b6c8e

Introduce new RenderTester API

view details

Ben Cochran

commit sha 7bbf8974d85f6edeacfee7819075ef8cc3e86e2f

Fix WorkflowTesting podspec

view details

Ben Cochran

commit sha 9ef997a1c09ba564241d9f78366f6631b30c2dbe

Update tests to new RenderTester API

view details

Ben Cochran

commit sha 9994503a5ccbcaa13f30f3e1540adeffcb5a7dec

Add tests for RenderTester failures

view details

Ben Cochran

commit sha f1e1163d152cb3d391cbda925fa8ec4bcc88b57d

Use raw strings to reduce quote escaping in expected errors See [SE-200](https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md)

view details

Ben Cochran

commit sha d643163844e719e27f30d0ae4292b54e3d50d9d4

Make WorkflowRenderTester and RenderTesterResult structs This makes it a little more clear when mutations are happening, and makes it less weird that we mutate a shared thing while also passing it along (e.g. it’s now clear that the _only_ way to correctly use the render tester is to use the tester that results from each call as you go)

view details

Ben Cochran

commit sha 61a94f19395861d3e0db0e318b13337be5fef8ef

Use `assert` for asserting methods in RenderTester

view details

Ben Cochran

commit sha bdf9e5456ff28526efcdf548257bf5106f9ca8e7

Update tutorial tests and documentation for RenderTester change

view details

Ben Cochran

commit sha 3ee0eb872ec57278e5bde1927013c780907755b0

Fix WorkflowRenderTesterFailureTests on Xcode 11

view details

Ben Cochran

commit sha d459a9ca187efcfac2d7a1c00cb55bdec1b4b4d7

Assert expectations on the line they were expected on, not on the `render` call

view details

push time in 11 days

Pull request review commentsquare/workflow-swift

Introduce new RenderTester API

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++import Workflow+import XCTest++struct AppliedAction<WorkflowType: Workflow> {

I don’t think so if the comment about @testable import being the issue was correct (since this file does a standard import)

bencochran

comment created time in 11 days

pull request commentsquare/workflow-swift

Introduce new RenderTester API

After noting “though I do think I should update assertions to give the file and line so we can correctly place the error where it should be” I realized it would be nicer if expectations asserted on the line of the expectation rather than on render, so I updated to do that (which also means assertions on expectWorkflow doesn’t need file and line ✨).

I also fixed the fact that WorkflowRenderTesterFailureTests wouldn’t compile on Xcode 11 and filed #18.

bencochran

comment created time in 11 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 6afcff958b06c2069f5ff6e86d50089b4c8627a7

Fix WorkflowRenderTesterFailureTests on Xcode 11

view details

Ben Cochran

commit sha 142c1026eaa4b70c17bc35c462d953c753e50c68

Assert expectations on the line they were expected on, not on the `render` call

view details

push time in 11 days

issue openedsquare/workflow-swift

CI isn’t running WorkflowTestingTests

My PR used Xcode 12-only testing APIs but all checks passed on Xcode 11.4. This means WorkflowTestingTests must not be running on CI.

created time in 11 days

Pull request review commentsquare/workflow-swift

Introduce new RenderTester API

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++#if DEBUG++    @testable import Workflow++    extension RenderTester {+        internal class AnyExpectedWorkflow {+            let workflowType: Any.Type+            let key: String+            fileprivate init(workflowType: Any.Type, key: String) {+                self.workflowType = workflowType+                self.key = key+            }+        }++        internal class ExpectedWorkflow<ExpectedWorkflowType: Workflow>: AnyExpectedWorkflow {+            let rendering: ExpectedWorkflowType.Rendering+            let output: ExpectedWorkflowType.Output?++            init(key: String, rendering: ExpectedWorkflowType.Rendering, output: ExpectedWorkflowType.Output?) {

Ah, right, yep!

bencochran

comment created time in 11 days

Pull request review commentsquare/workflow-swift

Introduce new RenderTester API

+/*+ * Copyright 2020 Square Inc.+ *+ * Licensed under the Apache License, Version 2.0 (the "License");+ * you may not use this file except in compliance with the License.+ * You may obtain a copy of the License at+ *+ *     http://www.apache.org/licenses/LICENSE-2.0+ *+ * Unless required by applicable law or agreed to in writing, software+ * distributed under the License is distributed on an "AS IS" BASIS,+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ * See the License for the specific language governing permissions and+ * limitations under the License.+ */++#if DEBUG++    @testable import Workflow++    extension RenderTester {+        internal class AnyExpectedWorkflow {+            let workflowType: Any.Type+            let key: String+            fileprivate init(workflowType: Any.Type, key: String) {+                self.workflowType = workflowType+                self.key = key+            }+        }++        internal class ExpectedWorkflow<ExpectedWorkflowType: Workflow>: AnyExpectedWorkflow {+            let rendering: ExpectedWorkflowType.Rendering+            let output: ExpectedWorkflowType.Output?++            init(key: String, rendering: ExpectedWorkflowType.Rendering, output: ExpectedWorkflowType.Output?) {

Do we? We don’t actually want to reproduce/change the matching behavior since the RenderContext won’t have additional matching logic for Workers. I’d pictured something like this:

extension RenderTester {
    public func expect<ExpectedWorkerType: Worker>(
        worker expectedWorker: ExpectedWorkerType,
        key: String = "",
        producingOutput output: ExpectedWorkerType.Output? = nil
    ) -> RenderTester<WorkflowType> {
        return self.expectWorkflow(
            type: WorkerWorkflow<ExpectedWorkerType>,
            key: key,
            producingRendering: (),
            producingOutput: output,
            assertions: { actualWorker in
                XCTAssert(expectedWorker.isEquivalent(to: actualWorker))
            }
        )
    }
}

(though I do think I should update assertions to give the file and line so we can correctly place the error where it should be)

bencochran

comment created time in 11 days

push eventsquare/workflow-swift

Dhaval Shreyas

commit sha 79dd961a63fc1bc73508f8c1143cd8841304ccfb

Rename rendered func calls [WIP] [DNM]

view details

Dhaval Shreyas

commit sha cbe26aef428c98ae2f924cd2258625c86d820d5c

Merge pull request #9 from square/dhaval/renameFuns Rename `rendered` API

view details

Ben Cochran

commit sha 43247cc26e76dffcf1128c659e98c1b7a61d2e84

Allow updating the root workflow in a ContainerViewController To accomplish this, we now always wrap the root workflow. This allows us to keep a typed WorkflowHost instance that we can later update. Side benefit is we no longer need an extension to host an AnyWorkflowConvertible, it can be the primary initializer now.

view details

push time in 11 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 505eab5a0df7dadc79a4a13d0b4e6bc599fcb8a0

Use `assert` for asserting methods in RenderTester

view details

Ben Cochran

commit sha adfe199a23cda725d985cc0b961d6c1b2fc4d3bd

Update tutorial tests and documentation for RenderTester change

view details

push time in 11 days

issue commentsquare/workflow-swift

Change to a chain-able RenderTester API

  1. Yeah, I think not allowing chaining multiple renders is good. As written in this PR, the old functionality still exists in the deprecated method (by returning a whole new RenderTester), but the new API is a one-shot immutable model.

  2. Oh, yeah, that’s a good distinction. In general I like verify for the ones where the consumer asserts and assert for the ones where the tester asserts. We do though have a couple of these that do a little bit of both (e.g. verifyOutput takes an (Output) -> Void and first asserts that there is an output before passing it to the given assertion closure), but I’m inclined to keep them as verify.

bencochran

comment created time in 11 days

PR opened square/workflow-swift

Update WorkflowActionTester to match style of WorkflowRenderTester
  • assert -> `verify
  • Pull output verification into its own method

Checklist

  • [X] Unit Tests
  • [ ] ~UI Tests~
  • [ ] ~Snapshot Tests (iOS only)~
  • [ ] I have made corresponding changes to the documentation
+175 -56

0 comment

2 changed files

pr created time in 11 days

create barnchsquare/workflow-swift

branch : bc/new-action-tester-api

created branch time in 11 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 45ad7fa78ab2888d482788c27c30fa761058dbeb

Make WorkflowRenderTester and RenderTesterResult structs This makes it a little more clear when mutations are happening, and makes it less weird that we mutate a shared thing while also passing it along (e.g. it’s now clear that the _only_ way to correctly use the render tester is to use the tester that results from each call as you go)

view details

push time in 11 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 65c6a2e15d08a18f7006902734e690d6739449a3

Make WorkflowRenderTester and RenderTesterResult structs This makes it a little more clear when mutations are happening, and makes it less weird that we mutate a shared thing while also passing it along (e.g. it’s now clear that the _only_ way to correctly use the render tester is to use the tester that results from each call as you go)

view details

push time in 11 days

issue openedsquare/workflow-swift

Change to a chain-able RenderTester API

As described in square/workflow#702, we should move to a chain-able RenderTester API.

Benefits:

  1. Separate expectations during the render from verification of post-render state of the world

    Our current ExpectedState is the trickiest of this IMO. It’s unclear from the API if this is asserts the state before the render or after it (it’s after).

  2. Extensibility

    The current model, all expectations being passed in a single method, leaves us in an extensibility trap. When Workers become Workflows, it’s natural that ExpectedWorker is actually a wrapper around ExpectedWorkflow. We can do that from the same module without breaking the API, but if we move Worker out of the core module, we have to move worker expectations with it. By making expectations expressed as method calls, we can move the expect(worker:…) method to an extension and build it on top of expectWorkflow(type:…).

  3. Personally, I think it reads nicer

    A chained API reads more easily as a story down a single column than what we have today where the actual types being expected are nested a few layers deep in a single call. (It also throws all of its implementation detail onto the API surface)

Open Questions

  1. Today, Swift’s RenderTester can be used across multiple renders (it updates its internal workflow state according to the actions produced in each render). Kotlin’s RenderTester does not allow this. We should probably decide which model we like and converge.

  2. Our WorkflowActionTester uses assert in its method names. Kotlin’s RenderTester uses verify. We should probably consolidate all of these to either assert or verify across the board.

created time in 12 days

push eventsquare/workflow-swift

Ben Cochran

commit sha e5b839106b9e1e5bf3bd081effe2a13842563598

Use raw strings to reduce quote escaping in expected errors See [SE-200](https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md)

view details

push time in 12 days

push eventsquare/workflow-swift

Ben Cochran

commit sha ec22ad78483a12c2bc6ac12176788cfda4f396eb

Introduce new RenderTester API

view details

Ben Cochran

commit sha 46902c5d961e898ad33144ce06b325eb9a0f72c5

Fix WorkflowTesting podspec

view details

Ben Cochran

commit sha 150cf157de8be6bff1368c8b36e853fa2adea08d

Update tests to new RenderTester API

view details

Ben Cochran

commit sha 72bc77ad8b3a8d12ccc46845f77893ea92d6d54d

Add tests for RenderTester failures

view details

push time in 12 days

push eventsquare/workflow-swift

Ben Cochran

commit sha ec9af5eedffccfc69801c3495d190e3637c2118f

Introduce new RenderTester API

view details

Ben Cochran

commit sha 7cafd8a754e3fd512e7bf191abb9a057261afdc2

Fix WorkflowTesting podspec

view details

push time in 12 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 94ee713dfa2af18f1691f56e8fa70c1404b83dc5

Fix WorkflowTesting podspec

view details

push time in 12 days

push eventsquare/workflow-swift

Ben Cochran

commit sha 20a982534b089210c6a028de08cce1e90c2f785c

Introduce new RenderTester API

view details

push time in 12 days

PR opened square/workflow-swift

Introduce new RenderTester API

Per https://github.com/square/workflow/issues/702, introduce a new RenderTester API shaped like the following:

NameLoadingWorkflow()
    .renderTester(initialState: .init(state: .loading, token: "user-token"))
    .expect(
        worker: LoadingWorker(token: "user-token"),
        output: .success("Ben Cochran")
    )
    .expectSideEffect(key: "some-extra-side-effect-just-for-fun")
    .render { rendering in 
        XCTAssertEqual(rendering.title, "Loading")
    }
    .verify(
        action: MyWorkflow.Action.loadSuccess(name: "Ben Cochran")
    )
    .verify(
        output: .complete
    )

The expect methods come in the following shapes:

  • Workflows:
    • expectWorkflow(workflowType: WorkflowType.Type, key: String, producingRendering: WorkflowType.Rendering, producingOutput: WorkflowType.Output?, assertions: ((WorkflowType) -> Void)?)
  • Workers
    • expect(worker: WorkerType, producingOutput: WorkerType.Output?)
  • Side effects
    • expectSideEffect(key: AnyHashable, producingAction: ActionType?)

The verify methods come in the following:

  • Actions
    • verifyAction(assertions: (Action) -> Void)
    • verify(action: Action) (when Equatable)
    • verifyNoAction()
  • Output
    • verifyOutput(assertions: (Output) -> Void)
    • verify(output: Output) (when Equatable)
    • verifyNoOutput()
  • Resulting state
    • verifyState(assertions: (State) -> Void)
    • verify(state: State) (when Equatable)

This also deprecates, but keeps around, the old API (you’ll see some type erasure and contortions in this PR to reimplement the old API on top of the new one).

Checklist

  • [X] Unit Tests
  • [ ] ~UI Tests~
  • [ ] ~Snapshot Tests (iOS only)~
  • [ ] I have made corresponding changes to the documentation
+1366 -538

0 comment

10 changed files

pr created time in 12 days

create barnchsquare/workflow-swift

branch : bc/new-render-tester

created branch time in 12 days

Pull request review commentsquare/workflow-swift

WorkflowTesting changes to support GUWT

 extension ExpectedSideEffect { }  public struct ExpectedWorkflow {-    let workflowType: Any.Type-    let key: String-    let rendering: Any-    let output: Any?--    public init<WorkflowType: Workflow>(type: WorkflowType.Type, key: String = "", rendering: WorkflowType.Rendering, output: WorkflowType.Output? = nil) {-        self.workflowType = type-        self.key = key+    public let rendering: Any

Exposing the type-erased internals here worries me. Do these need to be public?

dhavalshreyas

comment created time in 14 days

pull request commentsquare/workflow-swift

Rename `rendered` API

Post-merge “Looks great, thanks!”

dhavalshreyas

comment created time in 14 days

push eventsquare/workflow-swift

Ben Cochran

commit sha d18fd6f1e946f72522af8f0bf816918599543d34

actually perform assertions

view details

push time in 21 days

create barnchsquare/workflow-swift

branch : dhaval/reactiveSwiftWorker+bc

created branch time in 21 days

pull request commentsquare/workflow-swift

[Proposal] Allow updating the root workflow in a ContainerViewController

@zach-klippenstein Yeah, this largely got re-motivated in my head seeing that discussion go by (or, from what I was able to follow of it).

bencochran

comment created time in 22 days

PR opened square/workflow-swift

Reviewers
[Proposal] Allow updating the root workflow in a ContainerViewController

Especially when transitioning things to Workflow, when you have many small root workflows, it can be useful to feed in new properties to the root. Right now, most things I’ve seen that need to update this root workflow will initialize it with a Property<T> of something that can be changed from the outside later. A nicer pattern is to embrace the declarative nature of Workflow itself and allow it to be updated when necessary.

We originally worked on this and added update(workflow:) to WorkflowHost, but the fact that it was held as an Any in ContainerViewController prevented us from getting all the way there.

To accomplish this here, we now always wrap the root workflow. This allows us to keep a typed WorkflowHost instance that we can later update. A side benefit is we no longer need an extension to host an AnyWorkflowConvertible, and it can be the primary initializer now.

One thing to note is that I could initialize a ContainerViewController with WorkflowA and then update it with WorkflowB so long as those two had the same Output and Rendering. I think this is actually a feature, since this mimics how you can switch from rendering one workflow to another inside a workflow seamlessly.

Checklist

  • [X] Unit Tests
  • [ ] I have made corresponding changes to the documentation
+124 -74

0 comment

3 changed files

pr created time in 23 days

create barnchsquare/workflow-swift

branch : bc/container-view-controller-update

created branch time in 23 days

startedsquare/workflow-swift

started time in 23 days

issue openedsquare/workflow-swift

Consider swapping the generic parameters on ContainerViewController

ContainerViewController is currently ContainerViewController<Output, Rendering> whereas AnyWorkflow is AnyWorkflow<Rendering, Output>. This inconsistency has tripped me up a few times, and I don’t personally see a concrete reason for this ordering over the other. If we want to make these match, pre-1.0’s the time.

created time in 23 days

more