11import Testing
2- import Combine
2+ import Observation
33import Foundation
44
55@testable import WooCommerce
@@ -10,17 +10,14 @@ import class WooFoundation.CurrencySettings
1010import protocol WooFoundation. Analytics
1111
1212struct PointOfSaleOrderControllerTests {
13- let sut : PointOfSaleOrderController
1413 let mockOrderService = MockPOSOrderService ( )
1514 let mockReceiptService = MockReceiptService ( )
1615
17- init ( ) {
18- self . sut = PointOfSaleOrderController ( orderService: mockOrderService,
19- receiptService: mockReceiptService)
20- }
21-
16+ @available ( iOS 17 . 0 , * )
2217 @Test func syncOrder_without_items_doesnt_call_orderService( ) async throws {
2318 // Given
19+ let sut = PointOfSaleOrderController ( orderService: mockOrderService,
20+ receiptService: mockReceiptService)
2421
2522 // When
2623 await sut. syncOrder ( for: [ ] , retryHandler: { } )
@@ -29,8 +26,11 @@ struct PointOfSaleOrderControllerTests {
2926 #expect( mockOrderService. syncOrderWasCalled == false )
3027 }
3128
29+ @available ( iOS 17 . 0 , * )
3230 @Test func syncOrder_with_cart_matching_order_doesnt_call_orderService( ) async throws {
3331 // Given
32+ let sut = PointOfSaleOrderController ( orderService: mockOrderService,
33+ receiptService: mockReceiptService)
3434 let orderItem = OrderItem . fake ( ) . copy ( quantity: 1 )
3535 let fakeOrder = Order . fake ( ) . copy ( items: [ orderItem] )
3636 let cartItem = makeItem ( orderItemsToMatch: [ orderItem] )
@@ -46,8 +46,11 @@ struct PointOfSaleOrderControllerTests {
4646 #expect( mockOrderService. syncOrderWasCalled == false )
4747 }
4848
49+ @available ( iOS 17 . 0 , * )
4950 @Test func syncOrder_when_already_syncing_doesnt_call_orderService( ) async throws {
5051 // Given
52+ let sut = PointOfSaleOrderController ( orderService: mockOrderService,
53+ receiptService: mockReceiptService)
5154 mockOrderService. simulateSyncing = true
5255 Task {
5356 await sut. syncOrder ( for: [ makeItem ( quantity: 1 ) ] , retryHandler: { } )
@@ -64,6 +67,7 @@ struct PointOfSaleOrderControllerTests {
6467 #expect( mockOrderService. syncOrderWasCalled == false )
6568 }
6669
70+ @available ( iOS 17 . 0 , * )
6771 @Test func syncOrder_with_no_previous_order_calls_orderService( ) async throws {
6872 // Given
6973 let currencySettings = CurrencySettings ( currencyCode: . AUD,
@@ -82,8 +86,11 @@ struct PointOfSaleOrderControllerTests {
8286 #expect( mockOrderService. spySyncOrderCurrency == . AUD)
8387 }
8488
89+ @available ( iOS 17 . 0 , * )
8590 @Test func syncOrder_with_changes_from_previous_order_calls_orderService( ) async throws {
8691 // Given
92+ let sut = PointOfSaleOrderController ( orderService: mockOrderService,
93+ receiptService: mockReceiptService)
8794 let cartItem = makeItem ( quantity: 1 )
8895 let orderItem = OrderItem . fake ( ) . copy ( quantity: 1 )
8996 let fakeOrder = Order . fake ( ) . copy ( items: [ orderItem] )
@@ -100,25 +107,35 @@ struct PointOfSaleOrderControllerTests {
100107 #expect( mockOrderService. syncOrderWasCalled)
101108 }
102109
110+ @available ( iOS 17 . 0 , * )
103111 @Test func syncOrder_with_no_previous_order_sets_orderState_syncing_then_loaded( ) async throws {
104112 // Given
113+ let sut = PointOfSaleOrderController ( orderService: mockOrderService,
114+ receiptService: mockReceiptService)
105115 let fakeOrder = Order . fake ( )
106116 mockOrderService. orderToReturn = fakeOrder
107- var cancellables = Set < AnyCancellable > ( )
108- var orderStates : [ PointOfSaleInternalOrderState ] = [ ]
109- await confirmation ( ) { confirmation in
110- // We can use `withObservationTracking` when we move to @Observable
111- sut. orderStatePublisher. collect ( 3 )
112- . sink { orderState in
113- orderStates. append ( contentsOf: orderState)
117+ var orderStates : [ PointOfSaleInternalOrderState ] = [ sut. orderState]
118+ var orderStateAppendTask : Task < Void , Never > ? = nil
119+ await confirmation ( expectedCount: 2 ) { confirmation in
120+ @Sendable func observeOrderState( ) {
121+ withObservationTracking {
122+ _ = sut. orderState
123+ } onChange: {
124+ orderStateAppendTask = Task { @MainActor in
125+ orderStates. append ( sut. orderState)
126+ }
114127 confirmation ( )
128+ observeOrderState ( )
115129 }
116- . store ( in: & cancellables)
130+ }
131+ observeOrderState ( )
117132
118133 // When
119134 await sut. syncOrder ( for: [ makeItem ( ) ] , retryHandler: { } )
120135 }
121136
137+ await orderStateAppendTask? . value
138+
122139 // Then
123140 #expect( orderStates == [
124141 . idle,
@@ -128,25 +145,35 @@ struct PointOfSaleOrderControllerTests {
128145 ] )
129146 }
130147
148+ @available ( iOS 17 . 0 , * )
131149 @Test func syncOrder_with_order_sync_failure_sets_orderState_syncing_then_error( ) async throws {
132150 // Given
151+ let sut = PointOfSaleOrderController ( orderService: mockOrderService,
152+ receiptService: mockReceiptService)
133153 mockOrderService. orderToReturn = nil
134154
135- var cancellables = Set < AnyCancellable > ( )
136- var orderStates : [ PointOfSaleInternalOrderState ] = [ ]
137- await confirmation ( ) { confirmation in
138- // We can use `withObservationTracking` when we move to @Observable
139- sut. orderStatePublisher. collect ( 3 )
140- . sink { orderState in
141- orderStates. append ( contentsOf: orderState)
155+ var orderStates : [ PointOfSaleInternalOrderState ] = [ sut. orderState]
156+ var orderStateAppendTask : Task < Void , Never > ? = nil
157+ await confirmation ( expectedCount: 2 ) { confirmation in
158+ @Sendable func observeOrderState( ) {
159+ withObservationTracking {
160+ _ = sut. orderState
161+ } onChange: {
162+ orderStateAppendTask = Task { @MainActor in
163+ orderStates. append ( sut. orderState)
164+ }
142165 confirmation ( )
166+ observeOrderState ( )
143167 }
144- . store ( in: & cancellables)
168+ }
169+ observeOrderState ( )
145170
146171 // When
147172 await sut. syncOrder ( for: [ makeItem ( ) ] , retryHandler: { } )
148173 }
149174
175+ await orderStateAppendTask? . value
176+
150177 // Then
151178 #expect( orderStates == [
152179 . idle,
@@ -157,8 +184,11 @@ struct PointOfSaleOrderControllerTests {
157184 ] )
158185 }
159186
187+ @available ( iOS 17 . 0 , * )
160188 @Test func sendReceipt_when_there_is_no_order_then_will_not_trigger( ) async throws {
161189 // Given
190+ let sut = PointOfSaleOrderController ( orderService: mockOrderService,
191+ receiptService: mockReceiptService)
162192163193
164194 // When
@@ -169,8 +199,11 @@ struct PointOfSaleOrderControllerTests {
169199 #expect( !mockReceiptService. sendReceiptWasCalled)
170200 }
171201
202+ @available ( iOS 17 . 0 , * )
172203 @Test func sendReceipt_calls_both_updateOrder_and_sendReceipt( ) async throws {
173204 // Given
205+ let sut = PointOfSaleOrderController ( orderService: mockOrderService,
206+ receiptService: mockReceiptService)
174207 let order = Order . fake ( )
175208 let recipientEmail = " [email protected] " 176209 mockOrderService. orderToReturn = order
@@ -187,9 +220,12 @@ struct PointOfSaleOrderControllerTests {
187220 #expect( mockReceiptService. sendReceiptWasCalled)
188221 }
189222
223+ @available ( iOS 17 . 0 , * )
190224 @Test func collectCashPayment_when_no_order_then_fails_with_noOrder_error( ) async throws {
191225 do {
192226 // Given/When
227+ let sut = PointOfSaleOrderController ( orderService: mockOrderService,
228+ receiptService: mockReceiptService)
193229 try await sut. collectCashPayment ( )
194230 } catch let error as PointOfSaleOrderController . PointOfSaleOrderControllerError {
195231 // Then
@@ -198,6 +234,7 @@ struct PointOfSaleOrderControllerTests {
198234 }
199235
200236 @MainActor
237+ @available ( iOS 17 . 0 , * )
201238 @Test func collectCashPayment_when_successful_calls_celebrate( ) async throws {
202239 // Given
203240 let sampleSiteID : Int64 = 1234
@@ -218,7 +255,7 @@ struct PointOfSaleOrderControllerTests {
218255 let completionResult : Bool = await withCheckedContinuation { continuation in
219256 mockStores. whenReceivingAction ( ofType: OrderAction . self) { action in
220257 switch action {
221- case let . updateOrder( siteID , order, _, _, onCompletion) :
258+ case let . updateOrder( _ , order, _, _, onCompletion) :
222259 onCompletion ( . success( order) )
223260 continuation. resume ( returning: true )
224261 default :
@@ -244,17 +281,17 @@ struct PointOfSaleOrderControllerTests {
244281 private let analyticsProvider = MockAnalyticsProvider ( )
245282 private let orderService = MockPOSOrderService ( )
246283 private let receiptService = MockReceiptService ( )
247- private let sut : PointOfSaleOrderController
248284
249285 init ( ) {
250286 analytics = WooAnalytics ( analyticsProvider: analyticsProvider)
251- sut = PointOfSaleOrderController ( orderService: orderService,
252- receiptService: receiptService,
253- analytics: analytics)
254287 }
255288
289+ @available ( iOS 17 . 0 , * )
256290 @Test func syncOrder_when_create_order_then_tracks_order_creation_success_event( ) async throws {
257291 // Given
292+ let sut = PointOfSaleOrderController ( orderService: orderService,
293+ receiptService: receiptService,
294+ analytics: analytics)
258295 let fakeOrderItem = OrderItem . fake ( ) . copy ( quantity: 1 )
259296 let fakeOrder = Order . fake ( )
260297 let fakeCartItem = makeItem ( orderItemsToMatch: [ fakeOrderItem] )
@@ -267,23 +304,16 @@ struct PointOfSaleOrderControllerTests {
267304 #expect( analyticsProvider. receivedEvents. first ( where: { $0 == " order_creation_success " } ) != nil )
268305 }
269306
307+ @available ( iOS 17 . 0 , * )
270308 @Test func syncOrder_when_create_order_fails_with_order_service_error_then_tracks_order_creation_failure_event( ) async throws {
271309 // Given
272- // 3 states are expected to be confirmed before returning orderState: .idle, .syncing. .error
273- let confirmationOrderStates = 3
274- var cancellables = Set < AnyCancellable > ( )
310+ let sut = PointOfSaleOrderController ( orderService : orderService ,
311+ receiptService : receiptService ,
312+ analytics : analytics )
275313 orderService. orderToReturn = nil
276314
277- await confirmation ( ) { confirmation in
278- sut. orderStatePublisher. collect ( confirmationOrderStates)
279- . sink { orderState in
280- confirmation ( )
281- }
282- . store ( in: & cancellables)
283-
284- // When
285- await sut. syncOrder ( for: [ makeItem ( ) ] , retryHandler: { } )
286- }
315+ // When
316+ await sut. syncOrder ( for: [ makeItem ( ) ] , retryHandler: { } )
287317
288318 // Then
289319 #expect( analyticsProvider. receivedEvents. first ( where: { $0 == " order_creation_failed " } ) != nil )
0 commit comments