diff --git a/package.json b/package.json index c7125716..66a3bd6b 100644 --- a/package.json +++ b/package.json @@ -36,12 +36,12 @@ "@types/sharp": "0.28.0", "@types/tmp": "0.2.3", "@typescript-eslint/eslint-plugin": "5.43.0", - "@vendure/admin-ui-plugin": "2.0.6", - "@vendure/asset-server-plugin": "2.0.6", - "@vendure/core": "2.0.6", - "@vendure/email-plugin": "2.0.6", - "@vendure/testing": "2.0.6", - "@vendure/ui-devkit": "2.0.6", + "@vendure/admin-ui-plugin": "2.0.9", + "@vendure/asset-server-plugin": "2.0.9", + "@vendure/core": "2.0.9", + "@vendure/email-plugin": "2.0.9", + "@vendure/testing": "2.0.9", + "@vendure/ui-devkit": "2.0.9", "aws-sdk": "2.1099.0", "copyfiles": "2.4.1", "eslint": "8.0.1", diff --git a/packages/vendure-order-client/CHANGELOG.md b/packages/vendure-order-client/CHANGELOG.md index 899f2f83..6c6f42d4 100644 --- a/packages/vendure-order-client/CHANGELOG.md +++ b/packages/vendure-order-client/CHANGELOG.md @@ -1,3 +1,7 @@ # 2.1.0 (2023-09-20) - Added loading states for currentUser and activeOrder store ([#256](https://github.com/Pinelab-studio/pinelab-vendure-plugins/pull/256)) + +# 2.2.0 (2023-10-03) + +- Added Eligible Shipping Method store with value, loading state and error([#265](https://github.com/Pinelab-studio/pinelab-vendure-plugins/pull/265)) diff --git a/packages/vendure-order-client/package.json b/packages/vendure-order-client/package.json index ca665274..749a48c5 100644 --- a/packages/vendure-order-client/package.json +++ b/packages/vendure-order-client/package.json @@ -1,6 +1,6 @@ { "name": "@pinelab/vendure-order-client", - "version": "2.1.0", + "version": "2.2.0", "description": "A tiny, framework agnostic client for managing active orders and checkout with Vendure.", "author": "Martijn van de Brug ", "homepage": "https://pinelab-plugins.com/", diff --git a/packages/vendure-order-client/src/queries.ts b/packages/vendure-order-client/src/queries.ts index 2d6f17b2..511134ea 100644 --- a/packages/vendure-order-client/src/queries.ts +++ b/packages/vendure-order-client/src/queries.ts @@ -358,4 +358,19 @@ export class GraphqlQueries { } ${this.CURRENT_USER_FIELDS} `; + + GET_ELIGIBLE_SHIPPING_METHODS = gql` + query GetEligibleShippingMethods { + eligibleShippingMethods { + id + name + price + priceWithTax + code + description + metadata + customFields + } + } + `; } diff --git a/packages/vendure-order-client/src/store-helpers.ts b/packages/vendure-order-client/src/store-helpers.ts index 44a7e24f..c516567e 100644 --- a/packages/vendure-order-client/src/store-helpers.ts +++ b/packages/vendure-order-client/src/store-helpers.ts @@ -1,5 +1,5 @@ import { MapStore } from 'nanostores'; -import { ErrorCode, ErrorResult } from './graphql-generated-types'; +import { ErrorResult } from './graphql-generated-types'; /** * Interface defining loading and error states per store @@ -57,7 +57,7 @@ export function HandleLoadingState(storeName: string) { return result; } catch (e: any) { store.setKey('error', { - errorCode: ErrorCode.UnknownError, + errorCode: e?.errorCode, message: e?.message, }); } finally { diff --git a/packages/vendure-order-client/src/vendure-order-client.ts b/packages/vendure-order-client/src/vendure-order-client.ts index 1a30df86..b7453c26 100644 --- a/packages/vendure-order-client/src/vendure-order-client.ts +++ b/packages/vendure-order-client/src/vendure-order-client.ts @@ -42,6 +42,7 @@ import { SetOrderShippingMethodMutation, Success, TransitionOrderToStateMutation, + ShippingMethodQuote, } from './graphql-generated-types'; import { GraphqlQueries } from './queries'; import { setResult, HandleLoadingState, StateStore } from './store-helpers'; @@ -57,6 +58,7 @@ const dummyFragment = gql` `; export type ActiveOrder = ActiveOrderFieldsFragment & T; +export type EligibleShippingMethod = ShippingMethodQuote & T; export type CurrentUser = CurrentUserFieldsFragment; /** @@ -81,6 +83,14 @@ export class VendureOrderClient { data: undefined, }); + $eligibleShippingMethods = map< + StateStore> | undefined> + >({ + loading: false, + error: undefined, + data: undefined, + }); + /** * The store object that holds the current logged in user */ @@ -137,6 +147,7 @@ export class VendureOrderClient { productVariantIds: [productVariantId], quantity, }); + void this.updateEligibleShippingMehods(); return activeOrder; } @@ -179,10 +190,12 @@ export class VendureOrderClient { quantity: -adjustment, // adjustment is negative, so invert it }); } + void this.updateEligibleShippingMehods(); return activeOrder; } async removeOrderLine(orderLineId: Id): Promise> { + void this.updateEligibleShippingMehods(); return await this.adjustOrderLine(orderLineId, 0); } @@ -205,6 +218,7 @@ export class VendureOrderClient { productVariantIds: allVariantIds, quantity: totalQuantity, }); + void this.updateEligibleShippingMehods(); return activeOrder; } @@ -221,6 +235,7 @@ export class VendureOrderClient { this.eventBus.emit('coupon-code-applied', { couponCode, }); + void this.updateEligibleShippingMehods(); return activeOrder; } @@ -242,6 +257,7 @@ export class VendureOrderClient { this.eventBus.emit('coupon-code-removed', { couponCode, }); + void this.updateEligibleShippingMehods(); return activeOrder; } @@ -257,6 +273,7 @@ export class VendureOrderClient { setCustomerForOrder as ActiveOrder ); setResult(this.$activeOrder, activeOrder); + void this.updateEligibleShippingMehods(); return activeOrder; } @@ -272,6 +289,7 @@ export class VendureOrderClient { setOrderShippingAddress as ActiveOrder ); setResult(this.$activeOrder, activeOrder); + void this.updateEligibleShippingMehods(); return activeOrder; } @@ -285,6 +303,7 @@ export class VendureOrderClient { setOrderBillingAddress as ActiveOrder ); setResult(this.$activeOrder, activeOrder); + void this.updateEligibleShippingMehods(); return activeOrder; } @@ -300,6 +319,7 @@ export class VendureOrderClient { setOrderShippingMethod as ActiveOrder ); setResult(this.$activeOrder, activeOrder); + void this.updateEligibleShippingMehods(); return activeOrder; } @@ -437,4 +457,12 @@ export class VendureOrderClient { throw e; } } + + @HandleLoadingState('$eligibleShippingMethods') + private async updateEligibleShippingMehods(): Promise { + const { eligibleShippingMethods } = await this.rawRequest<{ + eligibleShippingMethods: ShippingMethodQuote[]; + }>(this.queries.GET_ELIGIBLE_SHIPPING_METHODS); + setResult(this.$eligibleShippingMethods, eligibleShippingMethods); + } } diff --git a/packages/vendure-order-client/test/vendure-order-client.spec.ts b/packages/vendure-order-client/test/vendure-order-client.spec.ts index 3d3f6168..2cd5561a 100644 --- a/packages/vendure-order-client/test/vendure-order-client.spec.ts +++ b/packages/vendure-order-client/test/vendure-order-client.spec.ts @@ -21,7 +21,7 @@ import { import { initialData } from './initial-data'; import { testPaymentMethodHandler } from './test-payment-method-handler'; import { useStore } from '@nanostores/vue'; -import { MapStore } from 'nanostores'; +import { MapStore, listenKeys } from 'nanostores'; const storage: any = {}; const window = { @@ -52,423 +52,477 @@ let latestEmittedEvent: [string, VendureOrderEvent]; * T_2 is used as test product variant * T_1 is used as test order line */ -describe('Vendure order client', () => { - let server: TestServer; - const couponCodeName = 'couponCodeName'; - let activeOrderCode: string | undefined; - let adminClient: SimpleGraphQLClient; - let activeOrderStore: any; - let currentUserStore: any; - const regionNames = new Intl.DisplayNames(['en'], { type: 'region' }); - - beforeAll(async () => { - registerInitializer('sqljs', new SqljsInitializer('__data__')); - const config = mergeConfig(testConfig, { - paymentOptions: { - paymentMethodHandlers: [testPaymentMethodHandler], - }, - authOptions: { - requireVerification: false, - }, - logger: new DefaultLogger({ level: LogLevel.Debug }), - }); - ({ server, adminClient } = createTestEnvironment(config)); - await server.init({ - initialData, - productsCsvPath: path.join(__dirname, './product-import.csv'), - }); - }, 60000); - - type SearchFn = () => Promise; - - async function testActiveOrderLoadingState( - awaitAbleFunction: SearchFn - ): Promise { - return await testLoadingState(awaitAbleFunction, client.$activeOrder); - } +describe( + 'Vendure order client', + () => { + let server: TestServer; + const couponCodeName = 'couponCodeName'; + let activeOrderCode: string | undefined; + let adminClient: SimpleGraphQLClient; + let activeOrderStore: any; + let currentUserStore: any; + const regionNames = new Intl.DisplayNames(['en'], { type: 'region' }); + + beforeAll(async () => { + registerInitializer('sqljs', new SqljsInitializer('__data__')); + const config = mergeConfig(testConfig, { + paymentOptions: { + paymentMethodHandlers: [testPaymentMethodHandler], + }, + authOptions: { + requireVerification: false, + }, + logger: new DefaultLogger({ level: LogLevel.Debug }), + }); + ({ server, adminClient } = createTestEnvironment(config)); + await server.init({ + initialData, + productsCsvPath: path.join(__dirname, './product-import.csv'), + }); + }, 60000); - async function testLoadingState( - awaitAbleFunction: SearchFn, - store: MapStore - ): Promise { - expect(store.value?.loading).toBe(false); - const promise = awaitAbleFunction(); - expect(store.value?.loading).toBe(true); - const result = await promise; - expect(store.value?.loading).toBe(false); - return result; - } + type PromiseFn = () => Promise; - async function testCurrentUserLoadingState( - awaitAbleFunction: SearchFn - ): Promise { - return await testLoadingState(awaitAbleFunction, client.$currentUser); - } + async function testActiveOrderLoadingState( + awaitAbleFunction: PromiseFn + ): Promise { + return await testLoadingState(awaitAbleFunction, client.$activeOrder); + } - it('Starts the server successfully', async () => { - expect(server.app.getHttpServer).toBeDefined(); - }); - - it('Creates a promotion', async () => { - await adminClient.asSuperAdmin(); - const { createPromotion } = await adminClient.query( - gql` - mutation CreatePromotionMutation($name: String!) { - createPromotion( - input: { - enabled: true - couponCode: $name - translations: [{ languageCode: en, name: $name }] - conditions: [] - actions: [ - { - code: "order_fixed_discount" - arguments: [{ name: "discount", value: "10" }] - } - ] - } - ) { - ... on Promotion { - id - name - couponCode - } - ... on ErrorResult { - errorCode + async function testEligibleShippingMethodsLoadingState( + awaitAbleFunction: PromiseFn, + clientMethodName: string + ): Promise { + let wasSetToTrue = false; + let wasFinallySetToFalse = false; + listenKeys( + client.$eligibleShippingMethods, + ['loading'], + (currentEligibleShippingMethods) => { + if (currentEligibleShippingMethods.loading) { + wasSetToTrue = true; + } else { + if (wasSetToTrue) { + wasFinallySetToFalse = true; } } } - `, - { name: couponCodeName } - ); - expect(createPromotion.name).toBe(couponCodeName); - expect(createPromotion.couponCode).toBe(couponCodeName); - }); - - it('Creates a client', async () => { - client = new VendureOrderClient( - 'http://localhost:3050/shop-api', - 'channel-token', - additionalOrderFields - ); - activeOrderStore = useStore(client.$activeOrder); - currentUserStore = useStore(client.$currentUser); - expect(client).toBeInstanceOf(VendureOrderClient); - expect(activeOrderStore.value.data).toBeUndefined(); - expect(client.eventBus).toBeDefined(); - client.eventBus.on( - '*', - (eventType, e) => (latestEmittedEvent = [eventType, e]) - ); - }); - - describe('Cart management', () => { - it('Adds an item to order', async () => { - await testActiveOrderLoadingState( - async () => await client.addItemToOrder('T_2', 1) ); - activeOrderCode = activeOrderStore?.value.data.code; - expect(activeOrderStore?.value.data.lines[0].quantity).toBe(1); - expect(activeOrderStore?.value.data.lines[0].productVariant.id).toBe( - 'T_2' - ); - }); + await testActiveOrderLoadingState(awaitAbleFunction); + expect( + wasSetToTrue, + `eligibleShippingMethods's 'loading' wasn't set to true when '${clientMethodName}' started running` + ).toBe(true); + setTimeout(() => { + expect( + wasFinallySetToFalse, + `eligibleShippingMethods's 'loading' wasn't set to false when '${clientMethodName}' finished running` + ).toBe(true); + }, 1000); + } - it('Emits "item-added" event, with quantity 1', async () => { - const [eventType, event] = latestEmittedEvent; - expect(eventType).toEqual('item-added'); - expect(event).toEqual({ - productVariantIds: ['T_2'], - quantity: 1, - }); + async function testLoadingState( + awaitAbleFunction: PromiseFn, + store: MapStore + ): Promise { + expect(store.value?.loading).toBe(false); + const promise = awaitAbleFunction(); + expect(store.value?.loading).toBe(true); + const result = await promise; + expect(store.value?.loading).toBe(false); + return result; + } + + async function testCurrentUserLoadingState( + awaitAbleFunction: PromiseFn + ): Promise { + return await testLoadingState(awaitAbleFunction, client.$currentUser); + } + + it('Starts the server successfully', async () => { + expect(server.app.getHttpServer).toBeDefined(); }); - it('Retrieves active order with specified additional fields', async () => { - await client.getActiveOrder(); - expect(activeOrderStore.value?.data.lines[0].quantity).toBe(1); - expect(activeOrderStore.value?.data.lines[0].productVariant.id).toBe( - 'T_2' + it('Creates a promotion', async () => { + await adminClient.asSuperAdmin(); + const { createPromotion } = await adminClient.query( + gql` + mutation CreatePromotionMutation($name: String!) { + createPromotion( + input: { + enabled: true + couponCode: $name + translations: [{ languageCode: en, name: $name }] + conditions: [] + actions: [ + { + code: "order_fixed_discount" + arguments: [{ name: "discount", value: "10" }] + } + ] + } + ) { + ... on Promotion { + id + name + couponCode + } + ... on ErrorResult { + errorCode + } + } + } + `, + { name: couponCodeName } ); - expect(activeOrderStore.value?.data.history.totalItems).toBe(1); + expect(createPromotion.name).toBe(couponCodeName); + expect(createPromotion.couponCode).toBe(couponCodeName); }); - it('Increases quantity from 1 to 3', async () => { - await testActiveOrderLoadingState( - async () => await client.adjustOrderLine('T_1', 3) + it('Creates a client', async () => { + client = new VendureOrderClient( + 'http://localhost:3050/shop-api', + 'channel-token', + additionalOrderFields + ); + activeOrderStore = useStore(client.$activeOrder); + currentUserStore = useStore(client.$currentUser); + expect(client).toBeInstanceOf(VendureOrderClient); + expect(activeOrderStore.value.data).toBeUndefined(); + expect(client.eventBus).toBeDefined(); + client.eventBus.on( + '*', + (eventType, e) => (latestEmittedEvent = [eventType, e]) ); - expect(activeOrderStore.value?.data.lines[0].quantity).toBe(3); }); - it('Emits "item-added" event, with quantity 2', async () => { - const [eventType, event] = latestEmittedEvent; - expect(eventType).toEqual('item-added'); - expect(event).toEqual({ - productVariantIds: ['T_2'], - quantity: 2, + describe('Cart management', () => { + it( + 'Adds an item to order', + async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.addItemToOrder('T_2', 1), + 'addItemToOrder' + ); + activeOrderCode = activeOrderStore?.value.data.code; + expect(activeOrderStore?.value.data.lines[0].quantity).toBe(1); + expect(activeOrderStore?.value.data.lines[0].productVariant.id).toBe( + 'T_2' + ); + }, + { timeout: 10000 } + ); + + it('Emits "item-added" event, with quantity 1', async () => { + const [eventType, event] = latestEmittedEvent; + expect(eventType).toEqual('item-added'); + expect(event).toEqual({ + productVariantIds: ['T_2'], + quantity: 1, + }); }); - }); - it('Removes the order line', async () => { - await testActiveOrderLoadingState( - async () => await client.removeOrderLine('T_1') - ); - expect(activeOrderStore.value?.data.lines.length).toBe(0); - }); + it('Retrieves active order with specified additional fields', async () => { + await client.getActiveOrder(); + expect(activeOrderStore.value?.data.lines[0].quantity).toBe(1); + expect(activeOrderStore.value?.data.lines[0].productVariant.id).toBe( + 'T_2' + ); + expect(activeOrderStore.value?.data.history.totalItems).toBe(1); + }); - it('Emits "item-removed" event, with quantity 3', async () => { - const [eventType, event] = latestEmittedEvent; - expect(eventType).toEqual('item-removed'); - expect(event).toEqual({ - productVariantIds: ['T_2'], - quantity: 3, + it('Increases quantity from 1 to 3', async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.adjustOrderLine('T_1', 3), + 'adjustOrderLine' + ); + expect(activeOrderStore.value?.data.lines[0].quantity).toBe(3); }); - }); - it('Adds an item to order for the second time', async () => { - await testActiveOrderLoadingState( - async () => await client.addItemToOrder('T_2', 1) - ); - expect(activeOrderStore.value?.data.lines[0].quantity).toBe(1); - expect(activeOrderStore.value?.data.lines[0].productVariant.id).toBe( - 'T_2' - ); - }); + it('Emits "item-added" event, with quantity 2', async () => { + const [eventType, event] = latestEmittedEvent; + expect(eventType).toEqual('item-added'); + expect(event).toEqual({ + productVariantIds: ['T_2'], + quantity: 2, + }); + }); - it('Removes all order lines', async () => { - await testActiveOrderLoadingState( - async () => await client.removeAllOrderLines() - ); - expect(activeOrderStore.value?.data.lines.length).toBe(0); - }); + it('Removes the order line', async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.removeOrderLine('T_1'), + 'removeOrderLine' + ); + expect(activeOrderStore.value?.data.lines.length).toBe(0); + }); - it('Emits "item-removed" event, with quantity 1', async () => { - const [eventType, event] = latestEmittedEvent; - expect(eventType).toEqual('item-removed'); - expect(event).toEqual({ - productVariantIds: ['T_2'], - quantity: 1, + it('Emits "item-removed" event, with quantity 3', async () => { + const [eventType, event] = latestEmittedEvent; + expect(eventType).toEqual('item-removed'); + expect(event).toEqual({ + productVariantIds: ['T_2'], + quantity: 3, + }); }); - }); - }); - describe('Guest checkout', () => { - it('Adds an item to order', async () => { - await testActiveOrderLoadingState( - async () => await client.addItemToOrder('T_2', 1) - ); - expect(activeOrderStore.value?.data.lines[0].quantity).toBe(1); - expect(activeOrderStore.value?.data.lines[0].productVariant.id).toBe( - 'T_2' - ); + it('Adds an item to order for the second time', async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.addItemToOrder('T_2', 1), + 'addItemToOrder' + ); + expect(activeOrderStore.value?.data.lines[0].quantity).toBe(1); + expect(activeOrderStore.value?.data.lines[0].productVariant.id).toBe( + 'T_2' + ); + }); + + it('Removes all order lines', async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.removeAllOrderLines(), + 'removeAllOrderLines' + ); + expect(activeOrderStore.value?.data.lines.length).toBe(0); + }); + + it('Emits "item-removed" event, with quantity 1', async () => { + const [eventType, event] = latestEmittedEvent; + expect(eventType).toEqual('item-removed'); + expect(event).toEqual({ + productVariantIds: ['T_2'], + quantity: 1, + }); + }); }); - it('Applies invalid coupon', async () => { - try { - await testActiveOrderLoadingState( - async () => await client.applyCouponCode('fghj') + describe('Guest checkout', () => { + it('Adds an item to order', async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.addItemToOrder('T_2', 1), + 'addItemToOrder' ); - } catch (e: any) { - expect(activeOrderStore.value.error.errorCode).toBe( - 'COUPON_CODE_INVALID_ERROR' + expect(activeOrderStore.value?.data.lines[0].quantity).toBe(1); + expect(activeOrderStore.value?.data.lines[0].productVariant.id).toBe( + 'T_2' ); - } - }); + }); - it('Applies valid coupon', async () => { - await testActiveOrderLoadingState( - async () => await client.applyCouponCode(couponCodeName) - ); - expect( - (activeOrderStore.value.data as ActiveOrderFieldsFragment)?.couponCodes - ?.length - ).toEqual(1); - }); + it('Applies invalid coupon', async () => { + try { + await testEligibleShippingMethodsLoadingState( + async () => await client.applyCouponCode('fghj'), + 'applyCouponCode' + ); + } catch (e: any) { + expect(activeOrderStore.value.error.errorCode).toBe( + 'COUPON_CODE_INVALID_ERROR' + ); + } + }); - it('Emits "coupon-code-applied" event', async () => { - const [eventType, event] = latestEmittedEvent; - expect(eventType).toEqual('coupon-code-applied'); - expect(event).toEqual({ - couponCode: couponCodeName, + it('Applies valid coupon', async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.applyCouponCode(couponCodeName), + 'applyCouponCode' + ); + expect( + (activeOrderStore.value.data as ActiveOrderFieldsFragment) + ?.couponCodes?.length + ).toEqual(1); }); - }); - it('Removes coupon', async () => { - await testActiveOrderLoadingState( - async () => await client.removeCouponCode(couponCodeName) - ); - expect(activeOrderStore.value.data?.couponCodes?.length).toEqual(0); - }); + it('Emits "coupon-code-applied" event', async () => { + const [eventType, event] = latestEmittedEvent; + expect(eventType).toEqual('coupon-code-applied'); + expect(event).toEqual({ + couponCode: couponCodeName, + }); + }); - it('Emits "coupon-code-removed" event', async () => { - const [eventType, event] = latestEmittedEvent; - expect(eventType).toEqual('coupon-code-removed'); - expect(event).toEqual({ - couponCode: couponCodeName, + it('Removes coupon', async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.removeCouponCode(couponCodeName), + 'removeCouponCode' + ); + expect(activeOrderStore.value.data?.couponCodes?.length).toEqual(0); }); - }); - it('Adds customer', async () => { - const createCustomerInput: CreateCustomerInput = { - emailAddress: 'example@gmail.com', - firstName: 'Mein', - lastName: 'Zohn', - }; - await testActiveOrderLoadingState( - async () => await client.setCustomerForOrder(createCustomerInput) - ); - const customer = ( - activeOrderStore.value.data as ActiveOrderFieldsFragment - ).customer; - if (!customer) { - throw Error('Failed to create customer'); - } - expect(customer.emailAddress).toEqual(createCustomerInput.emailAddress); - expect(customer.firstName).toEqual(createCustomerInput.firstName); - expect(customer.lastName).toEqual(createCustomerInput.lastName); - }); + it('Emits "coupon-code-removed" event', async () => { + const [eventType, event] = latestEmittedEvent; + expect(eventType).toEqual('coupon-code-removed'); + expect(event).toEqual({ + couponCode: couponCodeName, + }); + }); - it('Adds shipping address', async () => { - const addShippingAddressInput: CreateAddressInput = { - streetLine1: ' Stree Line in Ethiopia', - countryCode: 'GB', - }; - await testActiveOrderLoadingState( - async () => - await client.setOrderShippingAddress(addShippingAddressInput) - ); - const shippingAddress = ( - activeOrderStore.value.data as ActiveOrderFieldsFragment - ).shippingAddress; - if (!shippingAddress) { - throw Error('Failed to set shipping address'); - } - expect(shippingAddress.country).toEqual( - regionNames.of(addShippingAddressInput.countryCode) - ); - expect(shippingAddress.streetLine1).toEqual( - addShippingAddressInput.streetLine1 - ); - }); + it('Adds customer', async () => { + const createCustomerInput: CreateCustomerInput = { + emailAddress: 'example@gmail.com', + firstName: 'Mein', + lastName: 'Zohn', + }; + await testEligibleShippingMethodsLoadingState( + async () => await client.setCustomerForOrder(createCustomerInput), + 'setCustomerForOrder' + ); + const customer = ( + activeOrderStore.value.data as ActiveOrderFieldsFragment + ).customer; + if (!customer) { + throw Error('Failed to create customer'); + } + expect(customer.emailAddress).toEqual(createCustomerInput.emailAddress); + expect(customer.firstName).toEqual(createCustomerInput.firstName); + expect(customer.lastName).toEqual(createCustomerInput.lastName); + }); - it('Adds billing address', async () => { - const addBillingAddressInput: CreateAddressInput = { - streetLine1: 'ANother Stree Line in Ethiopia', - countryCode: 'GB', - }; - await testActiveOrderLoadingState( - async () => await client.addBillingAddress(addBillingAddressInput) - ); - const billingAddress = ( - activeOrderStore.value.data as ActiveOrderFieldsFragment - ).billingAddress; - if (!billingAddress) { - throw Error('Failed to set billing address'); - } - expect(billingAddress.country).toEqual( - regionNames.of(addBillingAddressInput.countryCode) - ); - expect(billingAddress.streetLine1).toEqual( - addBillingAddressInput.streetLine1 - ); - }); + it('Adds shipping address', async () => { + const addShippingAddressInput: CreateAddressInput = { + streetLine1: ' Stree Line in Ethiopia', + countryCode: 'GB', + }; + await testEligibleShippingMethodsLoadingState( + async () => + await client.setOrderShippingAddress(addShippingAddressInput), + 'setOrderShippingAddress' + ); + const shippingAddress = ( + activeOrderStore.value.data as ActiveOrderFieldsFragment + ).shippingAddress; + if (!shippingAddress) { + throw Error('Failed to set shipping address'); + } + expect(shippingAddress.country).toEqual( + regionNames.of(addShippingAddressInput.countryCode) + ); + expect(shippingAddress.streetLine1).toEqual( + addShippingAddressInput.streetLine1 + ); + }); - it('Sets shipping method', async () => { - await testActiveOrderLoadingState( - async () => await client.setOrderShippingMethod(['T_1']) - ); - expect( - (activeOrderStore.value.data as ActiveOrderFieldsFragment).shippingLines - ?.length - ).toEqual(1); - expect( - ( + it('Adds billing address', async () => { + const addBillingAddressInput: CreateAddressInput = { + streetLine1: 'ANother Stree Line in Ethiopia', + countryCode: 'GB', + }; + await testEligibleShippingMethodsLoadingState( + async () => await client.addBillingAddress(addBillingAddressInput), + 'addBillingAddress' + ); + const billingAddress = ( activeOrderStore.value.data as ActiveOrderFieldsFragment - ).shippingLines?.find((s) => s.shippingMethod?.id === 'T_1') - ).toBeDefined(); - }); + ).billingAddress; + if (!billingAddress) { + throw Error('Failed to set billing address'); + } + expect(billingAddress.country).toEqual( + regionNames.of(addBillingAddressInput.countryCode) + ); + expect(billingAddress.streetLine1).toEqual( + addBillingAddressInput.streetLine1 + ); + }); - it('Transitions order to arranging payment state', async () => { - await testActiveOrderLoadingState( - async () => await client.transitionOrderToState('ArrangingPayment') - ); - expect( - (activeOrderStore.value.data as ActiveOrderFieldsFragment).state - ).toBe('ArrangingPayment'); + it('Sets shipping method', async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.setOrderShippingMethod(['T_1']), + 'setOrderShippingMethod' + ); + expect( + (activeOrderStore.value.data as ActiveOrderFieldsFragment) + .shippingLines?.length + ).toEqual(1); + expect( + ( + activeOrderStore.value.data as ActiveOrderFieldsFragment + ).shippingLines?.find((s) => s.shippingMethod?.id === 'T_1') + ).toBeDefined(); + }); + + it('Transitions order to arranging payment state', async () => { + await testActiveOrderLoadingState( + async () => await client.transitionOrderToState('ArrangingPayment') + ); + expect( + (activeOrderStore.value.data as ActiveOrderFieldsFragment).state + ).toBe('ArrangingPayment'); + }); + + it('Adds payment', async () => { + const addPaymentInput: PaymentInput = { + method: 'test-payment', + metadata: { + id: 0, + }, + }; + await testActiveOrderLoadingState( + async () => await client.addPayment(addPaymentInput) + ); + expect( + (activeOrderStore.value.data as ActiveOrderFieldsFragment).payments + ?.length + ).toBeGreaterThan(0); + const testPayment = ( + activeOrderStore.value.data as ActiveOrderFieldsFragment + ).payments?.find((p) => p.method === addPaymentInput.method); + expect(testPayment?.metadata.public.id).toEqual( + addPaymentInput.metadata.id + ); + }); + + it('Gets order by code', async () => { + if (!activeOrderCode) { + throw Error('Active order code is not defined'); + } + await client.getOrderByCode(activeOrderCode); + expect(activeOrderCode).toEqual(activeOrderStore.value.data.code); + }); }); - it('Adds payment', async () => { - const addPaymentInput: PaymentInput = { - method: 'test-payment', - metadata: { - id: 0, - }, + describe('Registered customer checkout', () => { + const createNewCustomerInput = { + emailAddress: `test${Math.random()}@xyz.com`, + password: '1qaz2wsx', }; - await testActiveOrderLoadingState( - async () => await client.addPayment(addPaymentInput) - ); - expect( - (activeOrderStore.value.data as ActiveOrderFieldsFragment).payments - ?.length - ).toBeGreaterThan(0); - const testPayment = ( - activeOrderStore.value.data as ActiveOrderFieldsFragment - ).payments?.find((p) => p.method === addPaymentInput.method); - expect(testPayment?.metadata.public.id).toEqual( - addPaymentInput.metadata.id - ); - }); + /* */ + it('Register as customer', async () => { + const result = await client.registerCustomerAccount( + createNewCustomerInput + ); + expect((result as Success)?.success).toBe(true); + }); - it('Gets order by code', async () => { - if (!activeOrderCode) { - throw Error('Active order code is not defined'); - } - await client.getOrderByCode(activeOrderCode); - expect(activeOrderCode).toEqual(activeOrderStore.value.data.code); - }); - }); - - describe('Registered customer checkout', () => { - const createNewCustomerInput = { - emailAddress: `test${Math.random()}@xyz.com`, - password: '1qaz2wsx', - }; - /* */ - it('Register as customer', async () => { - const result = await client.registerCustomerAccount( - createNewCustomerInput - ); - expect((result as Success)?.success).toBe(true); - }); + it('Login with the new customer', async () => { + await testCurrentUserLoadingState( + async () => + await client.login( + createNewCustomerInput.emailAddress, + createNewCustomerInput.password + ) + ); + expect(currentUserStore.value.data.identifier).toBe( + createNewCustomerInput.emailAddress + ); + }); - it('Login with the new customer', async () => { - await testCurrentUserLoadingState( - async () => - await client.login( - createNewCustomerInput.emailAddress, - createNewCustomerInput.password - ) - ); - expect(currentUserStore.value.data.identifier).toBe( - createNewCustomerInput.emailAddress - ); + it('Add item to cart as the new customer', async () => { + await testEligibleShippingMethodsLoadingState( + async () => await client.addItemToOrder('T_1', 2), + 'addItemToOrder' + ); + expect(activeOrderStore.value.data.customer.emailAddress).toBe( + createNewCustomerInput.emailAddress + ); + expect(activeOrderStore.value.data.lines.length).toBe(1); + expect(activeOrderStore.value.data.lines[0].quantity).toBe(2); + expect(activeOrderStore.value.data.lines[0].productVariant.id).toBe( + 'T_1' + ); + }); }); - it('Add item to cart as the new customer', async () => { - await testActiveOrderLoadingState( - async () => await client.addItemToOrder('T_1', 2) - ); - expect(activeOrderStore.value.data.customer.emailAddress).toBe( - createNewCustomerInput.emailAddress - ); - expect(activeOrderStore.value.data.lines.length).toBe(1); - expect(activeOrderStore.value.data.lines[0].quantity).toBe(2); - expect(activeOrderStore.value.data.lines[0].productVariant.id).toBe( - 'T_1' - ); + afterAll(async () => { + await server.destroy(); }); - }); - - afterAll(async () => { - await server.destroy(); - }); -}); + }, + { timeout: 10000 } +); diff --git a/yarn.lock b/yarn.lock index d50d0d74..6bf7a6f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5791,18 +5791,18 @@ "@typescript-eslint/types" "5.58.0" eslint-visitor-keys "^3.3.0" -"@vendure/admin-ui-plugin@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@vendure/admin-ui-plugin/-/admin-ui-plugin-2.0.6.tgz#2f96f670d18f03f716724e6b92cb876297a9232b" - integrity sha512-0aQTiMspNu/Zi/DD7b7ueMjRHDMocdveqGV0gEWXpcQ8J783AUmvXIOs7PwdP7/1aBLDAGm3LGyfjohFHiEmWg== +"@vendure/admin-ui-plugin@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@vendure/admin-ui-plugin/-/admin-ui-plugin-2.0.9.tgz#5bff1261cdb161e35fcec48bb1560d2d233f2980" + integrity sha512-+LLyjOU0/fj9+itF/h0wrRUlPjiMbfvHIW8PBHRdsGhkZM8cK3zf8yR/IMwOVNfDYp3pwhAl+RsC7vAmzB484g== dependencies: date-fns "^2.30.0" fs-extra "^10.0.0" -"@vendure/admin-ui@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@vendure/admin-ui/-/admin-ui-2.0.6.tgz#9bbc591e711fc5ea3420b665744b2c61c1e40883" - integrity sha512-VCXV6f6Q6GsUVQNfKYo+N2Rs+R/cqOrG6wNEdUmSGGqUjKpq8djM79cAzAJCtCyA03jG7p+0QAd/kFZwLZmQ8g== +"@vendure/admin-ui@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@vendure/admin-ui/-/admin-ui-2.0.9.tgz#ac89c439f0812a658d15e2351700c8d2743bace6" + integrity sha512-ADte0EGKtFiHlF8pE3ulUH9Vc0dXJ3zSySFZMUd5neyQ5njgHMvZV35F84iGljnAAUfMHM7iLLqKv9TR1/xyqQ== dependencies: "@angular/animations" "^16.0.3" "@angular/cdk" "^16.0.2" @@ -5824,7 +5824,7 @@ "@ng-select/ng-select" "^11.0.0" "@ngx-translate/core" "^14.0.0" "@ngx-translate/http-loader" "^7.0.0" - "@vendure/common" "^2.0.6" + "@vendure/common" "^2.0.9" "@webcomponents/custom-elements" "^1.5.1" apollo-angular "^5.0.0" apollo-upload-client "^17.0.0" @@ -5852,24 +5852,24 @@ tslib "^2.5.0" zone.js "~0.13.0" -"@vendure/asset-server-plugin@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@vendure/asset-server-plugin/-/asset-server-plugin-2.0.6.tgz#62009d5ca8b02d1a2339f015a38baa06b335d215" - integrity sha512-xmt1S61WC+M0MrRKl7kuS9KPkuCN0SCucN2fpAHXd2EbbDkdNDd+WKcMeAdzmeaeST0EBI7EkyJX1kEil7Lm/A== +"@vendure/asset-server-plugin@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@vendure/asset-server-plugin/-/asset-server-plugin-2.0.9.tgz#c834e7a81dd9016ff543462eeca623d79e8dee0f" + integrity sha512-zRpPhkc7Z1WqnwU7Bs6zJnLxwqWZr8Hf3yrV8gkalqop4cAh4rZIRvkegeXtvdyXLnbQ9uBr+XKGnSUwmdrdNQ== dependencies: file-type "^16.5.3" fs-extra "^10.0.0" sharp "~0.32.1" -"@vendure/common@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@vendure/common/-/common-2.0.6.tgz#4c71aedc30f13afe4f9938798a256aa5ab43aed6" - integrity sha512-/xdrTdKk5b4nZYGSzJKlYlVTxap2yaxPcE1bVTDdiDyse/wqIbkd76nIlCaYouVce80/3kTFj0nJaSR2QXjhDA== +"@vendure/common@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@vendure/common/-/common-2.0.9.tgz#de3b722ddf142732e526a89caa877e050baaaa11" + integrity sha512-HcrBGuejXBfx/2+lOCdEVZmJHR8iq5BwgH4rv3gblLVcbp25WJRJYONzzU+Hm88wucEvFMWrmmq2/xXilKXLEA== -"@vendure/core@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@vendure/core/-/core-2.0.6.tgz#edd507387b38024445a6761ba22bac00c9f643de" - integrity sha512-5s+OLOoCxFgKmbz8xBRr7BQyuwvnMpBvKUlr1kLPoo4njY5vv3lXm/poYYloc3A1WELAa5fn28RNwfMQobBU4A== +"@vendure/core@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@vendure/core/-/core-2.0.9.tgz#34274ea47e92d194b018ee90e99dd144cb0d6571" + integrity sha512-Wm7GJ+uAKCSfo9I8lHp6fXb0zQkSwYbhc6opq5XlLOHTy0Z8wSGy7H8Z4l2JMzA4Xw78w0IDxMJ9dSfdhQ8y6Q== dependencies: "@graphql-tools/stitch" "^8.7.43" "@nestjs/apollo" "^10.2.0" @@ -5881,7 +5881,7 @@ "@nestjs/testing" "9.3.9" "@nestjs/typeorm" "9.0.1" "@types/fs-extra" "^9.0.1" - "@vendure/common" "^2.0.6" + "@vendure/common" "^2.0.9" apollo-server-express "3.6.3" bcrypt "^5.1.0" body-parser "^1.19.0" @@ -5911,10 +5911,10 @@ rxjs "^7.5.4" typeorm "0.3.11" -"@vendure/email-plugin@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@vendure/email-plugin/-/email-plugin-2.0.6.tgz#f57804a0bc7157fad484e3c70f40f40940a741b0" - integrity sha512-EydX63vbTXOI8zzVo7/mCCAJW4vb4MLl+AFMXxeIPmqpfDJr2PmpSTj0M13scM77S0mpf1P8PdIJIpUMhpTc4g== +"@vendure/email-plugin@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@vendure/email-plugin/-/email-plugin-2.0.9.tgz#29b3edabb0246de6892cdb12de2f6fbeeabb9655" + integrity sha512-soTedrmM2p+32mcEJPryMFq4g5C1HJhRIa0ZO0dLCPPFvbzz/hpYOCykDYFifpQe4Y4zC/pS8HgLaMCRqjpgAw== dependencies: "@types/nodemailer" "^6.4.0" dateformat "^3.0.3" @@ -5924,14 +5924,14 @@ mjml "^4.7.1" nodemailer "^6.4.13" -"@vendure/testing@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@vendure/testing/-/testing-2.0.6.tgz#60071b2cf484f71fbc80a0292846b010e054c882" - integrity sha512-RQWUVGF/PxnW0BVHNpbKfEPQOYFvN65cM3u/H69Z92XWAH3hi90AAzuVGMxJxRdT01a4165OrQtJ4YUyKZhL0w== +"@vendure/testing@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@vendure/testing/-/testing-2.0.9.tgz#a033b2489e18fa175d9a71ae7623023c6531bfda" + integrity sha512-mysgtC3tevMqnnR3O0wnBFk5LqaGsYsFHSqVutforqVBoiXY568xb3ymEd9rnxIdC955iXNj/vvdeQVohqPFTA== dependencies: "@graphql-typed-document-node/core" "^3.2.0" "@types/node-fetch" "^2.5.4" - "@vendure/common" "^2.0.6" + "@vendure/common" "^2.0.9" faker "^4.1.0" form-data "^3.0.0" graphql "16.6.0" @@ -5939,17 +5939,17 @@ node-fetch "^2.6.0" sql.js "1.8.0" -"@vendure/ui-devkit@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@vendure/ui-devkit/-/ui-devkit-2.0.6.tgz#b4463d198597e237efbac0e6bd80d1e68a830c7a" - integrity sha512-CRQeox2W3snRRIA4z94M72fiZnt91jS+fc6woKBbem20DEUmDC2F7XEWjwED+jaGTkGbpON5f0wW5kj09GvQKw== +"@vendure/ui-devkit@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@vendure/ui-devkit/-/ui-devkit-2.0.9.tgz#e827ca9b41e0ac6ca85e64bc895e1d5ef2ba767a" + integrity sha512-vBgES0E75+Hid9yr2GY6PTUT6nJ4KRYdhZQvUNqpHl63b6f1VgVq8uCnYgPtapz1E++4olbtXJHW+SV9+81YPw== dependencies: "@angular-devkit/build-angular" "^16.0.3" "@angular/cli" "^16.0.3" "@angular/compiler" "^16.0.3" "@angular/compiler-cli" "^16.0.3" - "@vendure/admin-ui" "^2.0.6" - "@vendure/common" "^2.0.6" + "@vendure/admin-ui" "^2.0.9" + "@vendure/common" "^2.0.9" chalk "^4.1.0" chokidar "^3.5.1" fs-extra "^10.0.0"