-
Notifications
You must be signed in to change notification settings - Fork 121
Customer search: Fill customer fields on Order screen #7885
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
5869ec0
d7060dc
e05a33d
db2fb24
5419298
fae8db5
dff4aed
64cb6fe
c5fbc90
1733984
9cf8388
ed04317
5dffa97
7b58531
121ba8a
d343863
2b772b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -172,9 +172,8 @@ struct EditOrderAddressForm<ViewModel: AddressFormViewModelProtocol>: View { | |
| .notice($viewModel.notice) | ||
| .sheet(isPresented: $showingCustomerSearch, content: { | ||
| OrderCustomerListView(siteID: viewModel.siteID, onCustomerTapped: { customer in | ||
| // Not implemented yet. | ||
| print("3 - Customer Callback. Fill Order data with Customer details") | ||
| print("4 - Customer ID: \(customer.customerID) - Name: \(customer.firstName ?? ""))") | ||
| viewModel.customerSelectedFromSearch(customer: customer) | ||
| showingCustomerSearch = false | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can move to the view model too if you like? Can wait for a future PR |
||
| }) | ||
| }) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -716,6 +716,76 @@ final class EditOrderAddressFormViewModelTests: XCTestCase { | |
| // Then | ||
| XCTAssertEqual(notice, AddressFormViewModel.NoticeFactory.createInvalidEmailNotice()) | ||
| } | ||
|
|
||
| func test_OrderAddressForm_billing_fields_are_updated_when_customerSelectedFromSearch() { | ||
| // Given | ||
| let viewModel = EditOrderAddressFormViewModel(order: Order.fake(), type: .billing) | ||
| let customer = Customer.fake().copy( | ||
| email: "[email protected]", | ||
| firstName: "Johnny", | ||
| lastName: "Appleseed", | ||
| billing: sampleAddressWithEmptyNullableFields(), | ||
| shipping: sampleAddressWithEmptyNullableFields() | ||
| ) | ||
|
|
||
| // When | ||
| viewModel.customerSelectedFromSearch(customer: customer) | ||
|
|
||
| // Then | ||
| XCTAssertEqual(viewModel.fields.email, customer.email) | ||
|
||
| XCTAssertEqual(viewModel.fields.firstName, customer.firstName) | ||
| XCTAssertEqual(viewModel.fields.lastName, customer.lastName) | ||
| XCTAssertEqual(viewModel.fields.company, customer.billing?.company) | ||
| XCTAssertEqual(viewModel.fields.address1, customer.billing?.address1) | ||
| XCTAssertEqual(viewModel.fields.address2, customer.billing?.address2) | ||
| XCTAssertEqual(viewModel.fields.city, customer.billing?.city) | ||
| XCTAssertEqual(viewModel.fields.state, customer.billing?.state) | ||
| XCTAssertEqual(viewModel.fields.postcode, customer.billing?.postcode) | ||
| XCTAssertEqual(viewModel.fields.country, customer.billing?.country) | ||
| XCTAssertEqual(viewModel.fields.phone, customer.billing?.phone) | ||
| } | ||
|
|
||
| func test_OrderAddressForm_shipping_fields_are_updated_when_customerSelectedFromSearch() { | ||
| // Given | ||
| let viewModel = EditOrderAddressFormViewModel(order: Order.fake(), type: .shipping) | ||
| let customer = Customer.fake().copy( | ||
| email: "[email protected]", | ||
| firstName: "Johnny", | ||
| lastName: "Appleseed", | ||
| billing: sampleAddressWithEmptyNullableFields(), | ||
| shipping: sampleAddressWithEmptyNullableFields() | ||
| ) | ||
|
|
||
| // When | ||
| viewModel.customerSelectedFromSearch(customer: customer) | ||
|
|
||
| // Then | ||
| XCTAssertEqual(viewModel.fields.email, customer.email) | ||
| XCTAssertEqual(viewModel.fields.firstName, customer.firstName) | ||
| XCTAssertEqual(viewModel.fields.lastName, customer.lastName) | ||
| XCTAssertEqual(viewModel.fields.company, customer.shipping?.company) | ||
| XCTAssertEqual(viewModel.fields.address1, customer.shipping?.address1) | ||
| XCTAssertEqual(viewModel.fields.address2, customer.shipping?.address2) | ||
| XCTAssertEqual(viewModel.fields.city, customer.shipping?.city) | ||
| XCTAssertEqual(viewModel.fields.state, customer.shipping?.state) | ||
| XCTAssertEqual(viewModel.fields.postcode, customer.shipping?.postcode) | ||
| XCTAssertEqual(viewModel.fields.country, customer.shipping?.country) | ||
| XCTAssertEqual(viewModel.fields.phone, customer.shipping?.phone) | ||
| } | ||
|
|
||
| func test_OrderAddressForm_shows_different_address_form_fields_when_addresses_differ_and_customerSelectedFromSearch() { | ||
| // Given | ||
| let viewModel = EditOrderAddressFormViewModel(order: Order.fake(), type: .billing) | ||
| let billing = sampleAddressWithEmptyNullableFields() | ||
| let shipping = Address.fake().copy(address1: "123 different fake street") | ||
| let customer = Customer.fake().copy(billing: billing, shipping: shipping) | ||
|
|
||
| // When | ||
| viewModel.customerSelectedFromSearch(customer: customer) | ||
|
|
||
| // Then | ||
| XCTAssertTrue(viewModel.showDifferentAddressForm) | ||
| } | ||
|
Comment on lines
+776
to
+788
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👏👏👏 So good to be able to test this kind of thing with confidence |
||
| } | ||
|
|
||
| private extension EditOrderAddressFormViewModelTests { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import XCTest | ||
| @testable import WooCommerce | ||
| import Yosemite | ||
|
|
||
| final class CustomerSearchUICommandTests: XCTestCase { | ||
| private let sampleSiteID: Int64 = 123 | ||
|
|
||
| override func setUp() { | ||
| super.setUp() | ||
| } | ||
|
|
||
| override func tearDown() { | ||
| super.tearDown() | ||
| } | ||
|
||
|
|
||
| func test_searchResultsPredicate_includes_siteID_and_keyword_when_keyword() { | ||
| // Given | ||
| let command = CustomerSearchUICommand(siteID: sampleSiteID) { _ in } | ||
|
|
||
| // When | ||
| let predicate = command.searchResultsPredicate(keyword: "some") | ||
| let expectedQuery = "siteID == 123 AND ANY searchResults.keyword == \"some\"" | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Double check if this actually filters down multiple sites.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed 👍 , for 2 customers with the same name and customer ID, in two different sites, only one is shown. |
||
|
|
||
| // Then | ||
| XCTAssertEqual(predicate?.predicateFormat, expectedQuery) | ||
| } | ||
|
|
||
| func test_cellViewModel_display_correct_customer_details() { | ||
| let command = CustomerSearchUICommand(siteID: sampleSiteID) { _ in } | ||
| let customer = Customer( | ||
| siteID: sampleSiteID, | ||
| customerID: 1, | ||
| email: "[email protected]", | ||
| firstName: "John", | ||
| lastName: "W", | ||
| billing: nil, | ||
| shipping: nil | ||
| ) | ||
|
|
||
| let cellViewModel = command.createCellViewModel(model: customer) | ||
|
|
||
| XCTAssertEqual(cellViewModel.id, String(customer.customerID)) | ||
| XCTAssertEqual(cellViewModel.title, "\(customer.firstName ?? "") \(customer.lastName ?? "")") | ||
| XCTAssertEqual(cellViewModel.subtitle, String(customer.email)) | ||
| } | ||
| } | ||
iamgabrielma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -184,4 +184,23 @@ final class CustomerStoreTests: XCTestCase { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| XCTAssertEqual(storedCustomer?.customerID, dummyCustomerID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| XCTAssertEqual(storedCustomer?.firstName, "John") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func test_searchCustomers_returns_no_customers_when_customer_is_not_registered() throws { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Given | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let customerID = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| network.simulateResponse(requestUrlSuffix: "customers", filename: "wc-analytics-customers") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| network.simulateResponse(requestUrlSuffix: "customers/\(customerID)", filename: "customer") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // When | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let response: Result<[Networking.Customer], Error> = waitFor { promise in | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let action = CustomerAction.searchCustomers(siteID: self.dummySiteID, keyword: self.dummyKeyword) { result in | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| promise(result) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.dispatcher.dispatch(action) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let customers = try response.get() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| XCTAssertEqual(customers.count, 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice test! This is business logic which is well worth checking. However... it doesn't really test what it looks like. I had a go at making it fail (by removing the The reason is that we don't have anyone in We can improve it further though... here's another way to do it, which more directly checks that the request is not sent, without needing to see what the customer response is: (N.B. This still needs an analytics customer with
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh that's nice! Made the changes on 7b58531 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of the name fields on customer, I think we should use
address.firstNameandaddress.lastName. Testing Android, with my always-strange test data with different names all over the place, shows that they use these fields to update the data.This makes sense, because the shipping address may be to a different person, e.g. if it's a gift.
There's
address.email, which could differ fromcustomer.email. Android seems to usebillingAddress.emailthere. The email for the secondary fields isn't shown on either platform... but I'm not sure whether it's used in any requests.It's worth checking that: what happens if we save a different email address in each place? Does it get added to the order twice? Is that what Android does? Is it a bug, one way or the other?
It's also just a nicer function... "Populate address fields with address"
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. I made the changes here: 121ba8a
Something that peaks my interest with this change is that the email property is now an optional, but it can't actually be optional because is a mandatory field for shippable orders. This seems to happen because when we declare both
fieldsandsecondaryFieldsin the protocol, both inherit fromAddressFormFields, however these have slightly different parameters in the API (for example shipping details does not have an email field)This seems a bug from our end when declaring the model, as with the
emailexample above, we're reusing the sameAddressFormFieldsmodel for both shipping and billing, but these are not exactly the same in the API or WC core (it would seem that the API doc is outdated as well, as phone does not appear as property for shipping but in WC core it does). I've opened an issue here: #7993 as most likely this can be improved.