11import XCTest
22import TestKit
33import Fakes
4+ import Combine
45
56@testable import WooCommerce
67@testable import Yosemite
@@ -11,6 +12,12 @@ class RemoteOrderSynchronizerTests: XCTestCase {
1112 private let sampleProductID : Int64 = 234
1213 private let sampleInputID : Int64 = 345
1314 private let sampleShippingID : Int64 = 456
15+ private var subscriptions = Set < AnyCancellable > ( )
16+
17+ override func setUp( ) {
18+ super. setUp ( )
19+ subscriptions. removeAll ( )
20+ }
1421
1522 func test_sending_status_input_updates_local_order( ) throws {
1623 // Given
@@ -134,4 +141,244 @@ class RemoteOrderSynchronizerTests: XCTestCase {
134141 // Then
135142 XCTAssertEqual ( synchronizer. order. shippingLines, [ ] )
136143 }
144+
145+ func test_sending_product_input_triggers_order_creation( ) {
146+ // Given
147+ let product = Product . fake ( ) . copy ( productID: sampleProductID)
148+ let stores = MockStoresManager ( sessionManager: . testingInstance)
149+ let synchronizer = RemoteOrderSynchronizer ( siteID: sampleSiteID, stores: stores)
150+
151+ // When
152+ let orderCreationInvoked : Bool = waitFor { promise in
153+ stores. whenReceivingAction ( ofType: OrderAction . self) { action in
154+ switch action {
155+ case . createOrder:
156+ promise ( true )
157+ default :
158+ promise ( false )
159+ }
160+ }
161+
162+ let input = OrderSyncProductInput ( product: . product( product) , quantity: 1 )
163+ synchronizer. setProduct. send ( input)
164+ }
165+
166+ // Then
167+ XCTAssertTrue ( orderCreationInvoked)
168+ }
169+
170+ func test_sending_addresses_input_triggers_order_creation( ) {
171+ // Given
172+ let address = Address . fake ( ) . copy ( firstName: " Woo " , lastName: " Customer " )
173+ let stores = MockStoresManager ( sessionManager: . testingInstance)
174+ let synchronizer = RemoteOrderSynchronizer ( siteID: sampleSiteID, stores: stores)
175+
176+ // When
177+ let orderCreationInvoked : Bool = waitFor { promise in
178+ stores. whenReceivingAction ( ofType: OrderAction . self) { action in
179+ switch action {
180+ case . createOrder:
181+ promise ( true )
182+ default :
183+ promise ( false )
184+ }
185+ }
186+
187+ let input = OrderSyncAddressesInput ( billing: address, shipping: address)
188+ synchronizer. setAddresses. send ( input)
189+ }
190+
191+ // Then
192+ XCTAssertTrue ( orderCreationInvoked)
193+ }
194+
195+ func test_sending_shipping_input_triggers_order_creation( ) {
196+ // Given
197+ let shippingLine = ShippingLine . fake ( ) . copy ( shippingID: sampleShippingID)
198+ let stores = MockStoresManager ( sessionManager: . testingInstance)
199+ let synchronizer = RemoteOrderSynchronizer ( siteID: sampleSiteID, stores: stores)
200+
201+ // When
202+ let orderCreationInvoked : Bool = waitFor { promise in
203+ stores. whenReceivingAction ( ofType: OrderAction . self) { action in
204+ switch action {
205+ case . createOrder:
206+ promise ( true )
207+ default :
208+ promise ( false )
209+ }
210+ }
211+
212+ synchronizer. setShipping. send ( shippingLine)
213+ }
214+
215+ // Then
216+ XCTAssertTrue ( orderCreationInvoked)
217+ }
218+
219+ func test_sending_fee_input_triggers_order_creation( ) {
220+ // Given
221+ let fee = OrderFeeLine . fake ( ) . copy ( )
222+ let stores = MockStoresManager ( sessionManager: . testingInstance)
223+ let synchronizer = RemoteOrderSynchronizer ( siteID: sampleSiteID, stores: stores)
224+
225+ // When
226+ let orderCreationInvoked : Bool = waitFor { promise in
227+ stores. whenReceivingAction ( ofType: OrderAction . self) { action in
228+ switch action {
229+ case . createOrder:
230+ promise ( true )
231+ default :
232+ promise ( false )
233+ }
234+ }
235+
236+ synchronizer. setFee. send ( fee)
237+ }
238+
239+ // Then
240+ XCTAssertTrue ( orderCreationInvoked)
241+ }
242+
243+ func test_states_are_properly_set_upon_success_order_creation( ) {
244+ // Given
245+ let product = Product . fake ( ) . copy ( productID: sampleProductID)
246+ let stores = MockStoresManager ( sessionManager: . testingInstance)
247+ let synchronizer = RemoteOrderSynchronizer ( siteID: sampleSiteID, stores: stores)
248+ stores. whenReceivingAction ( ofType: OrderAction . self) { action in
249+ switch action {
250+ case . createOrder( _, _, let completion) :
251+ completion ( . success( . fake( ) ) )
252+ default :
253+ XCTFail ( " Unexpected action: \( action) " )
254+ }
255+ }
256+
257+ // When
258+ let input = OrderSyncProductInput ( product: . product( product) , quantity: 1 )
259+ synchronizer. setProduct. send ( input)
260+
261+ let states : [ OrderSyncState ] = waitFor { promise in
262+ synchronizer. statePublisher
263+ . dropFirst ( )
264+ . collect ( 2 )
265+ . sink { states in
266+ promise ( states)
267+ }
268+ . store ( in: & self . subscriptions)
269+ }
270+
271+ // Then
272+ XCTAssertEqual ( states, [ . syncing, . synced] )
273+ }
274+
275+ func test_states_are_properly_set_upon_failing_order_creation( ) {
276+ // Given
277+ let product = Product . fake ( ) . copy ( productID: sampleProductID)
278+ let error = NSError ( domain: " " , code: 0 , userInfo: nil )
279+ let stores = MockStoresManager ( sessionManager: . testingInstance)
280+ let synchronizer = RemoteOrderSynchronizer ( siteID: sampleSiteID, stores: stores)
281+ stores. whenReceivingAction ( ofType: OrderAction . self) { action in
282+ switch action {
283+ case . createOrder( _, _, let completion) :
284+ completion ( . failure( error) )
285+ default :
286+ XCTFail ( " Unexpected action: \( action) " )
287+ }
288+ }
289+
290+ // When
291+ let input = OrderSyncProductInput ( product: . product( product) , quantity: 1 )
292+ synchronizer. setProduct. send ( input)
293+
294+ let states : [ OrderSyncState ] = waitFor { promise in
295+ synchronizer. statePublisher
296+ . dropFirst ( )
297+ . collect ( 2 )
298+ . sink { states in
299+ promise ( states)
300+ }
301+ . store ( in: & self . subscriptions)
302+ }
303+
304+ // Then
305+ assertEqual ( states, [ . syncing, . error( error) ] )
306+ }
307+
308+ func test_sending_double_input_triggers_only_one_order_creation( ) {
309+ // Given
310+ let product = Product . fake ( ) . copy ( productID: sampleProductID)
311+ let stores = MockStoresManager ( sessionManager: . testingInstance)
312+ let synchronizer = RemoteOrderSynchronizer ( siteID: sampleSiteID, stores: stores)
313+
314+ // When
315+ let exp = expectation ( description: #function)
316+ exp. expectedFulfillmentCount = 1
317+ exp. assertForOverFulfill = true
318+
319+ stores. whenReceivingAction ( ofType: OrderAction . self) { action in
320+ switch action {
321+ case . createOrder:
322+ exp. fulfill ( )
323+ default :
324+ break
325+ }
326+ }
327+
328+ let input1 = OrderSyncProductInput ( product: . product( product) , quantity: 1 )
329+ synchronizer. setProduct. send ( input1)
330+
331+ let input2 = OrderSyncProductInput ( product: . product( product) , quantity: 2 )
332+ synchronizer. setProduct. send ( input2)
333+
334+ // Then
335+ wait ( for: [ exp] , timeout: 1.0 )
336+ }
337+
338+ func test_order_is_created_with_draft_status_and_returned_with_selected_status( ) {
339+ // Given
340+ let stores = MockStoresManager ( sessionManager: . testingInstance)
341+ stores. whenReceivingAction ( ofType: SystemStatusAction . self) { action in // Set version that supports auto-draft
342+ switch action {
343+ case let . fetchSystemPlugin( _, _, onCompletion) :
344+ onCompletion ( . fake( ) . copy ( version: " 6.3.0 " ) )
345+ default :
346+ XCTFail ( " Unexpected action received: \( action) " )
347+ }
348+ }
349+ let synchronizer = RemoteOrderSynchronizer ( siteID: sampleSiteID, stores: stores)
350+ XCTAssertEqual ( synchronizer. order. status, . pending) // initial status
351+
352+ // When
353+ let submittedStatus : OrderStatusEnum = waitFor { promise in
354+ stores. whenReceivingAction ( ofType: OrderAction . self) { action in
355+ switch action {
356+ case let . createOrder( _, order, onCompletion) :
357+ onCompletion ( . success( order) )
358+ promise ( order. status)
359+ default :
360+ XCTFail ( " Unexpected action: \( action) " )
361+ }
362+ }
363+
364+ synchronizer. setFee. send ( . fake( ) )
365+ }
366+
367+ // Then
368+ XCTAssertEqual ( submittedStatus, . autoDraft) // Submitted Status
369+ XCTAssertEqual ( synchronizer. order. status, . pending) // Selected status
370+ }
371+ }
372+
373+ extension OrderSyncState : Equatable {
374+ public static func == ( lhs: OrderSyncState , rhs: OrderSyncState ) -> Bool {
375+ switch ( lhs, rhs) {
376+ case ( . syncing, . syncing) , ( . synced, . synced) :
377+ return true
378+ case ( . error( let error1) , . error( let error2) ) :
379+ return error1 as NSError == error2 as NSError
380+ default :
381+ return false
382+ }
383+ }
137384}
0 commit comments