mirror of
https://github.com/pointfreeco/swift-composable-architecture.git
synced 2025-12-20 09:11:33 +01:00
* Converted voice memos back to identified array
* update deps
* update docs for DismissEffect
* wip
* Add Sendable conformance to PresentationState (#2086)
* wip
* swift-format
* wip
* wip
* fix some warnings
* docs
* wip
* wip
* Catch some typos in Articles (#2088)
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* docs
* wip
* wip
* docs
* wip
* wip
* wip
* wip
* docs
* docs
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Fix invalid states count for 3 optionals and typos (#2094)
* wip
* wip
* more dismisseffect docs
* fixed some references
* navigation doc corrections
* more nav docs
* fix cancellation tests in release mode
* wrap some tests in #if DEBUG since they are testing expected failures
* update UUIDs in tests to use shorter initializer
* fixed a todo
* wip
* fix merge errors
* wip
* fix
* wip
* wip
* fixing a bunch of todos
* get rid of rawvalue in StackElementID
* more todos
* NavLinkStore docs
* fix swift 5.6 stuff
* fix some standups tests
* fix
* clean up
* docs fix
* fixes
* wip
* 5.6 fix
* wip
* wip
* dont parallelize tests
* updated demo readmes
* wip
* Use ObservedObject instead of StateObject for alert/dialog modifiers.
* integration tests for bad dismissal behavior
* check for runtime warnings in every integration test
* wip
* wip
* wip
* fix
* wip
* wip
* wip
* wip
* wip
* wip
* Drop a bunch of Hashables.
* some nav bug fixes
* wip
* wip
* wip
* fix
* fix
* wip
* wip
* Simplify recording test.
* add concurrent async test
* fix
* wip
* Refact how detail dismisses itself.
* fix
* 5.6 fix
* wip
* wip
* wip
* wip
* Add TestStore.assert.
* Revert "Add TestStore.assert."
This reverts commit a892cccc66.
* add Ukrainian Readme.md (#2121)
* Add TestStore.assert. (#2123)
* Add TestStore.assert.
* wip
* Update Sources/ComposableArchitecture/TestStore.swift
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* Update Sources/ComposableArchitecture/Documentation.docc/Extensions/TestStore.md
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* fix tests
---------
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* Run swift-format
* push for store.finish and presentation
* wip
* move docs around
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Add case subscripts
* wip
* wip
* wip
* 5.7-only
* wip
* wip
* wip
* wip
* fix
* revert store.finish task cancellation
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* add test for presentation scope
* wip
* wip
* wip
* wip
* wip
* cleanup
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Rename ReducerProtocol.swift to Reducer.swift (#2206)
* Hard-deprecate old SwitchStore initializers/overloads
* wip
* wip
* Resolve CaseStudies crash (#2258)
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Bump timeout for CI
* wip
* wip
---------
Co-authored-by: Jackson Utsch <jutechs@gmail.com>
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
Co-authored-by: 유재호 <y73447jh@gmail.com>
Co-authored-by: Dmytro <barabashdmyto@gmail.com>
Co-authored-by: mbrandonw <mbrandonw@users.noreply.github.com>
278 lines
7.9 KiB
Swift
278 lines
7.9 KiB
Swift
#if DEBUG
|
||
import ComposableArchitecture
|
||
import XCTest
|
||
|
||
@MainActor
|
||
final class TestStoreFailureTests: BaseTCATestCase {
|
||
func testNoStateChangeFailure() async {
|
||
enum Action { case first, second }
|
||
let store = TestStore(initialState: 0) {
|
||
Reduce<Int, Action> { state, action in
|
||
switch action {
|
||
case .first: return .send(.second)
|
||
case .second: return .none
|
||
}
|
||
}
|
||
}
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
Expected state to change, but no change occurred.
|
||
|
||
The trailing closure made no observable modifications to state. If no change to state is \
|
||
expected, omit the trailing closure.
|
||
"""
|
||
}
|
||
await store.send(.first) { _ = $0 }
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
Expected state to change, but no change occurred.
|
||
|
||
The trailing closure made no observable modifications to state. If no change to state is \
|
||
expected, omit the trailing closure.
|
||
"""
|
||
}
|
||
await store.receive(.second) { _ = $0 }
|
||
}
|
||
|
||
func testStateChangeFailure() async {
|
||
struct State: Equatable { var count = 0 }
|
||
let store = TestStore(initialState: State()) {
|
||
Reduce<State, Void> { state, action in
|
||
state.count += 1
|
||
return .none
|
||
}
|
||
}
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
A state change does not match expectation: …
|
||
|
||
− TestStoreFailureTests.State(count: 0)
|
||
+ TestStoreFailureTests.State(count: 1)
|
||
|
||
(Expected: −, Actual: +)
|
||
"""
|
||
}
|
||
await store.send(()) { $0.count = 0 }
|
||
}
|
||
|
||
func testUnexpectedStateChangeOnSendFailure() async {
|
||
struct State: Equatable { var count = 0 }
|
||
let store = TestStore(initialState: State()) {
|
||
Reduce<State, Void> { state, action in
|
||
state.count += 1
|
||
return .none
|
||
}
|
||
}
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
State was not expected to change, but a change occurred: …
|
||
|
||
− TestStoreFailureTests.State(count: 0)
|
||
+ TestStoreFailureTests.State(count: 1)
|
||
|
||
(Expected: −, Actual: +)
|
||
"""
|
||
}
|
||
await store.send(())
|
||
}
|
||
|
||
func testUnexpectedStateChangeOnReceiveFailure() async {
|
||
struct State: Equatable { var count = 0 }
|
||
enum Action { case first, second }
|
||
let store = TestStore(initialState: State()) {
|
||
Reduce<State, Action> { state, action in
|
||
switch action {
|
||
case .first: return .send(.second)
|
||
case .second:
|
||
state.count += 1
|
||
return .none
|
||
}
|
||
}
|
||
}
|
||
|
||
await store.send(.first)
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
State was not expected to change, but a change occurred: …
|
||
|
||
− TestStoreFailureTests.State(count: 0)
|
||
+ TestStoreFailureTests.State(count: 1)
|
||
|
||
(Expected: −, Actual: +)
|
||
"""
|
||
}
|
||
await store.receive(.second)
|
||
}
|
||
|
||
func testReceivedActionAfterDeinit() async {
|
||
enum Action { case first, second }
|
||
let store = TestStore(initialState: 0) {
|
||
Reduce<Int, Action> { state, action in
|
||
switch action {
|
||
case .first: return .send(.second)
|
||
case .second: return .none
|
||
}
|
||
}
|
||
}
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
The store received 1 unexpected action after this one: …
|
||
|
||
Unhandled actions:
|
||
• .second
|
||
"""
|
||
}
|
||
await store.send(.first)
|
||
}
|
||
|
||
func testEffectInFlightAfterDeinit() async {
|
||
let store = TestStore(initialState: 0) {
|
||
Reduce<Int, Void> { state, action in
|
||
.run { _ in try await Task.never() }
|
||
}
|
||
}
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
An effect returned for this action is still running. It must complete before the end of \
|
||
the test. …
|
||
|
||
To fix, inspect any effects the reducer returns for this action and ensure that all of \
|
||
them complete by the end of the test. There are a few reasons why an effect may not have \
|
||
completed:
|
||
|
||
• If using async/await in your effect, it may need a little bit of time to properly \
|
||
finish. To fix you can simply perform "await store.finish()" at the end of your test.
|
||
|
||
• If an effect uses a clock/scheduler (via "receive(on:)", "delay", "debounce", etc.), \
|
||
make sure that you wait enough time for it to perform the effect. If you are using a \
|
||
test clock/scheduler, advance it so that the effects may complete, or consider using an \
|
||
immediate clock/scheduler to immediately perform the effect instead.
|
||
|
||
• If you are returning a long-living effect (timers, notifications, subjects, etc.), \
|
||
then make sure those effects are torn down by marking the effect ".cancellable" and \
|
||
returning a corresponding cancellation effect ("Effect.cancel") from another action, or, \
|
||
if your effect is driven by a Combine subject, send it a completion.
|
||
"""
|
||
}
|
||
await store.send(())
|
||
}
|
||
|
||
func testSendActionBeforeReceivingFailure() async {
|
||
enum Action { case first, second }
|
||
let store = TestStore(initialState: 0) {
|
||
Reduce<Int, Action> { state, action in
|
||
switch action {
|
||
case .first: return .send(.second)
|
||
case .second: return .none
|
||
}
|
||
}
|
||
}
|
||
|
||
await store.send(.first)
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
Must handle 1 received action before sending an action: …
|
||
|
||
Unhandled actions: [
|
||
[0]: .second
|
||
]
|
||
"""
|
||
}
|
||
await store.send(.first)
|
||
|
||
await store.receive(.second)
|
||
await store.receive(.second)
|
||
}
|
||
|
||
func testReceiveNonExistentActionFailure() async {
|
||
enum Action { case action }
|
||
let store = TestStore(initialState: 0) {
|
||
Reduce<Int, Action> { _, _ in .none }
|
||
}
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
Expected to receive the following action, but didn't: …
|
||
|
||
TestStoreFailureTests.Action.action
|
||
"""
|
||
}
|
||
await store.receive(.action)
|
||
}
|
||
|
||
func testReceiveUnexpectedActionFailure() async {
|
||
enum Action { case first, second }
|
||
let store = TestStore(initialState: 0) {
|
||
Reduce<Int, Action> { state, action in
|
||
switch action {
|
||
case .first:
|
||
return .send(.second)
|
||
case .second:
|
||
state += 1
|
||
return .none
|
||
}
|
||
}
|
||
}
|
||
|
||
await store.send(.first)
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == """
|
||
Received unexpected action: …
|
||
|
||
− TestStoreFailureTests.Action.first
|
||
+ TestStoreFailureTests.Action.second
|
||
|
||
(Expected: −, Received: +)
|
||
"""
|
||
}
|
||
await store.receive(.first)
|
||
}
|
||
|
||
func testModifyClosureThrowsErrorFailure() async {
|
||
let store = TestStore(initialState: 0) {
|
||
Reduce<Int, Void> { _, _ in .none }
|
||
}
|
||
|
||
XCTExpectFailure {
|
||
$0.compactDescription == "Threw error: SomeError()"
|
||
}
|
||
await store.send(()) { _ in
|
||
struct SomeError: Error {}
|
||
throw SomeError()
|
||
}
|
||
}
|
||
|
||
func testExpectedStateEqualityMustModify() async {
|
||
let store = TestStore(initialState: 0) {
|
||
Reduce<Int, Bool> { state, action in
|
||
switch action {
|
||
case true: return .send(false)
|
||
case false: return .none
|
||
}
|
||
}
|
||
}
|
||
|
||
await store.send(true)
|
||
await store.receive(false)
|
||
|
||
XCTExpectFailure()
|
||
await store.send(true) {
|
||
$0 = 0
|
||
}
|
||
|
||
XCTExpectFailure()
|
||
await store.receive(false) {
|
||
$0 = 0
|
||
}
|
||
}
|
||
}
|
||
#endif
|