diff --git a/packages/core/e2e/graphql/generated-e2e-admin-types.ts b/packages/core/e2e/graphql/generated-e2e-admin-types.ts index 95ef3dffdc..dc39bccf97 100644 --- a/packages/core/e2e/graphql/generated-e2e-admin-types.ts +++ b/packages/core/e2e/graphql/generated-e2e-admin-types.ts @@ -4475,7 +4475,7 @@ export type GetCustomerQuery = { __typename?: 'Query' } & { export type AttemptLoginMutationVariables = { username: Scalars['String']; password: Scalars['String']; - rememberMe: Scalars['Boolean']; + rememberMe?: Maybe; }; export type AttemptLoginMutation = { __typename?: 'Mutation' } & { diff --git a/packages/core/e2e/graphql/shared-definitions.ts b/packages/core/e2e/graphql/shared-definitions.ts index e4c4464a82..f4a80a1ee4 100644 --- a/packages/core/e2e/graphql/shared-definitions.ts +++ b/packages/core/e2e/graphql/shared-definitions.ts @@ -194,7 +194,7 @@ export const GET_CUSTOMER = gql` `; export const ATTEMPT_LOGIN = gql` - mutation AttemptLogin($username: String!, $password: String!, $rememberMe: Boolean!) { + mutation AttemptLogin($username: String!, $password: String!, $rememberMe: Boolean) { login(username: $username, password: $password, rememberMe: $rememberMe) { user { ...CurrentUser diff --git a/packages/core/e2e/shop-order.e2e-spec.ts b/packages/core/e2e/shop-order.e2e-spec.ts index 71c1f77590..dc411e3ce6 100644 --- a/packages/core/e2e/shop-order.e2e-spec.ts +++ b/packages/core/e2e/shop-order.e2e-spec.ts @@ -12,6 +12,7 @@ import { testSuccessfulPaymentMethod, } from './fixtures/test-payment-methods'; import { + AttemptLogin, CreateAddressInput, GetCountryList, GetCustomer, @@ -36,6 +37,7 @@ import { TransitionToState, } from './graphql/generated-e2e-shop-types'; import { + ATTEMPT_LOGIN, GET_COUNTRY_LIST, GET_CUSTOMER, GET_CUSTOMER_LIST, @@ -80,7 +82,7 @@ describe('Shop orders', () => { await server.init({ initialData, productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'), - customerCount: 2, + customerCount: 3, }); await adminClient.asSuperAdmin(); }, TEST_SETUP_TIMEOUT_MS); @@ -874,4 +876,93 @@ describe('Shop orders', () => { }); }); }); + + describe('order merging', () => { + let customers: GetCustomerList.Items[]; + + beforeAll(async () => { + const result = await adminClient.query(GET_CUSTOMER_LIST); + customers = result.customers.items; + }); + + it('merges guest order with no existing order', async () => { + await shopClient.asAnonymousUser(); + const { addItemToOrder } = await shopClient.query< + AddItemToOrder.Mutation, + AddItemToOrder.Variables + >(ADD_ITEM_TO_ORDER, { + productVariantId: 'T_1', + quantity: 1, + }); + + expect(addItemToOrder!.lines.length).toBe(1); + expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_1'); + + await shopClient.query(ATTEMPT_LOGIN, { + username: customers[1].emailAddress, + password: 'test', + }); + const { activeOrder } = await shopClient.query(GET_ACTIVE_ORDER); + + expect(activeOrder!.lines.length).toBe(1); + expect(activeOrder!.lines[0].productVariant.id).toBe('T_1'); + }); + + it('merges guest order with existing order', async () => { + await shopClient.asAnonymousUser(); + const { addItemToOrder } = await shopClient.query< + AddItemToOrder.Mutation, + AddItemToOrder.Variables + >(ADD_ITEM_TO_ORDER, { + productVariantId: 'T_2', + quantity: 1, + }); + + expect(addItemToOrder!.lines.length).toBe(1); + expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_2'); + + await shopClient.query(ATTEMPT_LOGIN, { + username: customers[1].emailAddress, + password: 'test', + }); + const { activeOrder } = await shopClient.query(GET_ACTIVE_ORDER); + + expect(activeOrder!.lines.length).toBe(2); + expect(activeOrder!.lines[0].productVariant.id).toBe('T_1'); + expect(activeOrder!.lines[1].productVariant.id).toBe('T_2'); + }); + + /** + * See https://github.com/vendure-ecommerce/vendure/issues/263 + */ + it('does not merge when logging in to a different account (issue #263)', async () => { + await shopClient.query(ATTEMPT_LOGIN, { + username: customers[2].emailAddress, + password: 'test', + }); + const { activeOrder } = await shopClient.query(GET_ACTIVE_ORDER); + + expect(activeOrder).toBeNull(); + }); + + it('does not merge when logging back to other account (issue #263)', async () => { + const { addItemToOrder } = await shopClient.query< + AddItemToOrder.Mutation, + AddItemToOrder.Variables + >(ADD_ITEM_TO_ORDER, { + productVariantId: 'T_3', + quantity: 1, + }); + + await shopClient.query(ATTEMPT_LOGIN, { + username: customers[1].emailAddress, + password: 'test', + }); + const { activeOrder } = await shopClient.query(GET_ACTIVE_ORDER); + + expect(activeOrder!.lines.length).toBe(2); + expect(activeOrder!.lines[0].productVariant.id).toBe('T_1'); + expect(activeOrder!.lines[1].productVariant.id).toBe('T_2'); + }); + }); }); diff --git a/packages/core/src/service/services/order.service.ts b/packages/core/src/service/services/order.service.ts index 1f38523132..bfef3f62fe 100644 --- a/packages/core/src/service/services/order.service.ts +++ b/packages/core/src/service/services/order.service.ts @@ -717,6 +717,11 @@ export class OrderService { guestOrder?: Order, existingOrder?: Order, ): Promise { + if (guestOrder && guestOrder.customer) { + // In this case the "guest order" is actually an order of an existing Customer, + // so we do not want to merge at all. See https://github.com/vendure-ecommerce/vendure/issues/263 + return existingOrder; + } const mergeResult = await this.orderMerger.merge(guestOrder, existingOrder); const { orderToDelete, linesToInsert } = mergeResult; let { order } = mergeResult;