From 5d93ab3a438195f42ad91227bdf9f9ea8b4a6859 Mon Sep 17 00:00:00 2001 From: kevinlee11 Date: Fri, 25 Oct 2024 16:54:56 -0400 Subject: [PATCH] style: fix tabbing to use 4 spaces (#17) --- .eslintrc.cjs | 225 +- src/api/cart.ts | 572 ++--- src/api/customers.ts | 80 +- src/api/orders.ts | 86 +- src/api/places.ts | 86 +- src/api/resources.ts | 42 +- src/api/template.ts | 38 +- src/clients/buyers.ts | 118 +- src/helpers/auth.ts | 8 +- src/helpers/cookies.ts | 26 +- src/helpers/datetime.ts | 64 +- src/helpers/item.ts | 746 +++--- src/helpers/location.ts | 484 ++-- src/helpers/money.ts | 88 +- src/index.ts | 72 +- src/types/api/cart/index.ts | 40 +- src/types/api/cart/private.types.ts | 4 +- src/types/api/places/index.ts | 4 +- src/types/api/template/index.ts | 12 +- src/types/helpers/datetime/index.ts | 26 +- src/types/helpers/item/index.ts | 18 +- src/types/helpers/location/index.ts | 8 +- test/cart.test.ts | 2606 +++++++++---------- test/customers/coordinates.test.ts | 130 +- test/customers/loyalty.test.ts | 354 +-- test/helpers.item.test.ts | 3658 +++++++++++++-------------- test/helpers.location.test.ts | 1910 +++++++------- test/helpers.money.test.ts | 172 +- test/helpers.ts | 68 +- test/index.test.ts | 70 +- test/orders.test.ts | 270 +- test/places.test.ts | 340 +-- test/resources.test.ts | 178 +- test/template.test.ts | 188 +- vite.config.js | 48 +- 35 files changed, 6417 insertions(+), 6422 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 2135387..4e1ecf4 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,116 +1,113 @@ module.exports = { - 'env': { - 'browser': true, - 'es2021': true - }, - 'extends': [ - 'eslint:recommended', - 'plugin:jsonc/recommended-with-jsonc' - ], - 'overrides': [ - { - 'env': { - 'node': true - }, - 'files': [ - '.eslintrc.{js,cjs}' - ], - 'parserOptions': { - 'sourceType': 'script' - } - }, - { - 'files': ['*.json', '*.json5', '*.jsonc'], - 'parser': 'jsonc-eslint-parser', - }, - { - 'files': ['*.ts', '*.tsx'], - 'plugins': [ - 'import' - ], - 'extends': [ - 'plugin:@typescript-eslint/recommended', - 'plugin:@typescript-eslint/recommended-requiring-type-checking', - ], - 'parserOptions': { - 'project': ['./tsconfig.json'], - }, - 'settings': { - 'import/parsers': { - '@typescript-eslint/parser': ['.ts', '.tsx'] - }, - 'import/resolver': { - 'typescript': { - 'alwaysTryTypes': true, - 'project': './tsconfig.json' - } - } - }, - 'rules': { - '@typescript-eslint/naming-convention': [ - 'error', - { - selector: 'variable', - format: ['camelCase', 'PascalCase', 'UPPER_CASE'] - }, - { - selector: 'typeProperty', - format: ['camelCase', 'PascalCase', 'UPPER_CASE'] - } - ], - '@typescript-eslint/no-unsafe-assignment': [ - 'off' - ], - 'import/named': 'error', - '@typescript-eslint/type-annotation-spacing': 'error', - 'no-multiple-empty-lines': [ - 'error', - { max: 1 } - ], - '@typescript-eslint/member-delimiter-style': [ - 'error', - { - 'multiline': { - 'delimiter': 'semi', - 'requireLast': true - }, - 'singleline': { - 'delimiter': 'semi', - 'requireLast': false - } - } - ] - } - } - ], - 'parser': '@typescript-eslint/parser', - 'parserOptions': { - 'ecmaVersion': 'latest', - 'sourceType': 'module', - }, - 'plugins': [ - '@typescript-eslint', - '@stylistic/js', - ], - 'ignorePatterns': ['**/docs/*', '**/lib/*', '**/bin/*'], - 'rules': { - 'indent': [ - 'error', - 'tab' - ], - 'quotes': [ - 'error', - 'single' - ], - 'semi': [ - 'error', - 'always' - ], - 'no-console': [ - 'error' - ], - '@stylistic/js/no-trailing-spaces': [ - 'error' - ], - } + 'env': { + 'browser': true, + 'es2021': true + }, + 'extends': [ + 'eslint:recommended', + 'plugin:jsonc/recommended-with-jsonc' + ], + 'overrides': [ + { + 'env': { + 'node': true + }, + 'files': [ + '.eslintrc.{js,cjs}' + ], + 'parserOptions': { + 'sourceType': 'script' + } + }, + { + 'files': ['*.json', '*.json5', '*.jsonc'], + 'parser': 'jsonc-eslint-parser', + }, + { + 'files': ['*.ts', '*.tsx'], + 'plugins': [ + 'import' + ], + 'extends': [ + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], + 'parserOptions': { + 'project': ['./tsconfig.json'], + }, + 'settings': { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'] + }, + 'import/resolver': { + 'typescript': { + 'alwaysTryTypes': true, + 'project': './tsconfig.json' + } + } + }, + 'rules': { + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: 'variable', + format: ['camelCase', 'PascalCase', 'UPPER_CASE'] + }, + { + selector: 'typeProperty', + format: ['camelCase', 'PascalCase', 'UPPER_CASE'] + } + ], + '@typescript-eslint/no-unsafe-assignment': [ + 'off' + ], + 'import/named': 'error', + '@typescript-eslint/type-annotation-spacing': 'error', + 'no-multiple-empty-lines': [ + 'error', + { max: 1 } + ], + '@typescript-eslint/member-delimiter-style': [ + 'error', + { + 'multiline': { + 'delimiter': 'semi', + 'requireLast': true + }, + 'singleline': { + 'delimiter': 'semi', + 'requireLast': false + } + } + ] + } + } + ], + 'parser': '@typescript-eslint/parser', + 'parserOptions': { + 'ecmaVersion': 'latest', + 'sourceType': 'module', + }, + 'plugins': [ + '@typescript-eslint', + '@stylistic/js', + ], + 'ignorePatterns': ['**/docs/*', '**/lib/*', '**/bin/*'], + 'rules': { + 'quotes': [ + 'error', + 'single' + ], + 'semi': [ + 'error', + 'always' + ], + 'no-console': [ + 'error' + ], + '@stylistic/js/no-trailing-spaces': [ + 'error' + ], + '@stylistic/js/indent': ['error', 4], + } }; diff --git a/src/api/cart.ts b/src/api/cart.ts index b5c272a..c450505 100644 --- a/src/api/cart.ts +++ b/src/api/cart.ts @@ -1,34 +1,34 @@ import { - CartError, - CartBaseRequest, - AddItemRequest, - BuyNowItemRequest, - UpdateItemQuantityRequest, - RemoveItemRequest, - CartResponse, - CartValidationErrors, - CartFulfillment, - PutFulfillmentRequest, - AddItemModifier, - ScheduleType, - FulfillmentType, - AttachLoyaltyRewardRequest, - RemoveLoyaltyRewardRequest, + CartError, + CartBaseRequest, + AddItemRequest, + BuyNowItemRequest, + UpdateItemQuantityRequest, + RemoveItemRequest, + CartResponse, + CartValidationErrors, + CartFulfillment, + PutFulfillmentRequest, + AddItemModifier, + ScheduleType, + FulfillmentType, + AttachLoyaltyRewardRequest, + RemoveLoyaltyRewardRequest, } from '../types/api/cart'; import { - CartBaseErrorResponse, - CartFetchResponse, - CartRedirectFetchResponse, - CartFetchBody, + CartBaseErrorResponse, + CartFetchResponse, + CartRedirectFetchResponse, + CartFetchBody, } from '../types/api/cart/private.types'; import { - buildHeaders + buildHeaders } from '../helpers/auth'; import { - getCookie + getCookie } from '../helpers/cookies'; import type LooseObject from '../types/looseobject'; @@ -37,235 +37,235 @@ const CART_API_PREFIX = '/s/api/v1/cart'; const GENERIC_ERROR_MSG = 'Something went wrong'; const getCartError = (response: Response, responseJson: CartBaseErrorResponse) => { - const errorMessage = convertSnakeToCamel(responseJson.error || responseJson.message || response.statusText); - const cartError = new Error(errorMessage) as CartError; - if (responseJson.errors) { - const camelErrors: CartValidationErrors = {}; - // Because we set the interface to use camel case, let's - // transform the validation errors to use that as well - Object.keys(responseJson.errors).forEach(k => { - const camelErrorsValues = responseJson.errors![k].map(e => convertSnakeToCamel(e)); - camelErrors[convertSnakeToCamel(k)] = camelErrorsValues; - }); - cartError.errors = camelErrors; - } - if (responseJson.fields) { - cartError.fields = responseJson.fields; - } - if (response.status) { - cartError.status = response.status; - // Redirect is a valid 200, but because it's a redirect for an error - // change the status to 500 for the error object we're throwing - if (cartError.status === 200) { - cartError.status = 500; - } - } - if (responseJson.cart_errors) { - cartError.cart_errors = responseJson.cart_errors; - } - return cartError; + const errorMessage = convertSnakeToCamel(responseJson.error || responseJson.message || response.statusText); + const cartError = new Error(errorMessage) as CartError; + if (responseJson.errors) { + const camelErrors: CartValidationErrors = {}; + // Because we set the interface to use camel case, let's + // transform the validation errors to use that as well + Object.keys(responseJson.errors).forEach(k => { + const camelErrorsValues = responseJson.errors![k].map(e => convertSnakeToCamel(e)); + camelErrors[convertSnakeToCamel(k)] = camelErrorsValues; + }); + cartError.errors = camelErrors; + } + if (responseJson.fields) { + cartError.fields = responseJson.fields; + } + if (response.status) { + cartError.status = response.status; + // Redirect is a valid 200, but because it's a redirect for an error + // change the status to 500 for the error object we're throwing + if (cartError.status === 200) { + cartError.status = 500; + } + } + if (responseJson.cart_errors) { + cartError.cart_errors = responseJson.cart_errors; + } + return cartError; }; const createCartResponse = async (response: Response): Promise => { - const responseJson = await response.json() as CartFetchResponse; + const responseJson = await response.json() as CartFetchResponse; - if (!response.ok) { - const cartError = getCartError(response, responseJson); - throw cartError; - } + if (!response.ok) { + const cartError = getCartError(response, responseJson); + throw cartError; + } - return { - response, - data: responseJson.data!, - }; + return { + response, + data: responseJson.data!, + }; }; const createCartRedirectResponse = async (response: Response): Promise => { - if (response.redirected) { - // If we're redirected to the current URL, something went wrong - if (window.location.href === response.url) { - const jsonResponse = await response.json() as CartRedirectFetchResponse; - - if (jsonResponse?.response?.errors) { - const cartError = getCartError(response, jsonResponse.response.errors); - throw cartError; - } - // Should never hit here... - throw new Error(GENERIC_ERROR_MSG) as CartError; - } - window.location.href = response.url; - return; - } else if (!response.ok) { - const jsonResponse = await response.json() as CartBaseErrorResponse; - const cartError = getCartError(response, jsonResponse); - throw cartError; - } - // Should never hit here... - throw new Error(GENERIC_ERROR_MSG) as CartError; + if (response.redirected) { + // If we're redirected to the current URL, something went wrong + if (window.location.href === response.url) { + const jsonResponse = await response.json() as CartRedirectFetchResponse; + + if (jsonResponse?.response?.errors) { + const cartError = getCartError(response, jsonResponse.response.errors); + throw cartError; + } + // Should never hit here... + throw new Error(GENERIC_ERROR_MSG) as CartError; + } + window.location.href = response.url; + return; + } else if (!response.ok) { + const jsonResponse = await response.json() as CartBaseErrorResponse; + const cartError = getCartError(response, jsonResponse); + throw cartError; + } + // Should never hit here... + throw new Error(GENERIC_ERROR_MSG) as CartError; }; const convertSnakeToCamel = (str: string) => { - return str.replace(/[_][a-z0-9]/g, group => - group.toUpperCase().replace('_', '')); + return str.replace(/[_][a-z0-9]/g, group => + group.toUpperCase().replace('_', '')); }; const convertCamelToSnake = (str: string) => { - return str.replace(/[A-Z0-9]/g, letter => `_${letter.toLowerCase()}`); + return str.replace(/[A-Z0-9]/g, letter => `_${letter.toLowerCase()}`); }; const convertCamelObjToSnakeObj = (camelObj: LooseObject) => { - const snakeObj: LooseObject = {}; - Object.keys(camelObj).forEach(c => { - const value = camelObj[c as keyof object]; - if (Array.isArray(value)) { - snakeObj[convertCamelToSnake(c) as keyof object] = convertCamelObjToSnakeObjInArray(value); - } else if (value && typeof value === 'object') { - snakeObj[convertCamelToSnake(c) as keyof object] = convertCamelObjToSnakeObj(value); - } else { - snakeObj[convertCamelToSnake(c) as keyof object] = value; - } - }); - return snakeObj; + const snakeObj: LooseObject = {}; + Object.keys(camelObj).forEach(c => { + const value = camelObj[c as keyof object]; + if (Array.isArray(value)) { + snakeObj[convertCamelToSnake(c) as keyof object] = convertCamelObjToSnakeObjInArray(value); + } else if (value && typeof value === 'object') { + snakeObj[convertCamelToSnake(c) as keyof object] = convertCamelObjToSnakeObj(value); + } else { + snakeObj[convertCamelToSnake(c) as keyof object] = value; + } + }); + return snakeObj; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any const convertCamelObjToSnakeObjInArray = (array: any[]) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const snakeArray: any[] = []; - array.forEach(value => { - if (Array.isArray(value)) { - snakeArray.push(convertCamelObjToSnakeObjInArray(value)); - } else if (value && typeof value === 'object') { - snakeArray.push(convertCamelObjToSnakeObj(value as LooseObject)); - } else { - snakeArray.push(value); - } - }); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return snakeArray; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const snakeArray: any[] = []; + array.forEach(value => { + if (Array.isArray(value)) { + snakeArray.push(convertCamelObjToSnakeObjInArray(value)); + } else if (value && typeof value === 'object') { + snakeArray.push(convertCamelObjToSnakeObj(value as LooseObject)); + } else { + snakeArray.push(value); + } + }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return snakeArray; }; const createBuyNowItemFetchBody = (request: BuyNowItemRequest) => { - const body = createAddItemFetchBody(request); - // Buy now has no orderId - delete body.order_id; - return body; + const body = createAddItemFetchBody(request); + // Buy now has no orderId + delete body.order_id; + return body; }; const createFulfillment = (cartFulfillment: CartFulfillment) => { - const fulfillment: CartFulfillment = JSON.parse(JSON.stringify(cartFulfillment)); - if (fulfillment.fulfillmentType === FulfillmentType.PICKUP) { - if (!fulfillment.pickupDetails) { - fulfillment.pickupDetails = {}; - } - if (!fulfillment.pickupDetails.scheduleType) { - fulfillment.pickupDetails.scheduleType = ScheduleType.ASAP; - } - if (fulfillment.pickupDetails.curbsidePickupRequested == undefined) { - fulfillment.pickupDetails.curbsidePickupRequested = false; - } - if (!fulfillment.pickupDetails.curbsidePickupDetails) { - fulfillment.pickupDetails.curbsidePickupDetails = { - curbsideDetails: '' - }; - } - } else if (fulfillment.fulfillmentType === FulfillmentType.DELIVERY && fulfillment.deliveryDetails) { - if (fulfillment.deliveryDetails.noContactDelivery == undefined) { - fulfillment.deliveryDetails.noContactDelivery = false; - } - if (!fulfillment.deliveryDetails.scheduleType) { - fulfillment.deliveryDetails.scheduleType = ScheduleType.ASAP; - } - } - return fulfillment; + const fulfillment: CartFulfillment = JSON.parse(JSON.stringify(cartFulfillment)); + if (fulfillment.fulfillmentType === FulfillmentType.PICKUP) { + if (!fulfillment.pickupDetails) { + fulfillment.pickupDetails = {}; + } + if (!fulfillment.pickupDetails.scheduleType) { + fulfillment.pickupDetails.scheduleType = ScheduleType.ASAP; + } + if (fulfillment.pickupDetails.curbsidePickupRequested == undefined) { + fulfillment.pickupDetails.curbsidePickupRequested = false; + } + if (!fulfillment.pickupDetails.curbsidePickupDetails) { + fulfillment.pickupDetails.curbsidePickupDetails = { + curbsideDetails: '' + }; + } + } else if (fulfillment.fulfillmentType === FulfillmentType.DELIVERY && fulfillment.deliveryDetails) { + if (fulfillment.deliveryDetails.noContactDelivery == undefined) { + fulfillment.deliveryDetails.noContactDelivery = false; + } + if (!fulfillment.deliveryDetails.scheduleType) { + fulfillment.deliveryDetails.scheduleType = ScheduleType.ASAP; + } + } + return fulfillment; }; const createAddItemFetchBody = (request: AddItemRequest | BuyNowItemRequest) => { - // We want to keep all our props as camel case for consistency in our interface - // Need to transform these back to the snake case the server expects - - const clonedRequestLineItem = JSON.parse(JSON.stringify(request.lineItem)) as LooseObject; - if (!clonedRequestLineItem.quantity) { - clonedRequestLineItem.quantity = 1; - } - const lineItem = convertCamelObjToSnakeObj(clonedRequestLineItem); - - // Transform modifiers into the format the server expects - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (lineItem.modifiers?.length) { - const modifiers: Record>> = {}; - (lineItem.modifiers).forEach(m => { - if (m.type) { - if (!modifiers[m.type]) { - modifiers[m.type] = {}; - } - const clonedModifier = JSON.parse(JSON.stringify(m)) as LooseObject; - delete clonedModifier.id; - delete clonedModifier.type; - (modifiers[m.type])[m.id] = clonedModifier; - } - }); - - lineItem.modifiers = modifiers; - } else if (lineItem.modifiers) { - // Remove empty modifier array - delete lineItem.modifiers; - } - - const fetchBody: CartFetchBody = { - line_item: lineItem, - fulfillment: convertCamelObjToSnakeObj(createFulfillment(request.fulfillment)), - location_id: request.locationId, - // JSON.stringify will remove if undefined - order_id: getOrderId(request) - }; - - return fetchBody; + // We want to keep all our props as camel case for consistency in our interface + // Need to transform these back to the snake case the server expects + + const clonedRequestLineItem = JSON.parse(JSON.stringify(request.lineItem)) as LooseObject; + if (!clonedRequestLineItem.quantity) { + clonedRequestLineItem.quantity = 1; + } + const lineItem = convertCamelObjToSnakeObj(clonedRequestLineItem); + + // Transform modifiers into the format the server expects + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (lineItem.modifiers?.length) { + const modifiers: Record>> = {}; + (lineItem.modifiers).forEach(m => { + if (m.type) { + if (!modifiers[m.type]) { + modifiers[m.type] = {}; + } + const clonedModifier = JSON.parse(JSON.stringify(m)) as LooseObject; + delete clonedModifier.id; + delete clonedModifier.type; + (modifiers[m.type])[m.id] = clonedModifier; + } + }); + + lineItem.modifiers = modifiers; + } else if (lineItem.modifiers) { + // Remove empty modifier array + delete lineItem.modifiers; + } + + const fetchBody: CartFetchBody = { + line_item: lineItem, + fulfillment: convertCamelObjToSnakeObj(createFulfillment(request.fulfillment)), + location_id: request.locationId, + // JSON.stringify will remove if undefined + order_id: getOrderId(request) + }; + + return fetchBody; }; const createAttachLoyaltyRewardBody = (request: AttachLoyaltyRewardRequest) => { - return { - loyalty_account_id: request.loyaltyAccountId, - loyalty_reward_tier_id: request.loyaltyRewardTierId, - loyalty_reward_name: request.loyaltyRewardName, - discount_type: request.discountType, - discount_scope: request.discountScope, - discount_value: request.discountValue, - max_discount: request.maxDiscount, - item_ids: request.itemIds, - // category_ids: request.categoryIds, // TODO: make available once we support category-based rewards - order_id: getOrderId(request) - }; + return { + loyalty_account_id: request.loyaltyAccountId, + loyalty_reward_tier_id: request.loyaltyRewardTierId, + loyalty_reward_name: request.loyaltyRewardName, + discount_type: request.discountType, + discount_scope: request.discountScope, + discount_value: request.discountValue, + max_discount: request.maxDiscount, + item_ids: request.itemIds, + // category_ids: request.categoryIds, // TODO: make available once we support category-based rewards + order_id: getOrderId(request) + }; }; const createRemoveLoyaltyRewardBody = (request: RemoveLoyaltyRewardRequest) => { - return { - reward_id: request.rewardId, - order_id: getOrderId(request) - }; + return { + reward_id: request.rewardId, + order_id: getOrderId(request) + }; }; const getOrderId = (request: CartBaseRequest) => { - if (request.orderId !== undefined) { - return request.orderId; - } else { - return getCookie('com_cart_id') || undefined; - } + if (request.orderId !== undefined) { + return request.orderId; + } else { + return getCookie('com_cart_id') || undefined; + } }; export class Cart { - /** + /** * Retrieves the active cart id if it exists. * * ```ts * const cartId = sdk.cart.getActiveId(); * ``` */ - getActiveId(): string | undefined { - return getCookie('com_cart_id') || undefined; - } + getActiveId(): string | undefined { + return getCookie('com_cart_id') || undefined; + } - /** + /** * Adds an item to your cart order. * * ```ts @@ -310,17 +310,17 @@ export class Cart { * ``` * @throws {@link CartError} */ - async addItem(request: AddItemRequest): Promise { - const addItemFetchBody = createAddItemFetchBody(request); - const response = await fetch(`${CART_API_PREFIX}/add`, { - method: 'POST', - body: JSON.stringify(addItemFetchBody), - headers: buildHeaders() - }); - return await createCartResponse(response); - } - - /** + async addItem(request: AddItemRequest): Promise { + const addItemFetchBody = createAddItemFetchBody(request); + const response = await fetch(`${CART_API_PREFIX}/add`, { + method: 'POST', + body: JSON.stringify(addItemFetchBody), + headers: buildHeaders() + }); + return await createCartResponse(response); + } + + /** * Applies a loyalty reward to the cart if eligible * * ```ts @@ -345,17 +345,17 @@ export class Cart { * ``` * @throws {@link CartError} */ - async attachLoyaltyReward(request: AttachLoyaltyRewardRequest): Promise { - const attachLoyaltyRewardBody = createAttachLoyaltyRewardBody(request); - const response = await fetch(`${CART_API_PREFIX}/loyalty/reward`, { - method: 'POST', - body: JSON.stringify(attachLoyaltyRewardBody), - headers: buildHeaders() - }); - return await createCartResponse(response); - } - - /** + async attachLoyaltyReward(request: AttachLoyaltyRewardRequest): Promise { + const attachLoyaltyRewardBody = createAttachLoyaltyRewardBody(request); + const response = await fetch(`${CART_API_PREFIX}/loyalty/reward`, { + method: 'POST', + body: JSON.stringify(attachLoyaltyRewardBody), + headers: buildHeaders() + }); + return await createCartResponse(response); + } + + /** * Removes loyalty reward from the cart if present * * ```ts @@ -372,17 +372,17 @@ export class Cart { * ``` * @throws {@link CartError} */ - async removeLoyaltyReward(request: RemoveLoyaltyRewardRequest): Promise { - const removeLoyaltyRewardBody = createRemoveLoyaltyRewardBody(request); - const response = await fetch(`${CART_API_PREFIX}/loyalty/reward`, { - method: 'DELETE', - body: JSON.stringify(removeLoyaltyRewardBody), - headers: buildHeaders() - }); - return await createCartResponse(response); - } - - /** + async removeLoyaltyReward(request: RemoveLoyaltyRewardRequest): Promise { + const removeLoyaltyRewardBody = createRemoveLoyaltyRewardBody(request); + const response = await fetch(`${CART_API_PREFIX}/loyalty/reward`, { + method: 'DELETE', + body: JSON.stringify(removeLoyaltyRewardBody), + headers: buildHeaders() + }); + return await createCartResponse(response); + } + + /** * Adds an item to a new order and redirects to checkout on success. * * ```ts @@ -427,17 +427,17 @@ export class Cart { * ``` * @throws {@link CartError} */ - async buyNowItem(request: BuyNowItemRequest): Promise { - const buyNowFetchBody = createBuyNowItemFetchBody(request); - const response = await fetch(`${CART_API_PREFIX}/buy`, { - method: 'POST', - body: JSON.stringify(buyNowFetchBody), - headers: buildHeaders() - }); - return createCartRedirectResponse(response); - } - - /** + async buyNowItem(request: BuyNowItemRequest): Promise { + const buyNowFetchBody = createBuyNowItemFetchBody(request); + const response = await fetch(`${CART_API_PREFIX}/buy`, { + method: 'POST', + body: JSON.stringify(buyNowFetchBody), + headers: buildHeaders() + }); + return createCartRedirectResponse(response); + } + + /** * Updates the quantity of an item on an order. Quantity must be greater than 0. * * ```ts @@ -453,21 +453,21 @@ export class Cart { * ``` * @throws {@link CartError} */ - async updateItemQuantity(request: UpdateItemQuantityRequest): Promise { - const response = await fetch(`${CART_API_PREFIX}/update-quantity`, { - method: 'POST', - body: JSON.stringify({ - order_item_id: request.orderItemId, - quantity: request.quantity, - order_id: getOrderId(request) - }), - headers: buildHeaders() - }); - - return createCartResponse(response); - } - - /** + async updateItemQuantity(request: UpdateItemQuantityRequest): Promise { + const response = await fetch(`${CART_API_PREFIX}/update-quantity`, { + method: 'POST', + body: JSON.stringify({ + order_item_id: request.orderItemId, + quantity: request.quantity, + order_id: getOrderId(request) + }), + headers: buildHeaders() + }); + + return createCartResponse(response); + } + + /** * Removes a line item from an order. * * ```ts @@ -482,20 +482,20 @@ export class Cart { * ``` * @throws {@link CartError} */ - async removeItem(request: RemoveItemRequest): Promise { - const response = await fetch(`${CART_API_PREFIX}/remove-item`, { - method: 'POST', - body: JSON.stringify({ - order_item_id: request.orderItemId, - order_id: getOrderId(request) - }), - headers: buildHeaders() - }); - - return createCartResponse(response); - } - - /** + async removeItem(request: RemoveItemRequest): Promise { + const response = await fetch(`${CART_API_PREFIX}/remove-item`, { + method: 'POST', + body: JSON.stringify({ + order_item_id: request.orderItemId, + order_id: getOrderId(request) + }), + headers: buildHeaders() + }); + + return createCartResponse(response); + } + + /** * Replaces the fulfillment on an order. * * ```ts @@ -518,16 +518,16 @@ export class Cart { * ``` * @throws {@link CartError} */ - async putFulfillment(request: PutFulfillmentRequest): Promise { - const response = await fetch(`${CART_API_PREFIX}/${getOrderId(request)}/fulfillment`, { - method: 'PUT', - body: JSON.stringify({ - fulfillment: convertCamelObjToSnakeObj(createFulfillment(request.fulfillment)), - location_id: request.locationId, - }), - headers: buildHeaders() - }); - - return createCartResponse(response); - } + async putFulfillment(request: PutFulfillmentRequest): Promise { + const response = await fetch(`${CART_API_PREFIX}/${getOrderId(request)}/fulfillment`, { + method: 'PUT', + body: JSON.stringify({ + fulfillment: convertCamelObjToSnakeObj(createFulfillment(request.fulfillment)), + location_id: request.locationId, + }), + headers: buildHeaders() + }); + + return createCartResponse(response); + } } diff --git a/src/api/customers.ts b/src/api/customers.ts index 45b0d0f..1db9b9b 100644 --- a/src/api/customers.ts +++ b/src/api/customers.ts @@ -1,26 +1,26 @@ import { - buildHeaders + buildHeaders } from '../helpers/auth'; import { InitConfig } from '..'; import { - GetCoordinatesResponse, - GetLoyaltyAccountRequest, - GetLoyaltyAccountResponse, - NoLoyaltyAccountResponse + GetCoordinatesResponse, + GetLoyaltyAccountRequest, + GetLoyaltyAccountResponse, + NoLoyaltyAccountResponse } from '../types/api/customers'; import { BuyersServiceClient } from '../clients/buyers'; export class Customers { - initConfig: InitConfig; - buyersServiceClient: BuyersServiceClient; + initConfig: InitConfig; + buyersServiceClient: BuyersServiceClient; - constructor(initObj: InitConfig) { - this.initConfig = initObj; - this.buyersServiceClient = new BuyersServiceClient(initObj.merchantId); - } + constructor(initObj: InitConfig) { + this.initConfig = initObj; + this.buyersServiceClient = new BuyersServiceClient(initObj.merchantId); + } - /** + /** * Used to try and get the coordinates of the buyer based on their IP address. * If the coordinates can't be determined, this method returns an empty object. * @@ -33,26 +33,26 @@ export class Customers { * ``` * @throws {@link Error} */ - async getCoordinates(): Promise { - const userId: number = this.initConfig.userId; + async getCoordinates(): Promise { + const userId: number = this.initConfig.userId; - const requestUrl = `/app/website/cms/api/v1/users/${userId}/customers/coordinates`; + const requestUrl = `/app/website/cms/api/v1/users/${userId}/customers/coordinates`; - const response = await fetch(requestUrl, { - method: 'GET', - headers: buildHeaders() - }); - let coordinatesResponse = await response.json() as GetCoordinatesResponse; - // Transform empty array to empty object if no match is found - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (Array.isArray(coordinatesResponse)) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - coordinatesResponse = {}; - } - return coordinatesResponse; - } + const response = await fetch(requestUrl, { + method: 'GET', + headers: buildHeaders() + }); + let coordinatesResponse = await response.json() as GetCoordinatesResponse; + // Transform empty array to empty object if no match is found + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (Array.isArray(coordinatesResponse)) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + coordinatesResponse = {}; + } + return coordinatesResponse; + } - /** + /** * Search for an existing customer loyalty account by phone number. * If no loyalty account exists, this method returns an empty object. * @@ -65,16 +65,16 @@ export class Customers { * ``` * @throws {@link Error} */ - async getLoyaltyAccount(request: GetLoyaltyAccountRequest): + async getLoyaltyAccount(request: GetLoyaltyAccountRequest): Promise - { - const phone = request.phone; - const loyaltyAccount = await this.buyersServiceClient.getLoyaltyAccount(phone); - if (!loyaltyAccount) { - return {}; - } - return { - data: loyaltyAccount - }; - } + { + const phone = request.phone; + const loyaltyAccount = await this.buyersServiceClient.getLoyaltyAccount(phone); + if (!loyaltyAccount) { + return {}; + } + return { + data: loyaltyAccount + }; + } } diff --git a/src/api/orders.ts b/src/api/orders.ts index 910bc5c..ef0a11d 100644 --- a/src/api/orders.ts +++ b/src/api/orders.ts @@ -1,18 +1,18 @@ import { - buildHeaders + buildHeaders } from '../helpers/auth'; import { InitConfig } from '..'; import { GetOrderRequest, GetOrderResponse } from '../types/api/orders'; export class Orders { - initConfig: InitConfig; + initConfig: InitConfig; - constructor(initObj: InitConfig) { - this.initConfig = initObj; - } + constructor(initObj: InitConfig) { + this.initConfig = initObj; + } - /** + /** * Fetches complete details about a past order using the jwt token associated with that order. * * ```ts @@ -28,44 +28,44 @@ export class Orders { * } * ``` */ - async getOrder(request: GetOrderRequest): Promise { - const jwtToken: string = request.jwtToken; - const locationId: string = request.locationId; - const fulfillments: Array = request.fulfillments; + async getOrder(request: GetOrderRequest): Promise { + const jwtToken: string = request.jwtToken; + const locationId: string = request.locationId; + const fulfillments: Array = request.fulfillments; - if (!jwtToken) { - throw new Error('missing jwtToken'); - } - if (!locationId) { - throw new Error('missing locationId'); - } - if (!fulfillments) { - throw new Error('missing fulfillments'); - } - if (!this.initConfig.cmsSiteId) { - throw new Error('missing cmsSiteId'); - } - if (!Array.isArray(fulfillments)) { - throw new Error('fulfillments must be an array'); - } - const cmsSiteId: string = this.initConfig.cmsSiteId; - const validFulfillments = ['shipping', 'pickup', 'delivery']; - fulfillments.forEach((f) => { - if (!validFulfillments.includes(f.toLowerCase())) { - throw new Error('invalid value in fulfillments array: ' + f); - } - }); + if (!jwtToken) { + throw new Error('missing jwtToken'); + } + if (!locationId) { + throw new Error('missing locationId'); + } + if (!fulfillments) { + throw new Error('missing fulfillments'); + } + if (!this.initConfig.cmsSiteId) { + throw new Error('missing cmsSiteId'); + } + if (!Array.isArray(fulfillments)) { + throw new Error('fulfillments must be an array'); + } + const cmsSiteId: string = this.initConfig.cmsSiteId; + const validFulfillments = ['shipping', 'pickup', 'delivery']; + fulfillments.forEach((f) => { + if (!validFulfillments.includes(f.toLowerCase())) { + throw new Error('invalid value in fulfillments array: ' + f); + } + }); - let requestUrl = `/app/cms/api/v1/sites/${cmsSiteId}/order-again/${jwtToken}?location=${locationId}`; - fulfillments.forEach((f: string) => { - requestUrl += `&fulfillments[]=${f}`; - }); + let requestUrl = `/app/cms/api/v1/sites/${cmsSiteId}/order-again/${jwtToken}?location=${locationId}`; + fulfillments.forEach((f: string) => { + requestUrl += `&fulfillments[]=${f}`; + }); - const response = await fetch(requestUrl, { - method: 'GET', - headers: buildHeaders() - }); - const orderResponse = await response.json(); - return orderResponse as GetOrderResponse; - } + const response = await fetch(requestUrl, { + method: 'GET', + headers: buildHeaders() + }); + const orderResponse = await response.json(); + return orderResponse as GetOrderResponse; + } } diff --git a/src/api/places.ts b/src/api/places.ts index ddce375..0e3755f 100644 --- a/src/api/places.ts +++ b/src/api/places.ts @@ -1,25 +1,25 @@ import { - AutocompletePlacesRequest, - AutocompletePlacesResponse, - AutocompletePlaceType, - GetPlaceRequest, - GetPlaceResponse + AutocompletePlacesRequest, + AutocompletePlacesResponse, + AutocompletePlaceType, + GetPlaceRequest, + GetPlaceResponse } from '../types/api/places'; import { - buildHeaders + buildHeaders } from '../helpers/auth'; import { InitConfig } from '..'; export class Places { - initConfig: InitConfig; + initConfig: InitConfig; - constructor(initObj: InitConfig) { - this.initConfig = initObj; - } + constructor(initObj: InitConfig) { + this.initConfig = initObj; + } - /** + /** * Used to get a list of places autocompleted from an address (or partial address). * * ```ts @@ -35,23 +35,23 @@ export class Places { * ``` * @throws {@link Error} */ - async autocompletePlaces(request: AutocompletePlacesRequest): Promise { - const userId: number = this.initConfig.userId; - const siteId: string = this.initConfig.siteId; - const address = request.address; - const types = request.types ?? AutocompletePlaceType.GEOCODE; + async autocompletePlaces(request: AutocompletePlacesRequest): Promise { + const userId: number = this.initConfig.userId; + const siteId: string = this.initConfig.siteId; + const address = request.address; + const types = request.types ?? AutocompletePlaceType.GEOCODE; - const requestUrl = `/app/store/api/v28/pub/users/${userId}/sites/${siteId}/places?types=${types}&input=${address}`; + const requestUrl = `/app/store/api/v28/pub/users/${userId}/sites/${siteId}/places?types=${types}&input=${address}`; - const response = await fetch(requestUrl, { - method: 'GET', - headers: buildHeaders() - }); - const placesResponse = await response.json() as AutocompletePlacesResponse; - return placesResponse; - } + const response = await fetch(requestUrl, { + method: 'GET', + headers: buildHeaders() + }); + const placesResponse = await response.json() as AutocompletePlacesResponse; + return placesResponse; + } - /** + /** * Used to get the full details for a place using a `place_id` from autocompletePlaces. * * ```ts @@ -66,24 +66,24 @@ export class Places { * ``` * @throws {@link Error} */ - async getPlace(request: GetPlaceRequest): Promise { - const userId: number = this.initConfig.userId; - const siteId: string = this.initConfig.siteId; - const placeId = request.placeId; + async getPlace(request: GetPlaceRequest): Promise { + const userId: number = this.initConfig.userId; + const siteId: string = this.initConfig.siteId; + const placeId = request.placeId; - const requestUrl = `/app/store/api/v28/pub/users/${userId}/sites/${siteId}/places/${placeId}`; + const requestUrl = `/app/store/api/v28/pub/users/${userId}/sites/${siteId}/places/${placeId}`; - const response = await fetch(requestUrl, { - method: 'GET', - headers: buildHeaders() - }); - const placesResponse = await response.json(); - // Transform empty array to empty object if no match is found - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (Array.isArray(placesResponse.data)) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - placesResponse.data = {}; - } - return placesResponse as GetPlaceResponse; - } + const response = await fetch(requestUrl, { + method: 'GET', + headers: buildHeaders() + }); + const placesResponse = await response.json(); + // Transform empty array to empty object if no match is found + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (Array.isArray(placesResponse.data)) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + placesResponse.data = {}; + } + return placesResponse as GetPlaceResponse; + } } diff --git a/src/api/resources.ts b/src/api/resources.ts index b979fcb..0a4876e 100644 --- a/src/api/resources.ts +++ b/src/api/resources.ts @@ -1,16 +1,16 @@ import { - ResourcesResponse, - ResourcesRequest, - ResourceInput, + ResourcesResponse, + ResourcesRequest, + ResourceInput, } from '../types/api/resources'; import { - buildHeaders + buildHeaders } from '../helpers/auth'; import { ResourceMap } from '../types/api/resources/private.types'; export class Resources { - /** + /** * Used to load up to 5 resources. * * ```ts @@ -49,21 +49,21 @@ export class Resources { * ``` * @throws {@link Error} */ - async getResources(request: ResourcesRequest): Promise { - const resources: ResourceMap = {}; - for (const key in request) { - const resource: ResourceInput = request[key]; - resources[key] = resource; - } + async getResources(request: ResourcesRequest): Promise { + const resources: ResourceMap = {}; + for (const key in request) { + const resource: ResourceInput = request[key]; + resources[key] = resource; + } - const response = await fetch('/s/api/v1/resources', { - method: 'POST', - body: JSON.stringify({ - input: resources - }), - headers: buildHeaders() - }); - const resourcesResponse = await response.json() as ResourcesResponse; - return resourcesResponse; - } + const response = await fetch('/s/api/v1/resources', { + method: 'POST', + body: JSON.stringify({ + input: resources + }), + headers: buildHeaders() + }); + const resourcesResponse = await response.json() as ResourcesResponse; + return resourcesResponse; + } } diff --git a/src/api/template.ts b/src/api/template.ts index 82f32d1..fb0abed 100644 --- a/src/api/template.ts +++ b/src/api/template.ts @@ -1,15 +1,15 @@ import { - TemplateRequest, - TemplateError + TemplateRequest, + TemplateError } from '../types/api/template'; import { - buildHeaders + buildHeaders } from '../helpers/auth'; export class Template { - /** + /** * Used to load a Twig template via the API. * * ```ts @@ -31,22 +31,22 @@ export class Template { * ``` * @throws {@link TemplateError} */ - async getTemplate(request: TemplateRequest): Promise { - const response = await fetch('/s/api/v1/template', { - method: 'POST', - body: JSON.stringify({ - template: request.template, - props: request.props, - }), - headers: buildHeaders() - }); + async getTemplate(request: TemplateRequest): Promise { + const response = await fetch('/s/api/v1/template', { + method: 'POST', + body: JSON.stringify({ + template: request.template, + props: request.props, + }), + headers: buildHeaders() + }); - const resourceResponse: string = await response.text(); + const resourceResponse: string = await response.text(); - if (response.ok === false) { - throw new TemplateError('Unable to render template', resourceResponse); - } + if (response.ok === false) { + throw new TemplateError('Unable to render template', resourceResponse); + } - return resourceResponse; - } + return resourceResponse; + } } diff --git a/src/clients/buyers.ts b/src/clients/buyers.ts index b98fe45..12a917a 100644 --- a/src/clients/buyers.ts +++ b/src/clients/buyers.ts @@ -1,8 +1,8 @@ import { getCookie } from '../helpers/cookies'; import { - BSGetLoyaltyAccountRequest, - BSGetLoyaltyAccountResponse, - BSLoyaltyAccount + BSGetLoyaltyAccountRequest, + BSGetLoyaltyAccountResponse, + BSLoyaltyAccount } from '../types/clients/buyers.private'; const XSRF_COOKIE_NAME = 'customer_xsrf'; @@ -12,69 +12,69 @@ const PING_ENDPOINT = '/ping'; const GET_LOYALTY_ACCOUNT_ENDPOINT = '/loyalty/account/search'; export class BuyersServiceClient { - #merchantId: string; - constructor(merchantId: string) { - this.#merchantId = merchantId; - } + #merchantId: string; + constructor(merchantId: string) { + this.#merchantId = merchantId; + } - async getLoyaltyAccount(phone: string): Promise { - const body: BSGetLoyaltyAccountRequest = { - phone: phone, - }; - const response = await this.#requestJson( - `${BASE_URL}${GET_LOYALTY_ACCOUNT_ENDPOINT}`, - 'POST', - body - ); - return response?.data.loyalty_account ?? null; - } + async getLoyaltyAccount(phone: string): Promise { + const body: BSGetLoyaltyAccountRequest = { + phone: phone, + }; + const response = await this.#requestJson( + `${BASE_URL}${GET_LOYALTY_ACCOUNT_ENDPOINT}`, + 'POST', + body + ); + return response?.data.loyalty_account ?? null; + } - async #requestJson(url: string, method: string, body: object|null = null, retryIf419: boolean = true): Promise { - let token = getCookie(XSRF_COOKIE_NAME); - if (!token) { - await this.#ping(); - token = getCookie(XSRF_COOKIE_NAME) ?? ''; - } + async #requestJson(url: string, method: string, body: object|null = null, retryIf419: boolean = true): Promise { + let token = getCookie(XSRF_COOKIE_NAME); + if (!token) { + await this.#ping(); + token = getCookie(XSRF_COOKIE_NAME) ?? ''; + } - const options: RequestInit = { - method: method, - headers: this.#getHeaders(token), - }; - if (body) { - options.body = JSON.stringify(body); - } - const response = await fetch(url, options); + const options: RequestInit = { + method: method, + headers: this.#getHeaders(token), + }; + if (body) { + options.body = JSON.stringify(body); + } + const response = await fetch(url, options); - if (!response.ok) { - if (response.status === 404) { - return null; - } else if (response.status === 419 && retryIf419) { - // Even though we had a token, the server may have evicted this session from its cache. - // So let's generate a new token and try again. - await this.#ping(); - return await this.#requestJson(url, method, body, false); // don't retry if we get a 419 again - } else { - throw new Error(`Error ${response.status}: ${response.statusText}`); - } - } + if (!response.ok) { + if (response.status === 404) { + return null; + } else if (response.status === 419 && retryIf419) { + // Even though we had a token, the server may have evicted this session from its cache. + // So let's generate a new token and try again. + await this.#ping(); + return await this.#requestJson(url, method, body, false); // don't retry if we get a 419 again + } else { + throw new Error(`Error ${response.status}: ${response.statusText}`); + } + } - return await response.json() as T; - } + return await response.json() as T; + } - #getHeaders(xsrfToken: string) { - return { - 'Accept': 'application/json', - 'Content-Type': 'application/json; charset=UTF-8', - 'X-XSRF-TOKEN': xsrfToken, - 'Square-Merchant-Token': this.#merchantId, - }; - } + #getHeaders(xsrfToken: string) { + return { + 'Accept': 'application/json', + 'Content-Type': 'application/json; charset=UTF-8', + 'X-XSRF-TOKEN': xsrfToken, + 'Square-Merchant-Token': this.#merchantId, + }; + } - /** + /** * Calling ping will set the session ID and XSRF token cookies needed for subsequent requests */ - async #ping() { - const url = `${BASE_URL}${PING_ENDPOINT}`; - await fetch(url); - } + async #ping() { + const url = `${BASE_URL}${PING_ENDPOINT}`; + await fetch(url); + } } diff --git a/src/helpers/auth.ts b/src/helpers/auth.ts index 34ade28..e8998b3 100644 --- a/src/helpers/auth.ts +++ b/src/helpers/auth.ts @@ -2,14 +2,14 @@ * Returns the CSRF token from a custom page. */ export const getCsrfToken = () => { - return (document.querySelector('meta[name="csrf-token"]'))?.content; + return (document.querySelector('meta[name="csrf-token"]'))?.content; }; /** * Returns the headers required to make an API request for a custom page. */ export const buildHeaders = (): HeadersInit => ({ - 'Accept': 'application/json', - 'content-type' : 'application/json; charset=UTF-8', - 'X-CSRF-TOKEN': getCsrfToken() + 'Accept': 'application/json', + 'content-type' : 'application/json; charset=UTF-8', + 'X-CSRF-TOKEN': getCsrfToken() }); diff --git a/src/helpers/cookies.ts b/src/helpers/cookies.ts index 063fcb9..044b5fa 100644 --- a/src/helpers/cookies.ts +++ b/src/helpers/cookies.ts @@ -1,15 +1,15 @@ export const getCookie = (cookieName: string) => { - const name = cookieName + '='; - const decodedCookie = decodeURIComponent(document.cookie); - const ca = decodedCookie.split(';'); - for(let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) == ' ') { - c = c.substring(1); - } - if (c.indexOf(name) == 0) { - return c.substring(name.length, c.length); - } - } - return null; + const name = cookieName + '='; + const decodedCookie = decodeURIComponent(document.cookie); + const ca = decodedCookie.split(';'); + for(let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return null; }; diff --git a/src/helpers/datetime.ts b/src/helpers/datetime.ts index dcb8124..b79ab58 100644 --- a/src/helpers/datetime.ts +++ b/src/helpers/datetime.ts @@ -1,8 +1,8 @@ import { - DayOfWeek, - TimeString, - localizeDateFormats, - DateFormats, + DayOfWeek, + TimeString, + localizeDateFormats, + DateFormats, } from '../types/helpers/datetime'; /** @@ -23,8 +23,8 @@ export const getNumericTime = (timeString: TimeString): number => Number(timeStr * @return {Date} */ export const getDateObjInTimezone = (dateString: string, timeString: TimeString, tzOffsetStr: string) => { - const isoDate = new Date(dateString).toISOString().slice(0, 10); - return new Date(`${isoDate}T${timeString}${tzOffsetStr}`); + const isoDate = new Date(dateString).toISOString().slice(0, 10); + return new Date(`${isoDate}T${timeString}${tzOffsetStr}`); }; /** @@ -39,13 +39,13 @@ export const getDateObjInTimezone = (dateString: string, timeString: TimeString, * @returns {string} */ export const localizeDate = (dateObj: Date, locale: string, format: localizeDateFormats, timezone: string, hour12?: boolean) => { - const options: localizeDateFormats & { timeZone?: string; hour12?: boolean } = { ...format }; - options.timeZone = timezone; + const options: localizeDateFormats & { timeZone?: string; hour12?: boolean } = { ...format }; + options.timeZone = timezone; - if (typeof hour12 === 'boolean') { - options.hour12 = hour12; - } - return dateObj.toLocaleString(locale, options); + if (typeof hour12 === 'boolean') { + options.hour12 = hour12; + } + return dateObj.toLocaleString(locale, options); }; /** @@ -58,8 +58,8 @@ export const localizeDate = (dateObj: Date, locale: string, format: localizeDate * @return {DayOfWeek} */ export const getDayOfWeekKey = (dateObj: Date, locale: string, timezone: string): DayOfWeek => { - const dayOfWeek = localizeDate(dateObj, locale, DateFormats.weekdayShort, timezone); - return dayOfWeek.toUpperCase() as DayOfWeek; + const dayOfWeek = localizeDate(dateObj, locale, DateFormats.weekdayShort, timezone); + return dayOfWeek.toUpperCase() as DayOfWeek; }; /** @@ -73,25 +73,25 @@ export const getDayOfWeekKey = (dateObj: Date, locale: string, timezone: string) * @return {String} Ex: 9:00 am */ export const getFormattedTime = ( - date: Date, - timeString: string, - timeFormat: localizeDateFormats, - storeLocale: string, - timezone: string, - tzOffsetString: string, + date: Date, + timeString: string, + timeFormat: localizeDateFormats, + storeLocale: string, + timezone: string, + tzOffsetString: string, ) => { - const timeStrings = timeString.split(':'); - let hours = timeStrings[0]; - const minutes = timeStrings[1]; - const seconds = timeStrings[2]; - if (Number.parseInt(hours, 10) === 24) { - hours = '00'; - } - const currentDateString = localizeDate(date, 'en-US', DateFormats.yearNmonthNdayN, timezone); - const dateObj = getDateObjInTimezone( - currentDateString, + const timeStrings = timeString.split(':'); + let hours = timeStrings[0]; + const minutes = timeStrings[1]; + const seconds = timeStrings[2]; + if (Number.parseInt(hours, 10) === 24) { + hours = '00'; + } + const currentDateString = localizeDate(date, 'en-US', DateFormats.yearNmonthNdayN, timezone); + const dateObj = getDateObjInTimezone( + currentDateString, `${hours}:${minutes}:${seconds}` as TimeString, tzOffsetString, - ); - return localizeDate(dateObj, storeLocale, timeFormat, timezone).replace('AM', 'am').replace('PM', 'pm'); + ); + return localizeDate(dateObj, storeLocale, timeFormat, timezone).replace('AM', 'am').replace('PM', 'pm'); }; diff --git a/src/helpers/item.ts b/src/helpers/item.ts index c03593f..94ee6bc 100644 --- a/src/helpers/item.ts +++ b/src/helpers/item.ts @@ -1,432 +1,432 @@ import { - OptionSelection, - Item as ItemResource, - ItemOption, - ModifierList, - Variation, - ValidateItemError, - ItemPrice, - QuantityErrorType, - QuantityErrorTypeEnum, - GetItemPriceRequest, - ValidateItemRequest, - GetInStockVariationsForSelectedOptionsOrVariationRequest, - ItemPrepTime + OptionSelection, + Item as ItemResource, + ItemOption, + ModifierList, + Variation, + ValidateItemError, + ItemPrice, + QuantityErrorType, + QuantityErrorTypeEnum, + GetItemPriceRequest, + ValidateItemRequest, + GetInStockVariationsForSelectedOptionsOrVariationRequest, + ItemPrepTime } from '../types/helpers/item'; import { - AddLineItem, - AddItemModifier, - ChoiceModifier, - TextModifier, - ModifierType + AddLineItem, + AddItemModifier, + ChoiceModifier, + TextModifier, + ModifierType } from '../types/api/cart'; import { - Money + Money } from './money'; const getVariationSelections = (variation: Variation) => { - const variationSelections: OptionSelection[] = []; - if (variation.item_option_values) { - Object.keys(variation.item_option_values).forEach(itemOptionValuesKey => { - variationSelections.push({ - itemOptionId: itemOptionValuesKey, - choice: variation.item_option_values![itemOptionValuesKey].choice - }); - }); - } - return variationSelections; + const variationSelections: OptionSelection[] = []; + if (variation.item_option_values) { + Object.keys(variation.item_option_values).forEach(itemOptionValuesKey => { + variationSelections.push({ + itemOptionId: itemOptionValuesKey, + choice: variation.item_option_values![itemOptionValuesKey].choice + }); + }); + } + return variationSelections; }; const getEventEndDate = (item: ItemResource): Date => { - const endDate = item.item_type_details.end_date; - const endTime = item.item_type_details.end_time!; + const endDate = item.item_type_details.end_date; + const endTime = item.item_type_details.end_time!; - // date is in the format YYYY-MM-DD - let dateString = endDate + 'T'; + // date is in the format YYYY-MM-DD + let dateString = endDate + 'T'; - // time is in the format HH:MM AA - const timePieces = endTime.split(' '); - const numericTimePieces = timePieces[0].split(':'); - let hour = parseInt(numericTimePieces[0]) + (timePieces[1] === 'PM' ? 12 : 0); - hour -= numericTimePieces[0] === '12' ? 12 : 0; - const minute = numericTimePieces[1]; - if (hour.toString().length === 1) { - dateString += '0'; - } - dateString += `${hour}:${minute}:00${item.item_type_details.timezone_info!.utc_offset_string}`; + // time is in the format HH:MM AA + const timePieces = endTime.split(' '); + const numericTimePieces = timePieces[0].split(':'); + let hour = parseInt(numericTimePieces[0]) + (timePieces[1] === 'PM' ? 12 : 0); + hour -= numericTimePieces[0] === '12' ? 12 : 0; + const minute = numericTimePieces[1]; + if (hour.toString().length === 1) { + dateString += '0'; + } + dateString += `${hour}:${minute}:00${item.item_type_details.timezone_info!.utc_offset_string}`; - return new Date(dateString); + return new Date(dateString); }; export class Item { - /** + /** * Returns the variations for an item resource. */ - getVariations(item: ItemResource): Variation[] { - return item.variations; - } + getVariations(item: ItemResource): Variation[] { + return item.variations; + } - /** + /** * Returns the item options for an item resource. */ - getItemOptions(item: ItemResource): ItemOption[] | undefined { - return item.item_options; - } + getItemOptions(item: ItemResource): ItemOption[] | undefined { + return item.item_options; + } - /** + /** * Returns the modifier lists for an item resource. */ - getModifierLists(item: ItemResource): ModifierList[] | undefined { - return item.modifier_lists; - } + getModifierLists(item: ItemResource): ModifierList[] | undefined { + return item.modifier_lists; + } - /** + /** * Returns whether a particular variation is sold out. */ - isVariationSoldOut(variation: Variation): boolean { - return variation.sold_out || (variation.inventory_tracking_enabled && variation.inventory === 0); - } + isVariationSoldOut(variation: Variation): boolean { + return variation.sold_out || (variation.inventory_tracking_enabled && variation.inventory === 0); + } - /** + /** * Returns the QuantityErrorType if there's an item quantity error with the item varation, otherwise null. */ - getItemQuantityError(item: ItemResource, variation: Variation, quantity: number): QuantityErrorTypeEnum | null { - if (quantity <= 0) { - return QuantityErrorType.INVALID_QUANTITY; - } else if (this.isVariationSoldOut(variation)) { - return QuantityErrorType.SOLD_OUT; - } else if (variation.inventory_tracking_enabled && quantity > variation.inventory!) { - return QuantityErrorType.STOCK_EXCEEDED; - } else if (item.per_order_max && quantity > item.per_order_max) { - return QuantityErrorType.PER_ORDER_MAX_EXCEEDED; - } - return null; - } - - /** + getItemQuantityError(item: ItemResource, variation: Variation, quantity: number): QuantityErrorTypeEnum | null { + if (quantity <= 0) { + return QuantityErrorType.INVALID_QUANTITY; + } else if (this.isVariationSoldOut(variation)) { + return QuantityErrorType.SOLD_OUT; + } else if (variation.inventory_tracking_enabled && quantity > variation.inventory!) { + return QuantityErrorType.STOCK_EXCEEDED; + } else if (item.per_order_max && quantity > item.per_order_max) { + return QuantityErrorType.PER_ORDER_MAX_EXCEEDED; + } + return null; + } + + /** * Returns whether all variations of an item are sold out. */ - isItemSoldOut(item: ItemResource): boolean { - return item.variations.every(v => this.isVariationSoldOut(v)); - } + isItemSoldOut(item: ItemResource): boolean { + return item.variations.every(v => this.isVariationSoldOut(v)); + } - /** + /** * Returns all variations in stock for the selected options or variation. */ - getInStockVariationsForSelectedOptionsOrVariation({ item, selectedOptions = [], selectedVariationId = '', skipStockCheck = false }: GetInStockVariationsForSelectedOptionsOrVariationRequest): Variation[] { - return this.getVariations(item).reduce((acc: Variation[], variation) => { - if (!selectedVariationId && variation.item_option_values) { - const variationSelections = getVariationSelections(variation); - - // If choice does not exist in the variation, do not include - if (!selectedOptions.every((c) => variationSelections.find(vs => vs.itemOptionId === c.itemOptionId && vs.choice === c.choice))) { - return acc; - } - } else if (item.variations.length > 1) { - // If no item_option_values we have a flat variation. Only care if we have multiple flat variations. - if (variation.id !== selectedVariationId) { - return acc; - } - } - - // If the variation is marked as sold out, or the variation has no inventory, do not include - if (!skipStockCheck && this.isVariationSoldOut(variation)) { - return acc; - } - - acc.push(variation); - return acc; - }, []); - } - - /** + getInStockVariationsForSelectedOptionsOrVariation({ item, selectedOptions = [], selectedVariationId = '', skipStockCheck = false }: GetInStockVariationsForSelectedOptionsOrVariationRequest): Variation[] { + return this.getVariations(item).reduce((acc: Variation[], variation) => { + if (!selectedVariationId && variation.item_option_values) { + const variationSelections = getVariationSelections(variation); + + // If choice does not exist in the variation, do not include + if (!selectedOptions.every((c) => variationSelections.find(vs => vs.itemOptionId === c.itemOptionId && vs.choice === c.choice))) { + return acc; + } + } else if (item.variations.length > 1) { + // If no item_option_values we have a flat variation. Only care if we have multiple flat variations. + if (variation.id !== selectedVariationId) { + return acc; + } + } + + // If the variation is marked as sold out, or the variation has no inventory, do not include + if (!skipStockCheck && this.isVariationSoldOut(variation)) { + return acc; + } + + acc.push(variation); + return acc; + }, []); + } + + /** * Returns whether an item's option choice is disabled based on the selected options. */ - isOptionChoiceDisabledForSelectedOptions(item: ItemResource, optionChoice: OptionSelection, selectedOptions: OptionSelection[], removeMatchingOptionSet = true) { - // In most use cases we want to compare the itemOption against OTHER selectedOptions' option sets - if (removeMatchingOptionSet) { - selectedOptions = selectedOptions.filter(s => s.itemOptionId !== optionChoice.itemOptionId); - } - // Get in stock variations - const variationsInStockForChoices = this.getInStockVariationsForSelectedOptionsOrVariation({ item, selectedOptions }); - - let isInStock = false; - - variationsInStockForChoices.forEach(v => { - const variationSelections = getVariationSelections(v); - if (variationSelections.find(vs => vs.itemOptionId === optionChoice.itemOptionId && vs.choice === optionChoice.choice)) { - isInStock = true; - } - }); - return !isInStock; - } - - /** + isOptionChoiceDisabledForSelectedOptions(item: ItemResource, optionChoice: OptionSelection, selectedOptions: OptionSelection[], removeMatchingOptionSet = true) { + // In most use cases we want to compare the itemOption against OTHER selectedOptions' option sets + if (removeMatchingOptionSet) { + selectedOptions = selectedOptions.filter(s => s.itemOptionId !== optionChoice.itemOptionId); + } + // Get in stock variations + const variationsInStockForChoices = this.getInStockVariationsForSelectedOptionsOrVariation({ item, selectedOptions }); + + let isInStock = false; + + variationsInStockForChoices.forEach(v => { + const variationSelections = getVariationSelections(v); + if (variationSelections.find(vs => vs.itemOptionId === optionChoice.itemOptionId && vs.choice === optionChoice.choice)) { + isInStock = true; + } + }); + return !isInStock; + } + + /** * Returns whether a modifier list is valid for the selected modifiers. */ - isModifierListForSelectedModifiersValid(modifierList: ModifierList, selectedModifiers: AddItemModifier[]) { - const selectedModifier = selectedModifiers.find(m => m.id == modifierList.id); - - const min = modifierList.min_selected_modifiers; - const max = modifierList.max_selected_modifiers; - - let selectedValueCount = (selectedModifier)?.textEntry?.length || 0; - if ((selectedModifier)?.choiceSelections?.length) { - // Invalid or sold out choices - const invalidChoice = (selectedModifier).choiceSelections.find(s => !modifierList.modifiers?.find(m => { - if (typeof s === 'object') { - return m.id === s.id; - } - return m.id === s; - })); - const soldOutChoice = (selectedModifier).choiceSelections.find(s => modifierList.modifiers?.find(m => { - if (typeof s === 'object') { - return m.id === s.id; - } - return m.id === s; - })?.sold_out); - if (invalidChoice || soldOutChoice) { - return false; - } - selectedValueCount = (selectedModifier).choiceSelections.length; - } - - if (min && max && min === max) { - return selectedValueCount === min; - } - - if (min && max) { - return selectedValueCount >= min && selectedValueCount <= max; - } - - if (max) { - return selectedValueCount <= max; - } - - if (min) { - return selectedValueCount >= min; - } - - // min/max === 0 (i.e. not required) - return true; - } - - /** + isModifierListForSelectedModifiersValid(modifierList: ModifierList, selectedModifiers: AddItemModifier[]) { + const selectedModifier = selectedModifiers.find(m => m.id == modifierList.id); + + const min = modifierList.min_selected_modifiers; + const max = modifierList.max_selected_modifiers; + + let selectedValueCount = (selectedModifier)?.textEntry?.length || 0; + if ((selectedModifier)?.choiceSelections?.length) { + // Invalid or sold out choices + const invalidChoice = (selectedModifier).choiceSelections.find(s => !modifierList.modifiers?.find(m => { + if (typeof s === 'object') { + return m.id === s.id; + } + return m.id === s; + })); + const soldOutChoice = (selectedModifier).choiceSelections.find(s => modifierList.modifiers?.find(m => { + if (typeof s === 'object') { + return m.id === s.id; + } + return m.id === s; + })?.sold_out); + if (invalidChoice || soldOutChoice) { + return false; + } + selectedValueCount = (selectedModifier).choiceSelections.length; + } + + if (min && max && min === max) { + return selectedValueCount === min; + } + + if (min && max) { + return selectedValueCount >= min && selectedValueCount <= max; + } + + if (max) { + return selectedValueCount <= max; + } + + if (min) { + return selectedValueCount >= min; + } + + // min/max === 0 (i.e. not required) + return true; + } + + /** * Returns the disabled option choices for an item based on the selected options. */ - getDisabledOptionChoicesForSelectedOptions(item: ItemResource, itemOption: ItemOption, selectedOptions: OptionSelection[], removeMatchingOptionSet = true) { - const itemOptionChoices: OptionSelection[] = itemOption.choices.map(c => ({ - itemOptionId: itemOption.id, - choice: c - })); - const disabledOptionChoiceValues: string[] = []; - // In most use cases we want to compare the itemOption against OTHER selectedOptions' option sets - if (removeMatchingOptionSet) { - selectedOptions = selectedOptions.filter(s => s.itemOptionId !== itemOption.id); - } - itemOptionChoices.forEach((optionChoice) => { - if (this.isOptionChoiceDisabledForSelectedOptions(item, optionChoice, selectedOptions, removeMatchingOptionSet)) { - disabledOptionChoiceValues.push(optionChoice.choice); - } - }); - return disabledOptionChoiceValues; - } - - /** + getDisabledOptionChoicesForSelectedOptions(item: ItemResource, itemOption: ItemOption, selectedOptions: OptionSelection[], removeMatchingOptionSet = true) { + const itemOptionChoices: OptionSelection[] = itemOption.choices.map(c => ({ + itemOptionId: itemOption.id, + choice: c + })); + const disabledOptionChoiceValues: string[] = []; + // In most use cases we want to compare the itemOption against OTHER selectedOptions' option sets + if (removeMatchingOptionSet) { + selectedOptions = selectedOptions.filter(s => s.itemOptionId !== itemOption.id); + } + itemOptionChoices.forEach((optionChoice) => { + if (this.isOptionChoiceDisabledForSelectedOptions(item, optionChoice, selectedOptions, removeMatchingOptionSet)) { + disabledOptionChoiceValues.push(optionChoice.choice); + } + }); + return disabledOptionChoiceValues; + } + + /** * Returns whether an item with any combination of selected options, modifiers, variationId, and quantity is valid. * @throws {@link ValidateItemError} */ - validateItem({ item, selectedOptions = [], selectedModifiers = [], selectedVariationId = '', quantity = undefined, skipStockCheck = false, skipModifierCheck = false }: ValidateItemRequest): AddLineItem { - const errorItemOptionIds: string[] = []; - let flatVariationSelectionMissing = false; - let errorVariationId: string = ''; - let quantityErrorType: QuantityErrorTypeEnum = QuantityErrorType.SOLD_OUT; - const errorModifierListIds: string[] = []; - - if (item.item_options?.length && !selectedVariationId) { - item.item_options.forEach(o => { - if (!selectedOptions?.find(s => s.itemOptionId === o.id && o.choices.includes(s.choice))) { - errorItemOptionIds.push(o.id); - } - }); - } else if (!item.item_options && item.variations.length > 1 && !selectedVariationId) { - flatVariationSelectionMissing = true; - } - - // Only check in stock if there's no missing options (or selectedVariationId for flat variations) - let inStockVariation: Variation | null = null; - if (errorItemOptionIds.length === 0 && !flatVariationSelectionMissing) { - const inStockVariations = this.getInStockVariationsForSelectedOptionsOrVariation({ item, selectedOptions, selectedVariationId, skipStockCheck }); - if (inStockVariations.length === 0) { - const outOfStockVariations = this.getInStockVariationsForSelectedOptionsOrVariation({ item, selectedOptions, selectedVariationId, skipStockCheck: true }); - if (outOfStockVariations.length > 0) { - errorVariationId = outOfStockVariations[0].id; - } - } else { - inStockVariation = inStockVariations[0]; - if (quantity != null) { - const error = this.getItemQuantityError(item, inStockVariation, quantity); - if (error) { - quantityErrorType = error; - errorVariationId = inStockVariation.id; - } - } - } - } - - if (item.modifier_lists?.length && !skipModifierCheck) { - item.modifier_lists.forEach(m => { - if (!this.isModifierListForSelectedModifiersValid(m, selectedModifiers)) { - errorModifierListIds.push(m.id); - } - }); - } - - if (!inStockVariation || errorItemOptionIds.length || errorVariationId || errorModifierListIds.length) { - const validateError = new Error('Failed to validate item.') as ValidateItemError; - if (errorItemOptionIds.length) { - validateError.itemOptionIds = errorItemOptionIds; - } - if (flatVariationSelectionMissing) { - validateError.flatVariationSelectionMissing = true; - } - if (errorVariationId) { - validateError.variationId = errorVariationId; - validateError.quantityErrorType = quantityErrorType; - } - if (errorModifierListIds.length) { - validateError.modifierListIds = errorModifierListIds; - } - throw validateError; - } - - const addLineItem: AddLineItem = { - itemId: item.id, - variationId: inStockVariation.id, - modifiers: selectedModifiers - }; - if (quantity) { - addLineItem.quantity = quantity; - } - return addLineItem; - } - - /** + validateItem({ item, selectedOptions = [], selectedModifiers = [], selectedVariationId = '', quantity = undefined, skipStockCheck = false, skipModifierCheck = false }: ValidateItemRequest): AddLineItem { + const errorItemOptionIds: string[] = []; + let flatVariationSelectionMissing = false; + let errorVariationId: string = ''; + let quantityErrorType: QuantityErrorTypeEnum = QuantityErrorType.SOLD_OUT; + const errorModifierListIds: string[] = []; + + if (item.item_options?.length && !selectedVariationId) { + item.item_options.forEach(o => { + if (!selectedOptions?.find(s => s.itemOptionId === o.id && o.choices.includes(s.choice))) { + errorItemOptionIds.push(o.id); + } + }); + } else if (!item.item_options && item.variations.length > 1 && !selectedVariationId) { + flatVariationSelectionMissing = true; + } + + // Only check in stock if there's no missing options (or selectedVariationId for flat variations) + let inStockVariation: Variation | null = null; + if (errorItemOptionIds.length === 0 && !flatVariationSelectionMissing) { + const inStockVariations = this.getInStockVariationsForSelectedOptionsOrVariation({ item, selectedOptions, selectedVariationId, skipStockCheck }); + if (inStockVariations.length === 0) { + const outOfStockVariations = this.getInStockVariationsForSelectedOptionsOrVariation({ item, selectedOptions, selectedVariationId, skipStockCheck: true }); + if (outOfStockVariations.length > 0) { + errorVariationId = outOfStockVariations[0].id; + } + } else { + inStockVariation = inStockVariations[0]; + if (quantity != null) { + const error = this.getItemQuantityError(item, inStockVariation, quantity); + if (error) { + quantityErrorType = error; + errorVariationId = inStockVariation.id; + } + } + } + } + + if (item.modifier_lists?.length && !skipModifierCheck) { + item.modifier_lists.forEach(m => { + if (!this.isModifierListForSelectedModifiersValid(m, selectedModifiers)) { + errorModifierListIds.push(m.id); + } + }); + } + + if (!inStockVariation || errorItemOptionIds.length || errorVariationId || errorModifierListIds.length) { + const validateError = new Error('Failed to validate item.') as ValidateItemError; + if (errorItemOptionIds.length) { + validateError.itemOptionIds = errorItemOptionIds; + } + if (flatVariationSelectionMissing) { + validateError.flatVariationSelectionMissing = true; + } + if (errorVariationId) { + validateError.variationId = errorVariationId; + validateError.quantityErrorType = quantityErrorType; + } + if (errorModifierListIds.length) { + validateError.modifierListIds = errorModifierListIds; + } + throw validateError; + } + + const addLineItem: AddLineItem = { + itemId: item.id, + variationId: inStockVariation.id, + modifiers: selectedModifiers + }; + if (quantity) { + addLineItem.quantity = quantity; + } + return addLineItem; + } + + /** * Returns the price of an item based on the selected options, modifiers, and/or variation id. */ - getItemPrice({ item, selectedOptions = [], selectedVariationId = '', selectedModifiers = [], skipStockCheck = false, skipModifierCheck = false, formattedLocale = undefined }: GetItemPriceRequest): ItemPrice | null { - let validatedItem: AddLineItem | null = null; - try { - validatedItem = this.validateItem({ item, selectedOptions, selectedVariationId, selectedModifiers, skipStockCheck, skipModifierCheck }); - } catch (ex) { - // no-op - } - if (validatedItem) { - const variation = item.variations.find(v => v.id === validatedItem!.variationId)!; - let regularPrice = variation.price.regular.amount; - let salePrice = variation.price.sale.amount; - const currency = variation.price.regular.currency; - validatedItem.modifiers?.forEach(selectedModifier => { - if (selectedModifier.type === ModifierType.CHOICE || selectedModifier.type === ModifierType.GIFT_WRAP) { - const matchingModifierList = item.modifier_lists?.find(l => l.id === selectedModifier.id); - if (matchingModifierList) { - matchingModifierList.modifiers?.forEach(m => { - let quantity = 1; - if (selectedModifier.choiceSelections.find(s => { - if (typeof s === 'object') { - if (m.id === s.id) { - quantity = s.quantity; - return true; - } - } - return m.id === s; - }) && m.price_money) { - regularPrice += m.price_money.amount * quantity; - salePrice += m.price_money.amount * quantity; - } - }); - } - } - }); - const itemPrice: ItemPrice = { - regular: { - amount: regularPrice, - currency: currency, - formatted: '', - }, - sale: { - amount: salePrice, - currency: currency, - formatted: '', - } - }; - if (formattedLocale) { - const moneyHelper = new Money(); - itemPrice.regular.formatted = moneyHelper.formatMoney({ - amount: regularPrice, - currency: currency, - formatted: '' - }, formattedLocale); - itemPrice.sale.formatted = moneyHelper.formatMoney({ - amount: salePrice, - currency: currency, - formatted: '' - }, formattedLocale); - } - return itemPrice; - } - - return null; - } - - /** + getItemPrice({ item, selectedOptions = [], selectedVariationId = '', selectedModifiers = [], skipStockCheck = false, skipModifierCheck = false, formattedLocale = undefined }: GetItemPriceRequest): ItemPrice | null { + let validatedItem: AddLineItem | null = null; + try { + validatedItem = this.validateItem({ item, selectedOptions, selectedVariationId, selectedModifiers, skipStockCheck, skipModifierCheck }); + } catch (ex) { + // no-op + } + if (validatedItem) { + const variation = item.variations.find(v => v.id === validatedItem!.variationId)!; + let regularPrice = variation.price.regular.amount; + let salePrice = variation.price.sale.amount; + const currency = variation.price.regular.currency; + validatedItem.modifiers?.forEach(selectedModifier => { + if (selectedModifier.type === ModifierType.CHOICE || selectedModifier.type === ModifierType.GIFT_WRAP) { + const matchingModifierList = item.modifier_lists?.find(l => l.id === selectedModifier.id); + if (matchingModifierList) { + matchingModifierList.modifiers?.forEach(m => { + let quantity = 1; + if (selectedModifier.choiceSelections.find(s => { + if (typeof s === 'object') { + if (m.id === s.id) { + quantity = s.quantity; + return true; + } + } + return m.id === s; + }) && m.price_money) { + regularPrice += m.price_money.amount * quantity; + salePrice += m.price_money.amount * quantity; + } + }); + } + } + }); + const itemPrice: ItemPrice = { + regular: { + amount: regularPrice, + currency: currency, + formatted: '', + }, + sale: { + amount: salePrice, + currency: currency, + formatted: '', + } + }; + if (formattedLocale) { + const moneyHelper = new Money(); + itemPrice.regular.formatted = moneyHelper.formatMoney({ + amount: regularPrice, + currency: currency, + formatted: '' + }, formattedLocale); + itemPrice.sale.formatted = moneyHelper.formatMoney({ + amount: salePrice, + currency: currency, + formatted: '' + }, formattedLocale); + } + return itemPrice; + } + + return null; + } + + /** * Returns whether an item is an event and has ended. */ - isEventItemInThePast(item: ItemResource) { - if (item.square_online_type !== 'EVENT') { - return false; - } - const endDate = getEventEndDate(item); - return endDate <= new Date(); - } - - /** + isEventItemInThePast(item: ItemResource) { + if (item.square_online_type !== 'EVENT') { + return false; + } + const endDate = getEventEndDate(item); + return endDate <= new Date(); + } + + /** * Returns whether an item is a preorder and the cutoff time has passed. */ - isPreorderItemCutoffInThePast(item: ItemResource) { - if (!item.preordering.PICKUP) { - return false; - } - const cutoffTime = item.fulfillment_availability.PICKUP[0].availability_cutoff_at; - return new Date(cutoffTime) <= new Date(); - } - - /** + isPreorderItemCutoffInThePast(item: ItemResource) { + if (!item.preordering.PICKUP) { + return false; + } + const cutoffTime = item.fulfillment_availability.PICKUP[0].availability_cutoff_at; + return new Date(cutoffTime) <= new Date(); + } + + /** * Returns the item's prep time duration parsed into value, unit, is_time. * is_time means prep duration includes a time component (hour/minute/second). It's used to differentiate '4M' between '4 months' and '4 minutes' * Note that this function relies on the fact that prepTimeDuration currently only supports * a single time unit! I.e. P2DT6H20M is not currently supported and will not work. */ - parsePrepTime(prepTimeDuration: string): ItemPrepTime | null { - const parsedPrepTime = /(PT(?\d+)(?\w))|(P(?\d+)(?\w))/g.exec(prepTimeDuration)?.groups; - if (!(parsedPrepTime?.timeValue && parsedPrepTime?.timeUnit) && !(parsedPrepTime?.value && parsedPrepTime?.unit)) { - return null; - } - - const isTime = Boolean(parsedPrepTime?.timeValue); - const value = isTime - ? Number(parsedPrepTime?.timeValue) - : Number(parsedPrepTime?.value); - const unit = isTime - ? parsedPrepTime?.timeUnit - : parsedPrepTime?.unit; - - return { - value: value, - unit: unit, - is_time: isTime, - }; - } + parsePrepTime(prepTimeDuration: string): ItemPrepTime | null { + const parsedPrepTime = /(PT(?\d+)(?\w))|(P(?\d+)(?\w))/g.exec(prepTimeDuration)?.groups; + if (!(parsedPrepTime?.timeValue && parsedPrepTime?.timeUnit) && !(parsedPrepTime?.value && parsedPrepTime?.unit)) { + return null; + } + + const isTime = Boolean(parsedPrepTime?.timeValue); + const value = isTime + ? Number(parsedPrepTime?.timeValue) + : Number(parsedPrepTime?.value); + const unit = isTime + ? parsedPrepTime?.timeUnit + : parsedPrepTime?.unit; + + return { + value: value, + unit: unit, + is_time: isTime, + }; + } } diff --git a/src/helpers/location.ts b/src/helpers/location.ts index 64be447..230f2da 100644 --- a/src/helpers/location.ts +++ b/src/helpers/location.ts @@ -1,33 +1,33 @@ import { - localizeDate, - getDayOfWeekKey, - getNumericTime, - getDateObjInTimezone, - getFormattedTime, + localizeDate, + getDayOfWeekKey, + getNumericTime, + getDateObjInTimezone, + getFormattedTime, } from './datetime'; import { - Hours, - OpenInterval, - OpenStatus, - OpenStatusDayAndTime, - Location as LocationResource, + Hours, + OpenInterval, + OpenStatus, + OpenStatusDayAndTime, + Location as LocationResource, } from '../types/helpers/location'; import { - DayOfWeek, - TimeString, - DateFormats, + DayOfWeek, + TimeString, + DateFormats, } from '../types/helpers/datetime'; import type { - FulfillmentType, + FulfillmentType, } from '../types/api/cart'; export class Location { - readonly OpenStatus = OpenStatus; + readonly OpenStatus = OpenStatus; - /** + /** * Returns the current open status and relevant time for a location's fulfillment type * * @param {LocationResource} location @@ -35,35 +35,35 @@ export class Location { * @param {'PICKUP' | 'DELIVERY'} fulfillment * @return { OpenStatusDayAndTime | null } */ - getLocationFulfillmentOpenStatusDayAndTime( - location: LocationResource, - locale: string, - fulfillment: typeof FulfillmentType.PICKUP | typeof FulfillmentType.DELIVERY, - ): OpenStatusDayAndTime | null { - const timezone = location.timezone.name; - const tzOffsetString = location.timezone.offset_string; - const hours = location[fulfillment].hours; - return this.getOpenStatusDayAndTime(locale, timezone, tzOffsetString, hours); - } + getLocationFulfillmentOpenStatusDayAndTime( + location: LocationResource, + locale: string, + fulfillment: typeof FulfillmentType.PICKUP | typeof FulfillmentType.DELIVERY, + ): OpenStatusDayAndTime | null { + const timezone = location.timezone.name; + const tzOffsetString = location.timezone.offset_string; + const hours = location[fulfillment].hours; + return this.getOpenStatusDayAndTime(locale, timezone, tzOffsetString, hours); + } - /** + /** * Returns the current open status and relevant time for a location's business hours * * @param {LocationResource} location * @param {string} locale - Hyphenized language/country locale combo, e.g. en-US (BCP 47). * @return { OpenStatusDayAndTime | null } */ - getLocationBusinessHoursOpenStatusDayAndTime( - location: LocationResource, - locale: string, - ): OpenStatusDayAndTime | null { - const timezone = location.timezone.name; - const tzOffsetString = location.timezone.offset_string; - const hours = location.square_business_hours; - return this.getOpenStatusDayAndTime(locale, timezone, tzOffsetString, hours); - } + getLocationBusinessHoursOpenStatusDayAndTime( + location: LocationResource, + locale: string, + ): OpenStatusDayAndTime | null { + const timezone = location.timezone.name; + const tzOffsetString = location.timezone.offset_string; + const hours = location.square_business_hours; + return this.getOpenStatusDayAndTime(locale, timezone, tzOffsetString, hours); + } - /** + /** * Given an hours object, returns the current open status and relevant time * * @param {string} locale - Hyphenized language/country locale combo, e.g. en-US (BCP 47). @@ -72,62 +72,62 @@ export class Location { * @param {Hours} hours * @return { OpenStatusDayAndTime | null } */ - getOpenStatusDayAndTime( - locale: string, - timezone: string, - tzOffsetString: string, - hours: Hours, - ): OpenStatusDayAndTime | null { - const dateNow = new Date(); - const currentOpenInterval = this.getCurrentOpenInterval(locale, timezone, hours); - if (currentOpenInterval) { - const dateObj = this.getIntervalCloseDateObject(currentOpenInterval, locale, timezone, tzOffsetString, hours); - if (!dateObj) { - // return special case for 24/7 businesses - return { - status: this.OpenStatus.CURRENTLY_OPEN, - time: '', - day: '', - }; - } + getOpenStatusDayAndTime( + locale: string, + timezone: string, + tzOffsetString: string, + hours: Hours, + ): OpenStatusDayAndTime | null { + const dateNow = new Date(); + const currentOpenInterval = this.getCurrentOpenInterval(locale, timezone, hours); + if (currentOpenInterval) { + const dateObj = this.getIntervalCloseDateObject(currentOpenInterval, locale, timezone, tzOffsetString, hours); + if (!dateObj) { + // return special case for 24/7 businesses + return { + status: this.OpenStatus.CURRENTLY_OPEN, + time: '', + day: '', + }; + } - const closeTime = localizeDate(dateObj, locale, DateFormats.hourNminuteNsecondN, timezone, false); - const openUntil = getFormattedTime(dateObj, closeTime, DateFormats.hourNminuteN, locale, timezone, tzOffsetString); - const openUntilDay = getFormattedTime(dateObj, closeTime, DateFormats.weekdayLong, locale, timezone, tzOffsetString); - return { - status: this.OpenStatus.CURRENTLY_OPEN, - time: openUntil, - day: openUntilDay, - }; - } + const closeTime = localizeDate(dateObj, locale, DateFormats.hourNminuteNsecondN, timezone, false); + const openUntil = getFormattedTime(dateObj, closeTime, DateFormats.hourNminuteN, locale, timezone, tzOffsetString); + const openUntilDay = getFormattedTime(dateObj, closeTime, DateFormats.weekdayLong, locale, timezone, tzOffsetString); + return { + status: this.OpenStatus.CURRENTLY_OPEN, + time: openUntil, + day: openUntilDay, + }; + } - const nextOpenIntervalToday = this.getNextOpenIntervalToday(locale, timezone, hours); - if (nextOpenIntervalToday) { - const opensAt = getFormattedTime(dateNow, nextOpenIntervalToday.open, DateFormats.hourNminuteN, locale, timezone, tzOffsetString); - const opensAtDay = getFormattedTime(dateNow, nextOpenIntervalToday.open, DateFormats.weekdayLong, locale, timezone, tzOffsetString); - return { - status: this.OpenStatus.OPENS_LATER_TODAY, - time: opensAt, - day: opensAtDay, - }; - } + const nextOpenIntervalToday = this.getNextOpenIntervalToday(locale, timezone, hours); + if (nextOpenIntervalToday) { + const opensAt = getFormattedTime(dateNow, nextOpenIntervalToday.open, DateFormats.hourNminuteN, locale, timezone, tzOffsetString); + const opensAtDay = getFormattedTime(dateNow, nextOpenIntervalToday.open, DateFormats.weekdayLong, locale, timezone, tzOffsetString); + return { + status: this.OpenStatus.OPENS_LATER_TODAY, + time: opensAt, + day: opensAtDay, + }; + } - const nextOpenIntervalAfterToday = this.getNextOpenIntervalAfterToday(locale, timezone, hours); - if (nextOpenIntervalAfterToday) { - const { date, interval: nextInterval } = nextOpenIntervalAfterToday; - const opensAt = getFormattedTime(date, nextInterval.open, DateFormats.hourNminuteN, locale, timezone, tzOffsetString); - const opensAtDay = getFormattedTime(date, nextInterval.open, DateFormats.weekdayLong, locale, timezone, tzOffsetString); - return { - status: this.OpenStatus.OPENS_ANOTHER_DAY, - time: opensAt, - day: opensAtDay, - }; - } + const nextOpenIntervalAfterToday = this.getNextOpenIntervalAfterToday(locale, timezone, hours); + if (nextOpenIntervalAfterToday) { + const { date, interval: nextInterval } = nextOpenIntervalAfterToday; + const opensAt = getFormattedTime(date, nextInterval.open, DateFormats.hourNminuteN, locale, timezone, tzOffsetString); + const opensAtDay = getFormattedTime(date, nextInterval.open, DateFormats.weekdayLong, locale, timezone, tzOffsetString); + return { + status: this.OpenStatus.OPENS_ANOTHER_DAY, + time: opensAt, + day: opensAtDay, + }; + } - return null; - } + return null; + } - /** + /** * Returns date object of interval close time, accounting for 24 hour businesses, and hours that span between days * * @param {OpenInterval} currentOpenInterval @@ -137,74 +137,74 @@ export class Location { * @param {Hours} hours * @return {Date} */ - getIntervalCloseDateObject( - currentOpenInterval: OpenInterval, - locale: string, - timezone: string, - tzOffsetString: string, - hours: Hours - ): Date | null { - const dateNow = new Date(); - let currentTimeString = currentOpenInterval.close; - let currentDateString = localizeDate(dateNow, locale, DateFormats.yearNmonthNdayN, timezone); + getIntervalCloseDateObject( + currentOpenInterval: OpenInterval, + locale: string, + timezone: string, + tzOffsetString: string, + hours: Hours + ): Date | null { + const dateNow = new Date(); + let currentTimeString = currentOpenInterval.close; + let currentDateString = localizeDate(dateNow, locale, DateFormats.yearNmonthNdayN, timezone); - // This bit of logic is for handling businesses that are open for 24 hours a day, or for businesses - // that are open across days (e.g. 10:00 PM - 2:00 AM) - let previousOpenInterval: null | OpenInterval = null; - let previousOpenDay = 0; - if (currentTimeString === '24:00:00') { - const nextDateObj = new Date(); - for (let i = 0; i < 8; i += 1) { - // If we are on the 8th loop, then the business is open 24/7 - if (i === 7) { - return null; - } + // This bit of logic is for handling businesses that are open for 24 hours a day, or for businesses + // that are open across days (e.g. 10:00 PM - 2:00 AM) + let previousOpenInterval: null | OpenInterval = null; + let previousOpenDay = 0; + if (currentTimeString === '24:00:00') { + const nextDateObj = new Date(); + for (let i = 0; i < 8; i += 1) { + // If we are on the 8th loop, then the business is open 24/7 + if (i === 7) { + return null; + } - nextDateObj.setDate(nextDateObj.getDate() + 1); - const dayOfWeekKey = getDayOfWeekKey( - nextDateObj, - locale, - timezone, - ); + nextDateObj.setDate(nextDateObj.getDate() + 1); + const dayOfWeekKey = getDayOfWeekKey( + nextDateObj, + locale, + timezone, + ); - const openIntervals = hours[dayOfWeekKey]; - if (openIntervals.length) { - const openInterval = openIntervals.find(time => time.open === '00:00:00'); + const openIntervals = hours[dayOfWeekKey]; + if (openIntervals.length) { + const openInterval = openIntervals.find(time => time.open === '00:00:00'); - // If interval opens at 00:00:00 and closes at 24:00:00, then the business is open that full day - // continue to the next day - if (openInterval && openInterval.close === '24:00:00') { - previousOpenInterval = openInterval; - previousOpenDay = nextDateObj.getDay(); - continue; - } + // If interval opens at 00:00:00 and closes at 24:00:00, then the business is open that full day + // continue to the next day + if (openInterval && openInterval.close === '24:00:00') { + previousOpenInterval = openInterval; + previousOpenDay = nextDateObj.getDay(); + continue; + } - // If interval is open at 00:00:00 and closes at a time before 24:00:00, we should return that closing time and day - if (openInterval) { - currentTimeString = openInterval.close; - currentDateString = localizeDate(nextDateObj, locale, DateFormats.yearNmonthNdayN, timezone); - break; - } - } + // If interval is open at 00:00:00 and closes at a time before 24:00:00, we should return that closing time and day + if (openInterval) { + currentTimeString = openInterval.close; + currentDateString = localizeDate(nextDateObj, locale, DateFormats.yearNmonthNdayN, timezone); + break; + } + } - // This handles the case where a business is open for 24 hours a day for multiple days in a row, but not fully 24/7 - if (previousOpenInterval) { - currentTimeString = previousOpenInterval.close; - nextDateObj.setDate(previousOpenDay); - currentDateString = localizeDate(nextDateObj, locale, DateFormats.yearNmonthNdayN, timezone); - } - break; - } - } + // This handles the case where a business is open for 24 hours a day for multiple days in a row, but not fully 24/7 + if (previousOpenInterval) { + currentTimeString = previousOpenInterval.close; + nextDateObj.setDate(previousOpenDay); + currentDateString = localizeDate(nextDateObj, locale, DateFormats.yearNmonthNdayN, timezone); + } + break; + } + } - return getDateObjInTimezone( - currentDateString, - currentTimeString, - tzOffsetString, - ); - } + return getDateObjInTimezone( + currentDateString, + currentTimeString, + tzOffsetString, + ); + } - /** + /** * Gets open intervals for today * * @param {string} locale - Hyphenized language/country locale combo, e.g. en-US (BCP 47). @@ -212,20 +212,20 @@ export class Location { * @param {Hours} hours * @return {OpenInterval[]} */ - getOpenIntervalsForToday( - locale: string, - timezone: string, - hours: Hours - ): OpenInterval[] { - const dayOfWeekKey: DayOfWeek = getDayOfWeekKey( - new Date(), - locale, - timezone - ); - return hours[dayOfWeekKey]; - } + getOpenIntervalsForToday( + locale: string, + timezone: string, + hours: Hours + ): OpenInterval[] { + const dayOfWeekKey: DayOfWeek = getDayOfWeekKey( + new Date(), + locale, + timezone + ); + return hours[dayOfWeekKey]; + } - /** + /** * Get the open interval if we are currently in an open interval. Otherwise null * * @param {string} locale - Hyphenized language/country locale combo, e.g. en-US (BCP 47). @@ -233,32 +233,32 @@ export class Location { * @param {Hours} hours * @return {OpenInterval | null } */ - getCurrentOpenInterval( - locale: string, - timezone: string, - hours: Hours - ): OpenInterval | null { - const todayOpenIntervals = this.getOpenIntervalsForToday(locale, timezone, hours); - if (!todayOpenIntervals.length) { - return null; - } - const currentTimeStr = localizeDate(new Date(), locale, DateFormats.hourNminuteNsecondN, timezone, false) as TimeString; - let currentTimeNum = getNumericTime(currentTimeStr); + getCurrentOpenInterval( + locale: string, + timezone: string, + hours: Hours + ): OpenInterval | null { + const todayOpenIntervals = this.getOpenIntervalsForToday(locale, timezone, hours); + if (!todayOpenIntervals.length) { + return null; + } + const currentTimeStr = localizeDate(new Date(), locale, DateFormats.hourNminuteNsecondN, timezone, false) as TimeString; + let currentTimeNum = getNumericTime(currentTimeStr); - // Times between 00:00:00 and 01:00:00 are between 240000 and 250000... so we need to adjust - if (currentTimeNum >= 240000) { - currentTimeNum -= 240000; - } + // Times between 00:00:00 and 01:00:00 are between 240000 and 250000... so we need to adjust + if (currentTimeNum >= 240000) { + currentTimeNum -= 240000; + } - const openInterval = todayOpenIntervals.find((openInterval) => { - const { open, close } = openInterval; - return currentTimeNum >= getNumericTime(open) && currentTimeNum < getNumericTime(close); - }); + const openInterval = todayOpenIntervals.find((openInterval) => { + const { open, close } = openInterval; + return currentTimeNum >= getNumericTime(open) && currentTimeNum < getNumericTime(close); + }); - return openInterval || null; - } + return openInterval || null; + } - /** + /** * Get the next open interval today * If we are currently open in an interval, that will be returned * @@ -267,40 +267,38 @@ export class Location { * @param {Hours} hours * @return {OpenInterval | null } */ - getNextOpenIntervalToday( - locale: string, - timezone: string, - hours: Hours - ): OpenInterval | null { - const todayOpenIntervals = this.getOpenIntervalsForToday(locale, timezone, hours); - if (!todayOpenIntervals.length) { - return null; - } + getNextOpenIntervalToday( + locale: string, + timezone: string, + hours: Hours + ): OpenInterval | null { + const todayOpenIntervals = this.getOpenIntervalsForToday(locale, timezone, hours); + if (!todayOpenIntervals.length) { + return null; + } - const currentTimeStr = localizeDate(new Date(), locale, DateFormats.hourNminuteNsecondN, timezone, false) as TimeString; - const currentTimeNum = getNumericTime(currentTimeStr); - let currentOrNextInterval: OpenInterval | null = null; - todayOpenIntervals.forEach((openInterval) => { - const intervalStartNum = getNumericTime(openInterval.open); - const intervalEndNum = getNumericTime(openInterval.close); - const intervalIsCurrentOrLater = currentTimeNum < intervalStartNum + const currentTimeStr = localizeDate(new Date(), locale, DateFormats.hourNminuteNsecondN, timezone, false) as TimeString; + const currentTimeNum = getNumericTime(currentTimeStr); + let currentOrNextInterval: OpenInterval | null = null; + todayOpenIntervals.forEach((openInterval) => { + const intervalStartNum = getNumericTime(openInterval.open); + const intervalEndNum = getNumericTime(openInterval.close); + const intervalIsCurrentOrLater = currentTimeNum < intervalStartNum || (currentTimeNum >= intervalStartNum && currentTimeNum < intervalEndNum); - if ( - intervalIsCurrentOrLater - && ( - // If we don't have a currentOrNextInterval yet, or if the current interval is earlier than the currentOrNextInterval - // we are doing this check since the intervals could be be out of order, and we want to make sure we are returing the - // next closest interval - !currentOrNextInterval || intervalStartNum < getNumericTime(currentOrNextInterval.open) - ) - ) { - currentOrNextInterval = openInterval; - } - }); - return currentOrNextInterval; - } + if ( + intervalIsCurrentOrLater + // If we don't have a currentOrNextInterval yet, or if the current interval is earlier than the currentOrNextInterval + // we are doing this check since the intervals could be be out of order, and we want to make sure we are returing the + // next closest interval + && (!currentOrNextInterval || intervalStartNum < getNumericTime(currentOrNextInterval.open)) + ) { + currentOrNextInterval = openInterval; + } + }); + return currentOrNextInterval; + } - /** + /** * Get the next opening period after today within the next seven days * * @param {string} locale - Hyphenized language/country locale combo, e.g. en-US (BCP 47). @@ -308,40 +306,40 @@ export class Location { * @param {Hours} hours * @return {Object | null} { open: openTime, close: closeTime } */ - getNextOpenIntervalAfterToday( - locale: string, - timezone: string, - hours: Hours, - ): { + getNextOpenIntervalAfterToday( + locale: string, + timezone: string, + hours: Hours, + ): { date: Date; interval: OpenInterval; } | null - { - const nextDateObj = new Date(); - let nextOpenInterval = null; - for (let i = 0; i < 7; i += 1) { - nextDateObj.setDate(nextDateObj.getDate() + 1); - const dayOfWeekKey = getDayOfWeekKey( - nextDateObj, - locale, - timezone, - ); + { + const nextDateObj = new Date(); + let nextOpenInterval = null; + for (let i = 0; i < 7; i += 1) { + nextDateObj.setDate(nextDateObj.getDate() + 1); + const dayOfWeekKey = getDayOfWeekKey( + nextDateObj, + locale, + timezone, + ); - const openIntervals = hours[dayOfWeekKey]; + const openIntervals = hours[dayOfWeekKey]; - if (openIntervals.length) { - nextOpenInterval = openIntervals[0]; - break; - } - } + if (openIntervals.length) { + nextOpenInterval = openIntervals[0]; + break; + } + } - if (!nextOpenInterval) { - return null; - } + if (!nextOpenInterval) { + return null; + } - return { - date: nextDateObj, - interval: nextOpenInterval, - }; - } + return { + date: nextDateObj, + interval: nextOpenInterval, + }; + } } diff --git a/src/helpers/money.ts b/src/helpers/money.ts index 0fbf263..1f1f05b 100644 --- a/src/helpers/money.ts +++ b/src/helpers/money.ts @@ -4,27 +4,27 @@ import { Money as MoneyType } from '../types/helpers/money'; // use for currencies like "IQD" which is considered 0 decimal places from this logic, but 3 decimal places in the BE package // and the ISO 4217 spec. May need to re-visit if Square expands currency support. const getCurrencyFractionLength = (currency: string) => { - const numberFormat = new Intl.NumberFormat('en-US', { - style: 'currency', - currency: currency - }); - const fractionLength = numberFormat.formatToParts(1).find(part => part.type === 'fraction')?.value.length ?? 0; - return fractionLength; + const numberFormat = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: currency + }); + const fractionLength = numberFormat.formatToParts(1).find(part => part.type === 'fraction')?.value.length ?? 0; + return fractionLength; }; export class Money { - /** + /** * Formats the Money object based on the provided locale. * * @param money - The Money object to format. * @param formattedLocale - The locale to format the Money object in (BCP 47). * @returns The formatted amount. */ - formatMoney(money: MoneyType, formattedLocale = 'en-US'): string { - return this.formatAmount(money.amount, money.currency, formattedLocale); - } + formatMoney(money: MoneyType, formattedLocale = 'en-US'): string { + return this.formatAmount(money.amount, money.currency, formattedLocale); + } - /** + /** * Formats a subunits amount based on the provided currency and locale. * * @param amount - The amount in subunits. @@ -32,51 +32,51 @@ export class Money { * @param formattedLocale - The locale to format the amount in (BCP 47). * @returns The formatted amount. */ - formatAmount(amount: number, currency: string, formattedLocale = 'en-US'): string { - let formatter; - try { - formatter = new Intl.NumberFormat(formattedLocale, { - style: 'currency', - currency: currency - }); - } catch (err) { - // Fallback to en-US if it's an invalid BCP 47 locale - formatter = new Intl.NumberFormat('en-US', { - style: 'currency', - currency: currency - }); - } - amount = this.convertSubunitsToFloat(amount, currency); - return formatter.format(amount); - } + formatAmount(amount: number, currency: string, formattedLocale = 'en-US'): string { + let formatter; + try { + formatter = new Intl.NumberFormat(formattedLocale, { + style: 'currency', + currency: currency + }); + } catch (err) { + // Fallback to en-US if it's an invalid BCP 47 locale + formatter = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: currency + }); + } + amount = this.convertSubunitsToFloat(amount, currency); + return formatter.format(amount); + } - /** + /** * Converts a float amount to the lowest subunits for the currency, e.g. 10.00 to 1000 for USD. * * @param float - The float amount to convert. * @param currency - The currency of the amount (ISO 4217). * @returns The amount in subunits. */ - convertFloatToSubunits(float: number, currency: string): number { - const fractionLength = getCurrencyFractionLength(currency); - if (fractionLength > 0) { - return float * Math.pow(10, fractionLength); - } - return float; - } + convertFloatToSubunits(float: number, currency: string): number { + const fractionLength = getCurrencyFractionLength(currency); + if (fractionLength > 0) { + return float * Math.pow(10, fractionLength); + } + return float; + } - /** + /** * Converts a subunits amount to a float for the currency, e.g. 1000 subunits to 10.00 for USD. * * @param subunits - The subunits amount to convert. * @param currency - The currency of the amount (ISO 4217). * @returns The amount as a float. */ - convertSubunitsToFloat(subunits: number, currency: string): number { - const fractionLength = getCurrencyFractionLength(currency); - if (fractionLength > 0) { - return subunits / Math.pow(10, fractionLength); - } - return subunits; - } + convertSubunitsToFloat(subunits: number, currency: string): number { + const fractionLength = getCurrencyFractionLength(currency); + if (fractionLength > 0) { + return subunits / Math.pow(10, fractionLength); + } + return subunits; + } } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index cbd4e59..dfdf15e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,47 +18,47 @@ export interface InitConfig { } class SiteThemeSDK { - version: string = '0.0.0-semantic-release'; - cart: Cart; - orders: Orders; - places: Places; - resources: Resources; - template: Template; - customers: Customers; - helpers: { + version: string = '0.0.0-semantic-release'; + cart: Cart; + orders: Orders; + places: Places; + resources: Resources; + template: Template; + customers: Customers; + helpers: { item: Item; location: Location; money: Money; }; - constructor(initObj: InitConfig) { - if (!initObj.userId) { - throw new Error('missing user id'); - } - if (!initObj.siteId) { - throw new Error('missing site id'); - } - if (!initObj.merchantId) { - throw new Error('missing merchant id'); - } - if (!Number.isInteger(Number(initObj.userId))) { - throw new Error('invalid user id'); - } - if (!Number.isInteger(Number(initObj.siteId))) { - throw new Error('invalid site id'); - } - this.cart = new Cart(); - this.orders = new Orders(initObj); - this.places = new Places(initObj); - this.resources = new Resources(); - this.template = new Template(); - this.customers = new Customers(initObj); - this.helpers = { - item: new Item(), - location: new Location(), - money: new Money() - }; - } + constructor(initObj: InitConfig) { + if (!initObj.userId) { + throw new Error('missing user id'); + } + if (!initObj.siteId) { + throw new Error('missing site id'); + } + if (!initObj.merchantId) { + throw new Error('missing merchant id'); + } + if (!Number.isInteger(Number(initObj.userId))) { + throw new Error('invalid user id'); + } + if (!Number.isInteger(Number(initObj.siteId))) { + throw new Error('invalid site id'); + } + this.cart = new Cart(); + this.orders = new Orders(initObj); + this.places = new Places(initObj); + this.resources = new Resources(); + this.template = new Template(); + this.customers = new Customers(initObj); + this.helpers = { + item: new Item(), + location: new Location(), + money: new Money() + }; + } } export default SiteThemeSDK; diff --git a/src/types/api/cart/index.ts b/src/types/api/cart/index.ts index 5b21daf..fc0bcf8 100644 --- a/src/types/api/cart/index.ts +++ b/src/types/api/cart/index.ts @@ -1,37 +1,37 @@ /* eslint-disable @typescript-eslint/naming-convention */ export const FulfillmentType = { - SHIPMENT: 'SHIPMENT', - PICKUP: 'PICKUP', - DELIVERY: 'DELIVERY', - MANUAL: 'MANUAL' + SHIPMENT: 'SHIPMENT', + PICKUP: 'PICKUP', + DELIVERY: 'DELIVERY', + MANUAL: 'MANUAL' } as const; export type FulfillmentTypeEnum = typeof FulfillmentType[keyof typeof FulfillmentType]; export const ScheduleType = { - ASAP: 'ASAP', - SCHEDULED: 'SCHEDULED' + ASAP: 'ASAP', + SCHEDULED: 'SCHEDULED' } as const; export type ScheduleTypeEnum = typeof ScheduleType[keyof typeof ScheduleType]; export const ModifierType = { - CHOICE: 'CHOICE', - TEXT: 'TEXT', - GIFT_WRAP: 'GIFT_WRAP', - GIFT_MESSAGE: 'GIFT_MESSAGE' + CHOICE: 'CHOICE', + TEXT: 'TEXT', + GIFT_WRAP: 'GIFT_WRAP', + GIFT_MESSAGE: 'GIFT_MESSAGE' } as const; export type ModifierTypeEnum = typeof ModifierType[keyof typeof ModifierType]; export const CurrencyType = { - AUD: 'AUD', - CAD: 'CAD', - JPY: 'JPY', - GBP: 'GBP', - USD: 'USD', - EUR: 'EUR' + AUD: 'AUD', + CAD: 'CAD', + JPY: 'JPY', + GBP: 'GBP', + USD: 'USD', + EUR: 'EUR' } as const; export type CurrencyTypeEnum = typeof CurrencyType[keyof typeof CurrencyType] @@ -44,15 +44,15 @@ export type CurrencyTypeEnum = typeof CurrencyType[keyof typeof CurrencyType] * */ export const DiscountType = { - FIXED_AMOUNT: 'FIXED_AMOUNT', - FIXED_PERCENTAGE: 'FIXED_PERCENTAGE' + FIXED_AMOUNT: 'FIXED_AMOUNT', + FIXED_PERCENTAGE: 'FIXED_PERCENTAGE' } as const; export type DiscountTypeEnum = typeof DiscountType[keyof typeof DiscountType] export const DiscountScopeType = { - WHOLE_PURCHASE: 'WHOLE_PURCHASE', - LINE_ITEM: 'LINE_ITEM', + WHOLE_PURCHASE: 'WHOLE_PURCHASE', + LINE_ITEM: 'LINE_ITEM', } as const; export type DiscountScopeTypeEnum = typeof DiscountScopeType[keyof typeof DiscountScopeType] diff --git a/src/types/api/cart/private.types.ts b/src/types/api/cart/private.types.ts index 92d422e..94de8b4 100644 --- a/src/types/api/cart/private.types.ts +++ b/src/types/api/cart/private.types.ts @@ -1,6 +1,6 @@ import { - CartValidationErrors, - CartError + CartValidationErrors, + CartError } from '.'; import type LooseObject from '../../looseobject'; diff --git a/src/types/api/places/index.ts b/src/types/api/places/index.ts index b332540..589eed4 100644 --- a/src/types/api/places/index.ts +++ b/src/types/api/places/index.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ export const AutocompletePlaceType = { - ADDRESS: 'address', - GEOCODE: 'geocode' + ADDRESS: 'address', + GEOCODE: 'geocode' } as const; export type AutocompletePlaceTypeEnum = typeof AutocompletePlaceType[keyof typeof AutocompletePlaceType]; diff --git a/src/types/api/template/index.ts b/src/types/api/template/index.ts index 8556fcd..0d9a29c 100644 --- a/src/types/api/template/index.ts +++ b/src/types/api/template/index.ts @@ -1,13 +1,13 @@ import type LooseObject from '../../looseobject'; export class TemplateError extends Error { - /** Provides the generic rendered HTML error template that would be rendered via the page on a failure. You can choose to use this to display a rendered error, or handle it how you see fit. */ - template: string; + /** Provides the generic rendered HTML error template that would be rendered via the page on a failure. You can choose to use this to display a rendered error, or handle it how you see fit. */ + template: string; - constructor(message: string, template: string) { - super(message); - this.template = template; - } + constructor(message: string, template: string) { + super(message); + this.template = template; + } } export interface TemplateRequest { diff --git a/src/types/helpers/datetime/index.ts b/src/types/helpers/datetime/index.ts index a521437..7cd346f 100644 --- a/src/types/helpers/datetime/index.ts +++ b/src/types/helpers/datetime/index.ts @@ -5,19 +5,19 @@ export type TimeString = `${number}${number}:${number}${number}:${number}${numbe export type DayOfWeek = 'MON' | 'TUE' | 'WED' | 'THU' | 'FRI' | 'SAT' | 'SUN'; export const DateFormats = { - weekdayShort: { weekday: 'short' }, - weekdayLong: { weekday: 'long' }, - hourNminuteN: { hour: 'numeric', minute: 'numeric' }, - hourNminuteNsecondN: { hour: 'numeric', minute: 'numeric', second: 'numeric' }, - yearNmonth2day2: { year: 'numeric', month: '2-digit', day: '2-digit' }, - yearNmonthNdayN: { year: 'numeric', month: 'numeric', day: 'numeric' }, - yearNmonthLdayN: { year: 'numeric', month: 'long', day: 'numeric' }, - yearNmonthSdayN: { year: 'numeric', month: 'short', day: 'numeric' }, - yearNmonthLdayNhourNminuteN: { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' }, - yearNmonthSdayNhourNminuteN: { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }, - weekdayLyearNmonthLdayNhourNminuteN: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' }, - weekdaySyearNmonthSdayNhourNminuteN: { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }, - weekdayLhourNminuteN: { weekday: 'long', hour: 'numeric', minute: 'numeric' }, + weekdayShort: { weekday: 'short' }, + weekdayLong: { weekday: 'long' }, + hourNminuteN: { hour: 'numeric', minute: 'numeric' }, + hourNminuteNsecondN: { hour: 'numeric', minute: 'numeric', second: 'numeric' }, + yearNmonth2day2: { year: 'numeric', month: '2-digit', day: '2-digit' }, + yearNmonthNdayN: { year: 'numeric', month: 'numeric', day: 'numeric' }, + yearNmonthLdayN: { year: 'numeric', month: 'long', day: 'numeric' }, + yearNmonthSdayN: { year: 'numeric', month: 'short', day: 'numeric' }, + yearNmonthLdayNhourNminuteN: { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' }, + yearNmonthSdayNhourNminuteN: { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }, + weekdayLyearNmonthLdayNhourNminuteN: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' }, + weekdaySyearNmonthSdayNhourNminuteN: { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }, + weekdayLhourNminuteN: { weekday: 'long', hour: 'numeric', minute: 'numeric' }, } as const; export interface Duration { diff --git a/src/types/helpers/item/index.ts b/src/types/helpers/item/index.ts index 022eb7a..8c0f188 100644 --- a/src/types/helpers/item/index.ts +++ b/src/types/helpers/item/index.ts @@ -1,26 +1,26 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { - AddItemModifier, - ModifierTypeEnum + AddItemModifier, + ModifierTypeEnum } from '../../api/cart'; import { - Money + Money } from '../money'; export const QuantityErrorType = { - INVALID_QUANTITY: 'INVALID_QUANTITY', - SOLD_OUT: 'SOLD_OUT', - STOCK_EXCEEDED: 'STOCK_EXCEEDED', - PER_ORDER_MAX_EXCEEDED: 'PER_ORDER_MAX_EXCEEDED' + INVALID_QUANTITY: 'INVALID_QUANTITY', + SOLD_OUT: 'SOLD_OUT', + STOCK_EXCEEDED: 'STOCK_EXCEEDED', + PER_ORDER_MAX_EXCEEDED: 'PER_ORDER_MAX_EXCEEDED' } as const; export type QuantityErrorTypeEnum = typeof QuantityErrorType[keyof typeof QuantityErrorType]; export const SquareOnlineType = { - EVENT: 'EVENT', - PHYSICAL: 'PHYSICAL', + EVENT: 'EVENT', + PHYSICAL: 'PHYSICAL', } as const; export type SquareOnlineTypeEnum = typeof SquareOnlineType[keyof typeof SquareOnlineType]; diff --git a/src/types/helpers/location/index.ts b/src/types/helpers/location/index.ts index 68ce914..b48951d 100644 --- a/src/types/helpers/location/index.ts +++ b/src/types/helpers/location/index.ts @@ -1,15 +1,15 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type { - Money + Money } from '../money'; import { - TimeString, - Duration, + TimeString, + Duration, } from '../datetime'; import { - FulfillmentType, + FulfillmentType, } from '../../api/cart'; export enum OpenStatus { diff --git a/test/cart.test.ts b/test/cart.test.ts index bcd02e8..977eea5 100644 --- a/test/cart.test.ts +++ b/test/cart.test.ts @@ -2,17 +2,17 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { describe, expect, it, vi, beforeEach } from 'vitest'; import { - AddLineItem, - AddItemRequest, - BuyNowItemRequest, - FulfillmentType, - ModifierType, - CurrencyType, - AddItemModifier, - CartFulfillment, - ScheduleType, - AttachLoyaltyRewardRequest, - RemoveLoyaltyRewardRequest, DiscountTypeEnum, DiscountScopeTypeEnum + AddLineItem, + AddItemRequest, + BuyNowItemRequest, + FulfillmentType, + ModifierType, + CurrencyType, + AddItemModifier, + CartFulfillment, + ScheduleType, + AttachLoyaltyRewardRequest, + RemoveLoyaltyRewardRequest, DiscountTypeEnum, DiscountScopeTypeEnum } from '../src/types/api/cart'; import { ScheduleResource } from '../src/types/api/cart/private.types'; import { getTestSiteThemeSDK } from './helpers'; @@ -27,56 +27,56 @@ const SUBSCRIPTION_ID = 'subscription'; const CART_API_URL = '/s/api/v1/cart'; function createFetchResponse(data: LooseObject, ok: boolean, status: number, redirectUrl = ''): Response { - return { - ok, - headers: { - append: vi.fn(), - delete: vi.fn(), - get: vi.fn(), - has: vi.fn(), - set: vi.fn(), - forEach: vi.fn() - }, - redirected: !!redirectUrl, - status: status, - statusText: STATUS_TEXT, - type: 'default', - url: redirectUrl, - clone: vi.fn(), - body: null, - bodyUsed: false, - arrayBuffer: vi.fn(), - blob: vi.fn(), - formData: vi.fn(), - text: vi.fn(), - json: () => new Promise((resolve) => resolve(data)) - }; + return { + ok, + headers: { + append: vi.fn(), + delete: vi.fn(), + get: vi.fn(), + has: vi.fn(), + set: vi.fn(), + forEach: vi.fn() + }, + redirected: !!redirectUrl, + status: status, + statusText: STATUS_TEXT, + type: 'default', + url: redirectUrl, + clone: vi.fn(), + body: null, + bodyUsed: false, + arrayBuffer: vi.fn(), + blob: vi.fn(), + formData: vi.fn(), + text: vi.fn(), + json: () => new Promise((resolve) => resolve(data)) + }; } function createHeadersAndMethodForRequest(method: string = 'POST'): LooseObject { - return { - headers: { - 'Accept': 'application/json', - 'content-type' : 'application/json; charset=UTF-8', - 'X-CSRF-TOKEN': CSRF_TOKEN - }, - method: method, - }; + return { + headers: { + 'Accept': 'application/json', + 'content-type' : 'application/json; charset=UTF-8', + 'X-CSRF-TOKEN': CSRF_TOKEN + }, + method: method, + }; } function createAttachLoyaltyRewardRequest( - { - loyaltyAccountId = 'loyaltyAccountId', - loyaltyRewardTierId = 'loyaltyRewardTierId', - loyaltyRewardName = 'Loyalty Reward Name', - discountType = 'FIXED_AMOUNT', - discountScope = 'LINE_ITEM', - discountValue = 100, - maxDiscount = undefined, - itemIds = undefined, - // categoryIds = undefined, // TODO: uncomment once category-based rewards are supported - orderId = undefined - }: { + { + loyaltyAccountId = 'loyaltyAccountId', + loyaltyRewardTierId = 'loyaltyRewardTierId', + loyaltyRewardName = 'Loyalty Reward Name', + discountType = 'FIXED_AMOUNT', + discountScope = 'LINE_ITEM', + discountValue = 100, + maxDiscount = undefined, + itemIds = undefined, + // categoryIds = undefined, // TODO: uncomment once category-based rewards are supported + orderId = undefined + }: { loyaltyAccountId?: string; loyaltyRewardTierId?: string; loyaltyRewardName?: string; @@ -89,33 +89,33 @@ function createAttachLoyaltyRewardRequest( orderId?: string; } ): AttachLoyaltyRewardRequest { - return { - loyaltyAccountId, - loyaltyRewardTierId, - loyaltyRewardName, - discountType, - discountScope, - discountValue, - maxDiscount, - itemIds, - // categoryIds, // TODO: uncomment once category-based rewards are supported - orderId, - }; + return { + loyaltyAccountId, + loyaltyRewardTierId, + loyaltyRewardName, + discountType, + discountScope, + discountValue, + maxDiscount, + itemIds, + // categoryIds, // TODO: uncomment once category-based rewards are supported + orderId, + }; } function createAttachLoyaltyRewardFetchRequest( - { - loyaltyAccountId = 'loyaltyAccountId', - loyaltyRewardTierId = 'loyaltyRewardTierId', - loyaltyRewardName = 'Loyalty Reward Name', - discountType = 'FIXED_AMOUNT', - discountScope = 'LINE_ITEM', - discountValue = 100, - maxDiscount = undefined, - itemIds = undefined, - // categoryIds = undefined, // TODO: uncomment once category-based rewards are supported - orderId = 'orderId' - }: { + { + loyaltyAccountId = 'loyaltyAccountId', + loyaltyRewardTierId = 'loyaltyRewardTierId', + loyaltyRewardName = 'Loyalty Reward Name', + discountType = 'FIXED_AMOUNT', + discountScope = 'LINE_ITEM', + discountValue = 100, + maxDiscount = undefined, + itemIds = undefined, + // categoryIds = undefined, // TODO: uncomment once category-based rewards are supported + orderId = 'orderId' + }: { loyaltyAccountId?: string; loyaltyRewardTierId?: string; loyaltyRewardName?: string; @@ -128,62 +128,62 @@ function createAttachLoyaltyRewardFetchRequest( orderId?: string; } ) { - return { - loyalty_account_id: loyaltyAccountId, - loyalty_reward_tier_id: loyaltyRewardTierId, - loyalty_reward_name: loyaltyRewardName, - discount_type: discountType, - discount_scope: discountScope, - discount_value: discountValue, - max_discount: maxDiscount, - item_ids: itemIds, - // category_ids: categoryIds, // TODO: uncomment once category-based rewards are supported - order_id: orderId, - }; + return { + loyalty_account_id: loyaltyAccountId, + loyalty_reward_tier_id: loyaltyRewardTierId, + loyalty_reward_name: loyaltyRewardName, + discount_type: discountType, + discount_scope: discountScope, + discount_value: discountValue, + max_discount: maxDiscount, + item_ids: itemIds, + // category_ids: categoryIds, // TODO: uncomment once category-based rewards are supported + order_id: orderId, + }; } function createRemoveLoyaltyRewardRequest( - { - rewardId = 'rewardId', - orderId = undefined - }: { + { + rewardId = 'rewardId', + orderId = undefined + }: { rewardId?: string; orderId?: string; } ): RemoveLoyaltyRewardRequest { - return { - rewardId, - orderId, - }; + return { + rewardId, + orderId, + }; } function createRemoveLoyaltyRewardFetchRequest( - { - rewardId = 'rewardId', - orderId = undefined - }: { + { + rewardId = 'rewardId', + orderId = undefined + }: { rewardId?: string; orderId?: string; } ) { - return { - reward_id: rewardId, - order_id: orderId, - }; + return { + reward_id: rewardId, + order_id: orderId, + }; } function createAddOrBuyNowItemRequest( - { - orderId = undefined, - includePriceOverride = false, - subscriptionId = undefined, - isBuyNow = false, - isPickup = false, - isDelivery = false, - excludePickupDetails = false, - useNestedArray = false, - emptyModifiers = false, - }: { + { + orderId = undefined, + includePriceOverride = false, + subscriptionId = undefined, + isBuyNow = false, + isPickup = false, + isDelivery = false, + excludePickupDetails = false, + useNestedArray = false, + emptyModifiers = false, + }: { orderId?: string | undefined; includePriceOverride?: boolean; subscriptionId?: string; @@ -195,148 +195,148 @@ function createAddOrBuyNowItemRequest( emptyModifiers?: boolean; } ): AddItemRequest | BuyNowItemRequest { - const lineItem: AddLineItem = { - itemId: 'itemId', - variationId: 'variationId', - modifiers: [ - { - id: '6WVGAE3PKEHRWZHF54KR2PIN', - type: ModifierType.CHOICE, - choiceSelections: ['E3MWZ3PJ3VZDQWGW4G3KFZGW', 'GKCUYTB7ARN25J7BTRTOSVHO'] - }, - { - id: '11ede91fbff63a3ab4dbde667deefb9b', - type: ModifierType.TEXT, - textEntry: 'my t-shirt text' - }, - { - id: '11ede91fbff63a3ab4dbde667deefb9c', - type: ModifierType.TEXT, - textEntry: 'my t-shirt back text' - }, - ], - }; - - if (!isBuyNow) { - lineItem.modifiers = [ - ...lineItem.modifiers, - ...[ - { - id: '11ee185ca1cd3e98a25c9e3d692ffefb', - type: ModifierType.GIFT_WRAP, - choiceSelections: ['11ee185ca1cd7daebd029e3d692ffefb'] - }, - { - id: '11ee185ca17973e490449e3d692ffefb', - type: ModifierType.GIFT_MESSAGE, - textEntry: 'happy bday' - } - ] - ]; - } else { - // Testing invalid null being passed in - (lineItem).subscriptionPlanVariationId = subscriptionId || null; - } - - if (emptyModifiers) { - lineItem.modifiers = []; - } - - // Just to test convertCamelObjToSnakeObjInArray - if (useNestedArray) { - (lineItem).modifiers = [ - ...lineItem.modifiers, - ...[ - { - id: 'nestedArrayTest', - type: 'nestedArrayTest', - choiceSelections: [ - [ - 'a', - 'b', - 'c' - ], - [ - 'd', - 'e', - 'f' - ] - ] - } - ] - ]; - } - - if (includePriceOverride) { - lineItem.priceOverride = { - amount: 718, - currency: CurrencyType.USD - }; - } - - let pickupFulfillment: CartFulfillment | null = null; - if (isPickup) { - pickupFulfillment = { - fulfillmentType: FulfillmentType.PICKUP, - pickupDetails: { - scheduleType: ScheduleType.ASAP, - curbsidePickupRequested: true, - curbsidePickupDetails: { - curbsideDetails: 'I want it at the curb' - }, - pickupAt: '2023-08-28T20:45:14.683Z' - } - }; - if (excludePickupDetails) { - delete pickupFulfillment.pickupDetails; - } - } - - let deliveryFulfillment: CartFulfillment | null = null; - if (isDelivery) { - deliveryFulfillment = { - fulfillmentType: FulfillmentType.DELIVERY, - deliveryDetails: { - recipient: { - address: { - locality: 'The Bronx', - country: 'US', - postalCode: '10461', - administrativeDistrictLevel1: 'NY', - addressLine1: '1200 Hone Avenue', - } - }, - } - }; - } - - const request = { - lineItem, - fulfillment: pickupFulfillment || deliveryFulfillment || { - fulfillmentType: FulfillmentType.SHIPMENT - }, - locationId: 'location' - }; - - if (!isBuyNow) { - (request).orderId = orderId; - } - - return request; + const lineItem: AddLineItem = { + itemId: 'itemId', + variationId: 'variationId', + modifiers: [ + { + id: '6WVGAE3PKEHRWZHF54KR2PIN', + type: ModifierType.CHOICE, + choiceSelections: ['E3MWZ3PJ3VZDQWGW4G3KFZGW', 'GKCUYTB7ARN25J7BTRTOSVHO'] + }, + { + id: '11ede91fbff63a3ab4dbde667deefb9b', + type: ModifierType.TEXT, + textEntry: 'my t-shirt text' + }, + { + id: '11ede91fbff63a3ab4dbde667deefb9c', + type: ModifierType.TEXT, + textEntry: 'my t-shirt back text' + }, + ], + }; + + if (!isBuyNow) { + lineItem.modifiers = [ + ...lineItem.modifiers, + ...[ + { + id: '11ee185ca1cd3e98a25c9e3d692ffefb', + type: ModifierType.GIFT_WRAP, + choiceSelections: ['11ee185ca1cd7daebd029e3d692ffefb'] + }, + { + id: '11ee185ca17973e490449e3d692ffefb', + type: ModifierType.GIFT_MESSAGE, + textEntry: 'happy bday' + } + ] + ]; + } else { + // Testing invalid null being passed in + (lineItem).subscriptionPlanVariationId = subscriptionId || null; + } + + if (emptyModifiers) { + lineItem.modifiers = []; + } + + // Just to test convertCamelObjToSnakeObjInArray + if (useNestedArray) { + (lineItem).modifiers = [ + ...lineItem.modifiers, + ...[ + { + id: 'nestedArrayTest', + type: 'nestedArrayTest', + choiceSelections: [ + [ + 'a', + 'b', + 'c' + ], + [ + 'd', + 'e', + 'f' + ] + ] + } + ] + ]; + } + + if (includePriceOverride) { + lineItem.priceOverride = { + amount: 718, + currency: CurrencyType.USD + }; + } + + let pickupFulfillment: CartFulfillment | null = null; + if (isPickup) { + pickupFulfillment = { + fulfillmentType: FulfillmentType.PICKUP, + pickupDetails: { + scheduleType: ScheduleType.ASAP, + curbsidePickupRequested: true, + curbsidePickupDetails: { + curbsideDetails: 'I want it at the curb' + }, + pickupAt: '2023-08-28T20:45:14.683Z' + } + }; + if (excludePickupDetails) { + delete pickupFulfillment.pickupDetails; + } + } + + let deliveryFulfillment: CartFulfillment | null = null; + if (isDelivery) { + deliveryFulfillment = { + fulfillmentType: FulfillmentType.DELIVERY, + deliveryDetails: { + recipient: { + address: { + locality: 'The Bronx', + country: 'US', + postalCode: '10461', + administrativeDistrictLevel1: 'NY', + addressLine1: '1200 Hone Avenue', + } + }, + } + }; + } + + const request = { + lineItem, + fulfillment: pickupFulfillment || deliveryFulfillment || { + fulfillmentType: FulfillmentType.SHIPMENT + }, + locationId: 'location' + }; + + if (!isBuyNow) { + (request).orderId = orderId; + } + + return request; } function createAddOrBuyNowItemFetchRequest( - { - orderId = undefined, - includePriceOverride = false, - subscriptionId = undefined, - isBuyNow = false, - isPickup = false, - isDelivery = false, - excludePickupDetails = false, - useNestedArray = false, - emptyModifiers = false - }: { + { + orderId = undefined, + includePriceOverride = false, + subscriptionId = undefined, + isBuyNow = false, + isPickup = false, + isDelivery = false, + excludePickupDetails = false, + useNestedArray = false, + emptyModifiers = false + }: { orderId?: string | undefined; includePriceOverride?: boolean; subscriptionId?: string; @@ -348,161 +348,161 @@ function createAddOrBuyNowItemFetchRequest( emptyModifiers?: boolean; } ): any { - const lineItem: any = { - item_id: 'itemId', - variation_id: 'variationId', - modifiers: { - [ModifierType.CHOICE]: { - ['6WVGAE3PKEHRWZHF54KR2PIN']: { - choice_selections: ['E3MWZ3PJ3VZDQWGW4G3KFZGW', 'GKCUYTB7ARN25J7BTRTOSVHO'] - } - }, - [ModifierType.TEXT]: { - ['11ede91fbff63a3ab4dbde667deefb9b']: { - text_entry: 'my t-shirt text' - }, - ['11ede91fbff63a3ab4dbde667deefb9c']: { - text_entry: 'my t-shirt back text' - } - } - } - }; - - if (!isBuyNow) { - lineItem.modifiers = { - ...lineItem.modifiers, - ...{ - [ModifierType.GIFT_WRAP]: { - ['11ee185ca1cd3e98a25c9e3d692ffefb']: { - choice_selections: ['11ee185ca1cd7daebd029e3d692ffefb'] - } - }, - [ModifierType.GIFT_MESSAGE]: { - ['11ee185ca17973e490449e3d692ffefb']: { - text_entry: 'happy bday' - } - } - } - }; - } else { - // Testing invalid null being passed in - lineItem.subscription_plan_variation_id = subscriptionId || null; - } - - if (emptyModifiers) { - delete lineItem.modifiers; - } - - // Just to test convertCamelObjToSnakeObjInArray - if (useNestedArray) { - lineItem.modifiers = { - ...lineItem.modifiers, - ...{ - ['nestedArrayTest']: { - ['nestedArrayTest']: { - choice_selections: [ - [ - 'a', - 'b', - 'c' - ], - [ - 'd', - 'e', - 'f' - ] - ] - } - } - } - }; - } - - if (includePriceOverride) { - lineItem.price_override = { - amount: 718, - currency: CurrencyType.USD - }; - } - - lineItem.quantity = 1; - - let pickupFulfillment: LooseObject | null = null; - if (isPickup) { - pickupFulfillment = { - fulfillment_type: FulfillmentType.PICKUP, - pickup_details: { - schedule_type: ScheduleType.ASAP, - curbside_pickup_requested: true, - curbside_pickup_details: { - curbside_details: 'I want it at the curb' - }, - pickup_at: '2023-08-28T20:45:14.683Z' - } - }; - if (excludePickupDetails) { - pickupFulfillment.pickup_details = { - schedule_type: ScheduleType.ASAP, - curbside_pickup_requested: false, - curbside_pickup_details: { - curbside_details: '' - }, - }; - } - } - - let deliveryFulfillment: LooseObject | null = null; - if (isDelivery) { - deliveryFulfillment = { - fulfillment_type: FulfillmentType.DELIVERY, - delivery_details: { - recipient: { - address: { - locality: 'The Bronx', - country: 'US', - postal_code: '10461', - administrative_district_level_1: 'NY', - address_line_1: '1200 Hone Avenue', - } - }, - no_contact_delivery: false, - schedule_type: ScheduleType.ASAP, - } - }; - } - - const request: any = { - line_item: lineItem, - fulfillment: pickupFulfillment || deliveryFulfillment || { - fulfillment_type: FulfillmentType.SHIPMENT - }, - location_id: 'location' - }; - - if (!isBuyNow) { - request.order_id = orderId; - } - - return request; + const lineItem: any = { + item_id: 'itemId', + variation_id: 'variationId', + modifiers: { + [ModifierType.CHOICE]: { + ['6WVGAE3PKEHRWZHF54KR2PIN']: { + choice_selections: ['E3MWZ3PJ3VZDQWGW4G3KFZGW', 'GKCUYTB7ARN25J7BTRTOSVHO'] + } + }, + [ModifierType.TEXT]: { + ['11ede91fbff63a3ab4dbde667deefb9b']: { + text_entry: 'my t-shirt text' + }, + ['11ede91fbff63a3ab4dbde667deefb9c']: { + text_entry: 'my t-shirt back text' + } + } + } + }; + + if (!isBuyNow) { + lineItem.modifiers = { + ...lineItem.modifiers, + ...{ + [ModifierType.GIFT_WRAP]: { + ['11ee185ca1cd3e98a25c9e3d692ffefb']: { + choice_selections: ['11ee185ca1cd7daebd029e3d692ffefb'] + } + }, + [ModifierType.GIFT_MESSAGE]: { + ['11ee185ca17973e490449e3d692ffefb']: { + text_entry: 'happy bday' + } + } + } + }; + } else { + // Testing invalid null being passed in + lineItem.subscription_plan_variation_id = subscriptionId || null; + } + + if (emptyModifiers) { + delete lineItem.modifiers; + } + + // Just to test convertCamelObjToSnakeObjInArray + if (useNestedArray) { + lineItem.modifiers = { + ...lineItem.modifiers, + ...{ + ['nestedArrayTest']: { + ['nestedArrayTest']: { + choice_selections: [ + [ + 'a', + 'b', + 'c' + ], + [ + 'd', + 'e', + 'f' + ] + ] + } + } + } + }; + } + + if (includePriceOverride) { + lineItem.price_override = { + amount: 718, + currency: CurrencyType.USD + }; + } + + lineItem.quantity = 1; + + let pickupFulfillment: LooseObject | null = null; + if (isPickup) { + pickupFulfillment = { + fulfillment_type: FulfillmentType.PICKUP, + pickup_details: { + schedule_type: ScheduleType.ASAP, + curbside_pickup_requested: true, + curbside_pickup_details: { + curbside_details: 'I want it at the curb' + }, + pickup_at: '2023-08-28T20:45:14.683Z' + } + }; + if (excludePickupDetails) { + pickupFulfillment.pickup_details = { + schedule_type: ScheduleType.ASAP, + curbside_pickup_requested: false, + curbside_pickup_details: { + curbside_details: '' + }, + }; + } + } + + let deliveryFulfillment: LooseObject | null = null; + if (isDelivery) { + deliveryFulfillment = { + fulfillment_type: FulfillmentType.DELIVERY, + delivery_details: { + recipient: { + address: { + locality: 'The Bronx', + country: 'US', + postal_code: '10461', + administrative_district_level_1: 'NY', + address_line_1: '1200 Hone Avenue', + } + }, + no_contact_delivery: false, + schedule_type: ScheduleType.ASAP, + } + }; + } + + const request: any = { + line_item: lineItem, + fulfillment: pickupFulfillment || deliveryFulfillment || { + fulfillment_type: FulfillmentType.SHIPMENT + }, + location_id: 'location' + }; + + if (!isBuyNow) { + request.order_id = orderId; + } + + return request; } const scheduleResource: ScheduleResource = { - schedule: { - earliest_time: { - time_unix: 1893474000, - } - } + schedule: { + earliest_time: { + time_unix: 1893474000, + } + } }; const okCartResponse = { - data: { - cart: 'xyz' - } + data: { + cart: 'xyz' + } }; const okFetchResponse = createFetchResponse(okCartResponse, true, 200); const okFetchResult = { - response: okFetchResponse, - ...okCartResponse + response: okFetchResponse, + ...okCartResponse }; const scheduleFetchResponse = createFetchResponse(scheduleResource, true, 200); @@ -512,929 +512,929 @@ const mockWindowHrefSet = vi.fn(); const sdk = getTestSiteThemeSDK(); beforeEach(() => { - const documentMock = { - querySelector: () => { - return { - content: CSRF_TOKEN, - }; - }, - cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` - }; - - const windowMock = { - location: { - get href() { return CURRENT_URL; }, - set href(url) { - mockWindowHrefSet(url); - } - } - }; - - vi.stubGlobal('document', documentMock); - vi.stubGlobal('window', windowMock); - - global.fetch = vi.fn(); - vi.mocked(fetch).mockResolvedValue(okFetchResponse); + const documentMock = { + querySelector: () => { + return { + content: CSRF_TOKEN, + }; + }, + cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` + }; + + const windowMock = { + location: { + get href() { return CURRENT_URL; }, + set href(url) { + mockWindowHrefSet(url); + } + } + }; + + vi.stubGlobal('document', documentMock); + vi.stubGlobal('window', windowMock); + + global.fetch = vi.fn(); + vi.mocked(fetch).mockResolvedValue(okFetchResponse); }); describe('Get active id', () => { - it('should return the active cart id when it exists', () => { - const result = sdk.cart.getActiveId(); - expect(result).toStrictEqual(CART_TOKEN); - }); - - it('should return undefined when no active id exists', () => { - const documentMock = { - cookie: `test_cookie=test; not_com_cart_id=${CART_TOKEN};` - }; - vi.stubGlobal('document', documentMock); - const result = sdk.cart.getActiveId(); - expect(result).toBeUndefined(); - }); + it('should return the active cart id when it exists', () => { + const result = sdk.cart.getActiveId(); + expect(result).toStrictEqual(CART_TOKEN); + }); + + it('should return undefined when no active id exists', () => { + const documentMock = { + cookie: `test_cookie=test; not_com_cart_id=${CART_TOKEN};` + }; + vi.stubGlobal('document', documentMock); + const result = sdk.cart.getActiveId(); + expect(result).toBeUndefined(); + }); }); describe('Apply loyalty reward', () => { - it('should apply loyalty reward', async () => { - const actual = await sdk.cart.attachLoyaltyReward(createAttachLoyaltyRewardRequest({})); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/loyalty/reward`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest('POST'), - body: JSON.stringify(createAttachLoyaltyRewardFetchRequest({ orderId: CART_TOKEN })) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - expect(actual).toStrictEqual(okFetchResult); - }); - - it('should throw error', async () => { - const fetchResponse = createFetchResponse({}, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const attachLoyaltyRewardFn = sdk.cart.attachLoyaltyReward(createAttachLoyaltyRewardRequest({})); - - await expect(attachLoyaltyRewardFn).rejects.toThrowError(STATUS_TEXT); - await expect(attachLoyaltyRewardFn).rejects.toMatchObject({ - status: 500 - }); - }); + it('should apply loyalty reward', async () => { + const actual = await sdk.cart.attachLoyaltyReward(createAttachLoyaltyRewardRequest({})); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/loyalty/reward`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest('POST'), + body: JSON.stringify(createAttachLoyaltyRewardFetchRequest({ orderId: CART_TOKEN })) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + expect(actual).toStrictEqual(okFetchResult); + }); + + it('should throw error', async () => { + const fetchResponse = createFetchResponse({}, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const attachLoyaltyRewardFn = sdk.cart.attachLoyaltyReward(createAttachLoyaltyRewardRequest({})); + + await expect(attachLoyaltyRewardFn).rejects.toThrowError(STATUS_TEXT); + await expect(attachLoyaltyRewardFn).rejects.toMatchObject({ + status: 500 + }); + }); }); describe('Remove loyalty reward', () => { - it('should remove loyalty reward', async () => { - const result = await sdk.cart.removeLoyaltyReward(createRemoveLoyaltyRewardRequest({})); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/loyalty/reward`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest('DELETE'), - body: JSON.stringify(createRemoveLoyaltyRewardFetchRequest({ orderId: CART_TOKEN })) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - expect(result).toStrictEqual(okFetchResult); - }); - - it('should throw error', async () => { - const fetchResponse = createFetchResponse({}, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const removeLoyaltyRewardFn = sdk.cart.removeLoyaltyReward(createRemoveLoyaltyRewardRequest({})); - - await expect(removeLoyaltyRewardFn).rejects.toThrowError(STATUS_TEXT); - await expect(removeLoyaltyRewardFn).rejects.toMatchObject({ - status: 500 - }); - }); + it('should remove loyalty reward', async () => { + const result = await sdk.cart.removeLoyaltyReward(createRemoveLoyaltyRewardRequest({})); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/loyalty/reward`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest('DELETE'), + body: JSON.stringify(createRemoveLoyaltyRewardFetchRequest({ orderId: CART_TOKEN })) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual(okFetchResult); + }); + + it('should throw error', async () => { + const fetchResponse = createFetchResponse({}, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const removeLoyaltyRewardFn = sdk.cart.removeLoyaltyReward(createRemoveLoyaltyRewardRequest({})); + + await expect(removeLoyaltyRewardFn).rejects.toThrowError(STATUS_TEXT); + await expect(removeLoyaltyRewardFn).rejects.toMatchObject({ + status: 500 + }); + }); }); describe('Add item', () => { - it('should make valid fetch', async () => { - const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({})); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/add`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN })) - }) - ); - - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should make valid pickup fetch', async () => { - vi.mocked(fetch) - .mockResolvedValueOnce(okFetchResponse) - .mockResolvedValueOnce(scheduleFetchResponse) - .mockResolvedValueOnce(okFetchResponse); - - const request = createAddOrBuyNowItemRequest({ isPickup: true }); - const result = await sdk.cart.addItem(request); - // Verify request isn't modified by SDK - expect(request).toStrictEqual(createAddOrBuyNowItemRequest({ isPickup: true })); - const fetchRequest = createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, isPickup: true }); - expect(fetch).toHaveBeenNthCalledWith(1, - `${CART_API_URL}/add`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(fetchRequest) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should make valid pickup fetch with defaults', async () => { - vi.mocked(fetch) - .mockResolvedValueOnce(okFetchResponse) - .mockResolvedValueOnce(scheduleFetchResponse) - .mockResolvedValueOnce(okFetchResponse); - - const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ isPickup: true, excludePickupDetails: true })); - - const fetchRequest = createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, isPickup: true, excludePickupDetails: true }); - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/add`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(fetchRequest) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should make valid delivery fetch with defaults', async () => { - vi.mocked(fetch) - .mockResolvedValueOnce(okFetchResponse) - .mockResolvedValueOnce(scheduleFetchResponse) - .mockResolvedValueOnce(okFetchResponse); - - const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ isDelivery: true})); - - const fetchRequest = createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, isDelivery: true }); - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/add`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(fetchRequest) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should make valid fetch with no cart cookie', async () => { - const DocumentMock = { - querySelector: () => { - return { - content: CSRF_TOKEN, - }; - }, - cookie: 'test_cookie=test;' - }; - vi.stubGlobal('document', DocumentMock); - - const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({})); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/add`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(createAddOrBuyNowItemFetchRequest({})) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should make valid fetch and reset cart', async () => { - const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ orderId: '' })); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/add`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ orderId: '' })) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should make valid fetch with empty modifiers', async () => { - const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ emptyModifiers: true })); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/add`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, emptyModifiers: true })) - }) - ); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should throw error', async () => { - const fetchResponse = createFetchResponse({ - 'cart_errors': [ - { - translation: 'error message', - } - ] - }, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const addItemFn = sdk.cart.addItem({ - lineItem: { - itemId: 'itemId', - variationId: 'variationId' - }, - fulfillment: { - fulfillmentType: FulfillmentType.SHIPMENT - }, - locationId: 'location' - }); - - expect(fetch).toHaveBeenCalledTimes(1); - - await expect(addItemFn).rejects.toThrowError(STATUS_TEXT); - await expect(addItemFn).rejects.toMatchObject({ - status: 500, - cart_errors: [ - { - translation: 'error message', - } - ] - }); - }); - - it('should throw internal error', async () => { - const addItemResponse = { - error: 'error message' - }; - const fetchResponse = createFetchResponse(addItemResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const addItemFn = sdk.cart.addItem({ - lineItem: { - itemId: 'itemId', - variationId: 'variationId' - }, - fulfillment: { - fulfillmentType: FulfillmentType.SHIPMENT - }, - locationId: 'location' - }); - - expect(fetch).toHaveBeenCalledTimes(1); - - await expect(addItemFn).rejects.toThrowError(addItemResponse.error); - await expect(addItemFn).rejects.toMatchObject({ - status: 500 - }); - }); - - it('should throw invalid IDs error', async () => { - const errorFields = { - item: ['1', '2', '3'] - }; - const addItemResponse = { - error: 'Invalid IDs passed in payload', - fields: errorFields - }; - const fetchResponse = createFetchResponse(addItemResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const addItemFn = sdk.cart.addItem({ - lineItem: { - itemId: 'itemId', - variationId: 'variationId' - }, - fulfillment: { - fulfillmentType: FulfillmentType.SHIPMENT - }, - locationId: 'location' - }); - - expect(fetch).toHaveBeenCalledTimes(1); - - await expect(addItemFn).rejects.toThrowError(addItemResponse.error); - await expect(addItemFn).rejects.toMatchObject({ - status: 500, - fields: errorFields - }); - }); - - it('should throw validation error', async () => { - const addItemResponse = { - message: 'fulfillment_test.method_test is invalid', - errors: { - 'fulfillment_test.method_test': [ - 'fulfillment_test.method_test is invalid' - ] - } - }; - const fetchResponse = createFetchResponse(addItemResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const addItemFn = sdk.cart.addItem({ - lineItem: { - itemId: 'itemId', - variationId: 'variationId' - }, - fulfillment: { - fulfillmentType: FulfillmentType.SHIPMENT - }, - locationId: 'location' - }); - - expect(fetch).toHaveBeenCalledTimes(1); - - await expect(addItemFn).rejects.toThrowError('fulfillmentTest.methodTest is invalid'); - await expect(addItemFn).rejects.toMatchObject({ - status: 500, - errors: { - 'fulfillmentTest.methodTest': [ - 'fulfillmentTest.methodTest is invalid' - ] - } - }); - }); - - it('should test convertCamelObjToSnakeObjInArray', async () => { - const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ useNestedArray: true })); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/add`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, useNestedArray: true })) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(result).toStrictEqual(okFetchResult); - }); + it('should make valid fetch', async () => { + const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({})); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/add`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN })) + }) + ); + + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should make valid pickup fetch', async () => { + vi.mocked(fetch) + .mockResolvedValueOnce(okFetchResponse) + .mockResolvedValueOnce(scheduleFetchResponse) + .mockResolvedValueOnce(okFetchResponse); + + const request = createAddOrBuyNowItemRequest({ isPickup: true }); + const result = await sdk.cart.addItem(request); + // Verify request isn't modified by SDK + expect(request).toStrictEqual(createAddOrBuyNowItemRequest({ isPickup: true })); + const fetchRequest = createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, isPickup: true }); + expect(fetch).toHaveBeenNthCalledWith(1, + `${CART_API_URL}/add`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(fetchRequest) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should make valid pickup fetch with defaults', async () => { + vi.mocked(fetch) + .mockResolvedValueOnce(okFetchResponse) + .mockResolvedValueOnce(scheduleFetchResponse) + .mockResolvedValueOnce(okFetchResponse); + + const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ isPickup: true, excludePickupDetails: true })); + + const fetchRequest = createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, isPickup: true, excludePickupDetails: true }); + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/add`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(fetchRequest) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should make valid delivery fetch with defaults', async () => { + vi.mocked(fetch) + .mockResolvedValueOnce(okFetchResponse) + .mockResolvedValueOnce(scheduleFetchResponse) + .mockResolvedValueOnce(okFetchResponse); + + const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ isDelivery: true})); + + const fetchRequest = createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, isDelivery: true }); + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/add`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(fetchRequest) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should make valid fetch with no cart cookie', async () => { + const DocumentMock = { + querySelector: () => { + return { + content: CSRF_TOKEN, + }; + }, + cookie: 'test_cookie=test;' + }; + vi.stubGlobal('document', DocumentMock); + + const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({})); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/add`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(createAddOrBuyNowItemFetchRequest({})) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should make valid fetch and reset cart', async () => { + const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ orderId: '' })); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/add`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ orderId: '' })) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should make valid fetch with empty modifiers', async () => { + const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ emptyModifiers: true })); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/add`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, emptyModifiers: true })) + }) + ); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should throw error', async () => { + const fetchResponse = createFetchResponse({ + 'cart_errors': [ + { + translation: 'error message', + } + ] + }, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const addItemFn = sdk.cart.addItem({ + lineItem: { + itemId: 'itemId', + variationId: 'variationId' + }, + fulfillment: { + fulfillmentType: FulfillmentType.SHIPMENT + }, + locationId: 'location' + }); + + expect(fetch).toHaveBeenCalledTimes(1); + + await expect(addItemFn).rejects.toThrowError(STATUS_TEXT); + await expect(addItemFn).rejects.toMatchObject({ + status: 500, + cart_errors: [ + { + translation: 'error message', + } + ] + }); + }); + + it('should throw internal error', async () => { + const addItemResponse = { + error: 'error message' + }; + const fetchResponse = createFetchResponse(addItemResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const addItemFn = sdk.cart.addItem({ + lineItem: { + itemId: 'itemId', + variationId: 'variationId' + }, + fulfillment: { + fulfillmentType: FulfillmentType.SHIPMENT + }, + locationId: 'location' + }); + + expect(fetch).toHaveBeenCalledTimes(1); + + await expect(addItemFn).rejects.toThrowError(addItemResponse.error); + await expect(addItemFn).rejects.toMatchObject({ + status: 500 + }); + }); + + it('should throw invalid IDs error', async () => { + const errorFields = { + item: ['1', '2', '3'] + }; + const addItemResponse = { + error: 'Invalid IDs passed in payload', + fields: errorFields + }; + const fetchResponse = createFetchResponse(addItemResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const addItemFn = sdk.cart.addItem({ + lineItem: { + itemId: 'itemId', + variationId: 'variationId' + }, + fulfillment: { + fulfillmentType: FulfillmentType.SHIPMENT + }, + locationId: 'location' + }); + + expect(fetch).toHaveBeenCalledTimes(1); + + await expect(addItemFn).rejects.toThrowError(addItemResponse.error); + await expect(addItemFn).rejects.toMatchObject({ + status: 500, + fields: errorFields + }); + }); + + it('should throw validation error', async () => { + const addItemResponse = { + message: 'fulfillment_test.method_test is invalid', + errors: { + 'fulfillment_test.method_test': [ + 'fulfillment_test.method_test is invalid' + ] + } + }; + const fetchResponse = createFetchResponse(addItemResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const addItemFn = sdk.cart.addItem({ + lineItem: { + itemId: 'itemId', + variationId: 'variationId' + }, + fulfillment: { + fulfillmentType: FulfillmentType.SHIPMENT + }, + locationId: 'location' + }); + + expect(fetch).toHaveBeenCalledTimes(1); + + await expect(addItemFn).rejects.toThrowError('fulfillmentTest.methodTest is invalid'); + await expect(addItemFn).rejects.toMatchObject({ + status: 500, + errors: { + 'fulfillmentTest.methodTest': [ + 'fulfillmentTest.methodTest is invalid' + ] + } + }); + }); + + it('should test convertCamelObjToSnakeObjInArray', async () => { + const result = await sdk.cart.addItem(createAddOrBuyNowItemRequest({ useNestedArray: true })); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/add`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ orderId: CART_TOKEN, useNestedArray: true })) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(result).toStrictEqual(okFetchResult); + }); }); describe('Buy now item', () => { - it('should make valid fetch and redirect', async () => { - const okFetchResponse = createFetchResponse(okCartResponse, true, 200, REDIRECT_URL); - vi.mocked(fetch).mockResolvedValue(okFetchResponse); - - await sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/buy`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ isBuyNow: true })) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(mockWindowHrefSet).toBeCalledWith(REDIRECT_URL); - }); + it('should make valid fetch and redirect', async () => { + const okFetchResponse = createFetchResponse(okCartResponse, true, 200, REDIRECT_URL); + vi.mocked(fetch).mockResolvedValue(okFetchResponse); + + await sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/buy`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ isBuyNow: true })) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(mockWindowHrefSet).toBeCalledWith(REDIRECT_URL); + }); - it('should make valid pickup fetch and redirect', async () => { - const okFetchResponse = createFetchResponse(okCartResponse, true, 200, REDIRECT_URL); - - vi.mocked(fetch) - .mockResolvedValueOnce(okFetchResponse) - .mockResolvedValueOnce(scheduleFetchResponse) - .mockResolvedValueOnce(okFetchResponse); - - const request = createAddOrBuyNowItemRequest({ isBuyNow: true, isPickup: true }); - await sdk.cart.buyNowItem(request); - // Verify request isn't modified by SDK - expect(request).toStrictEqual(createAddOrBuyNowItemRequest({ isBuyNow: true, isPickup: true })); - - const fetchRequest = createAddOrBuyNowItemFetchRequest({ isBuyNow: true, isPickup: true }); - expect(fetch).toHaveBeenNthCalledWith(1, - `${CART_API_URL}/buy`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(fetchRequest) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(mockWindowHrefSet).toBeCalledWith(REDIRECT_URL); - }); - - it('should make valid price override fetch and redirect', async () => { - const okFetchResponse = createFetchResponse(okCartResponse, true, 200, REDIRECT_URL); - vi.mocked(fetch).mockResolvedValue(okFetchResponse); - - await sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true, includePriceOverride: true })); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/buy`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ isBuyNow: true, includePriceOverride: true })) - }) - ); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(mockWindowHrefSet).toBeCalledWith(REDIRECT_URL); - }); - - it('should make valid subscription fetch and redirect', async () => { - const okFetchResponse = createFetchResponse(okCartResponse, true, 200, REDIRECT_URL); - vi.mocked(fetch).mockResolvedValue(okFetchResponse); - - await sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true, subscriptionId: SUBSCRIPTION_ID })); - - expect(fetch).toHaveBeenCalledTimes(1); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/buy`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ isBuyNow: true, subscriptionId: SUBSCRIPTION_ID })) - }) - ); - - expect(mockWindowHrefSet).toBeCalledWith(REDIRECT_URL); - }); - - it('should throw redirect error', async () => { - const buyNowError = { - response: { - errors: { - error: 'error message' - } - } - }; - const fetchResponse = createFetchResponse(buyNowError, true, 200, CURRENT_URL); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const addItemFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); - - expect(fetch).toHaveBeenCalledTimes(1); - - await expect(addItemFn).rejects.toThrowError(buyNowError.response.errors.error); - await expect(addItemFn).rejects.toMatchObject({ - status: 500 - }); - }); - - it('should throw generic redirect error', async () => { - const buyNowError = { - response: {} - }; - const fetchResponse = createFetchResponse(buyNowError, true, 200, CURRENT_URL); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const addItemFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); - - expect(fetch).toHaveBeenCalledTimes(1); - - await expect(addItemFn).rejects.toThrowError('Something went wrong'); - }); - - it('should throw invalid IDs redirect error', async () => { - const errorFields = { - item: ['1', '2', '3'] - }; - const buyNowError = { - response: { - errors: { - error: 'Invalid IDs passed in payload', - fields: errorFields - } - } - }; - const fetchResponse = createFetchResponse(buyNowError, true, 200, CURRENT_URL); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const buyNowFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); - - expect(fetch).toHaveBeenCalledTimes(1); - - await expect(buyNowFn).rejects.toThrowError(buyNowError.response.errors.error); - await expect(buyNowFn).rejects.toMatchObject({ - fields: errorFields, - status: 500 - }); - }); - - it('should throw validation error', async () => { - const buyNowResponse = { - message: 'fulfillment_test.method_test is invalid', - errors: { - 'fulfillment_test.method_test': [ - 'fulfillment_test.method_test is invalid' - ] - } - }; - const fetchResponse = createFetchResponse(buyNowResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const buyNowFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); - - expect(fetch).toHaveBeenCalledTimes(1); - - await expect(buyNowFn).rejects.toThrowError('fulfillmentTest.methodTest is invalid'); - await expect(buyNowFn).rejects.toMatchObject({ - status: 500, - errors: { - 'fulfillmentTest.methodTest': [ - 'fulfillmentTest.methodTest is invalid' - ] - } - }); - }); - - it('should throw generic error', async () => { - const fetchResponse = createFetchResponse({}, true, 200); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const buyNowFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); - - expect(fetch).toHaveBeenCalledTimes(1); - - await expect(buyNowFn).rejects.toThrowError('Something went wrong'); - }); + it('should make valid pickup fetch and redirect', async () => { + const okFetchResponse = createFetchResponse(okCartResponse, true, 200, REDIRECT_URL); + + vi.mocked(fetch) + .mockResolvedValueOnce(okFetchResponse) + .mockResolvedValueOnce(scheduleFetchResponse) + .mockResolvedValueOnce(okFetchResponse); + + const request = createAddOrBuyNowItemRequest({ isBuyNow: true, isPickup: true }); + await sdk.cart.buyNowItem(request); + // Verify request isn't modified by SDK + expect(request).toStrictEqual(createAddOrBuyNowItemRequest({ isBuyNow: true, isPickup: true })); + + const fetchRequest = createAddOrBuyNowItemFetchRequest({ isBuyNow: true, isPickup: true }); + expect(fetch).toHaveBeenNthCalledWith(1, + `${CART_API_URL}/buy`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(fetchRequest) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(mockWindowHrefSet).toBeCalledWith(REDIRECT_URL); + }); + + it('should make valid price override fetch and redirect', async () => { + const okFetchResponse = createFetchResponse(okCartResponse, true, 200, REDIRECT_URL); + vi.mocked(fetch).mockResolvedValue(okFetchResponse); + + await sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true, includePriceOverride: true })); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/buy`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ isBuyNow: true, includePriceOverride: true })) + }) + ); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(mockWindowHrefSet).toBeCalledWith(REDIRECT_URL); + }); + + it('should make valid subscription fetch and redirect', async () => { + const okFetchResponse = createFetchResponse(okCartResponse, true, 200, REDIRECT_URL); + vi.mocked(fetch).mockResolvedValue(okFetchResponse); + + await sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true, subscriptionId: SUBSCRIPTION_ID })); + + expect(fetch).toHaveBeenCalledTimes(1); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/buy`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(createAddOrBuyNowItemFetchRequest({ isBuyNow: true, subscriptionId: SUBSCRIPTION_ID })) + }) + ); + + expect(mockWindowHrefSet).toBeCalledWith(REDIRECT_URL); + }); + + it('should throw redirect error', async () => { + const buyNowError = { + response: { + errors: { + error: 'error message' + } + } + }; + const fetchResponse = createFetchResponse(buyNowError, true, 200, CURRENT_URL); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const addItemFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); + + expect(fetch).toHaveBeenCalledTimes(1); + + await expect(addItemFn).rejects.toThrowError(buyNowError.response.errors.error); + await expect(addItemFn).rejects.toMatchObject({ + status: 500 + }); + }); + + it('should throw generic redirect error', async () => { + const buyNowError = { + response: {} + }; + const fetchResponse = createFetchResponse(buyNowError, true, 200, CURRENT_URL); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const addItemFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); + + expect(fetch).toHaveBeenCalledTimes(1); + + await expect(addItemFn).rejects.toThrowError('Something went wrong'); + }); + + it('should throw invalid IDs redirect error', async () => { + const errorFields = { + item: ['1', '2', '3'] + }; + const buyNowError = { + response: { + errors: { + error: 'Invalid IDs passed in payload', + fields: errorFields + } + } + }; + const fetchResponse = createFetchResponse(buyNowError, true, 200, CURRENT_URL); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const buyNowFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); + + expect(fetch).toHaveBeenCalledTimes(1); + + await expect(buyNowFn).rejects.toThrowError(buyNowError.response.errors.error); + await expect(buyNowFn).rejects.toMatchObject({ + fields: errorFields, + status: 500 + }); + }); + + it('should throw validation error', async () => { + const buyNowResponse = { + message: 'fulfillment_test.method_test is invalid', + errors: { + 'fulfillment_test.method_test': [ + 'fulfillment_test.method_test is invalid' + ] + } + }; + const fetchResponse = createFetchResponse(buyNowResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const buyNowFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); + + expect(fetch).toHaveBeenCalledTimes(1); + + await expect(buyNowFn).rejects.toThrowError('fulfillmentTest.methodTest is invalid'); + await expect(buyNowFn).rejects.toMatchObject({ + status: 500, + errors: { + 'fulfillmentTest.methodTest': [ + 'fulfillmentTest.methodTest is invalid' + ] + } + }); + }); + + it('should throw generic error', async () => { + const fetchResponse = createFetchResponse({}, true, 200); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const buyNowFn = sdk.cart.buyNowItem(createAddOrBuyNowItemRequest({ isBuyNow: true })); + + expect(fetch).toHaveBeenCalledTimes(1); + + await expect(buyNowFn).rejects.toThrowError('Something went wrong'); + }); }); describe('Update item quantity', () => { - it('should make valid fetch', async () => { - const request = { - orderItemId: 'id', - quantity: 2 - }; - const result = await sdk.cart.updateItemQuantity(request); - // Verify request isn't modified by SDK - expect(request).toStrictEqual({ - orderItemId: 'id', - quantity: 2 - }); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/update-quantity`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify({ - order_item_id: 'id', - quantity: 2, - order_id: CART_TOKEN - }) - }) - ); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should throw error', async () => { - const fetchResponse = createFetchResponse({}, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - const updateItemQuantityFn = sdk.cart.updateItemQuantity({ - orderItemId: 'id', - quantity: 2 - }); - - await expect(updateItemQuantityFn).rejects.toThrowError(STATUS_TEXT); - await expect(updateItemQuantityFn).rejects.toMatchObject({ - status: 500 - }); - }); - - it('should throw internal error', async () => { - const updateItemQuantityResponse = { - error: 'error message' - }; - const fetchResponse = createFetchResponse(updateItemQuantityResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const updateItemQuantityFn = sdk.cart.updateItemQuantity({ - orderItemId: 'id', - quantity: 2 - }); - - await expect(updateItemQuantityFn).rejects.toThrowError(updateItemQuantityResponse.error); - await expect(updateItemQuantityFn).rejects.toMatchObject({ - status: 500 - }); - }); - - it('should throw validation error', async () => { - const updateItemQuantityResponse = { - message: 'quantity must be numeric', - errors: { - 'quantity': [ - 'quantity must be numeric' - ] - } - }; - const fetchResponse = createFetchResponse(updateItemQuantityResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const updateItemQuantityFn = sdk.cart.updateItemQuantity({ - orderItemId: 'id', - quantity: 2 - }); - - await expect(updateItemQuantityFn).rejects.toThrowError(updateItemQuantityResponse.message); - await expect(updateItemQuantityFn).rejects.toMatchObject({ - status: 500, - errors: updateItemQuantityResponse.errors - }); - }); + it('should make valid fetch', async () => { + const request = { + orderItemId: 'id', + quantity: 2 + }; + const result = await sdk.cart.updateItemQuantity(request); + // Verify request isn't modified by SDK + expect(request).toStrictEqual({ + orderItemId: 'id', + quantity: 2 + }); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/update-quantity`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify({ + order_item_id: 'id', + quantity: 2, + order_id: CART_TOKEN + }) + }) + ); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should throw error', async () => { + const fetchResponse = createFetchResponse({}, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + const updateItemQuantityFn = sdk.cart.updateItemQuantity({ + orderItemId: 'id', + quantity: 2 + }); + + await expect(updateItemQuantityFn).rejects.toThrowError(STATUS_TEXT); + await expect(updateItemQuantityFn).rejects.toMatchObject({ + status: 500 + }); + }); + + it('should throw internal error', async () => { + const updateItemQuantityResponse = { + error: 'error message' + }; + const fetchResponse = createFetchResponse(updateItemQuantityResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const updateItemQuantityFn = sdk.cart.updateItemQuantity({ + orderItemId: 'id', + quantity: 2 + }); + + await expect(updateItemQuantityFn).rejects.toThrowError(updateItemQuantityResponse.error); + await expect(updateItemQuantityFn).rejects.toMatchObject({ + status: 500 + }); + }); + + it('should throw validation error', async () => { + const updateItemQuantityResponse = { + message: 'quantity must be numeric', + errors: { + 'quantity': [ + 'quantity must be numeric' + ] + } + }; + const fetchResponse = createFetchResponse(updateItemQuantityResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const updateItemQuantityFn = sdk.cart.updateItemQuantity({ + orderItemId: 'id', + quantity: 2 + }); + + await expect(updateItemQuantityFn).rejects.toThrowError(updateItemQuantityResponse.message); + await expect(updateItemQuantityFn).rejects.toMatchObject({ + status: 500, + errors: updateItemQuantityResponse.errors + }); + }); }); describe('Remove item', () => { - it('should make valid fetch', async () => { - const request = { - orderItemId: 'id' - }; - const result = await sdk.cart.removeItem(request); - // Verify request isn't modified by SDK - expect(request).toStrictEqual({ - orderItemId: 'id' - }); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/remove-item`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify({ - order_item_id: 'id', - order_id: CART_TOKEN - }) - }) - ); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should throw error', async () => { - const fetchResponse = createFetchResponse({}, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const removeItemFn = sdk.cart.removeItem({ - orderItemId: 'id' - }); - - await expect(removeItemFn).rejects.toThrowError(STATUS_TEXT); - await expect(removeItemFn).rejects.toMatchObject({ - status: 500 - }); - }); - - it('should throw internal error', async () => { - const removeItemResponse = { - error: 'error message' - }; - const fetchResponse = createFetchResponse(removeItemResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const removeItemFn = sdk.cart.removeItem({ - orderItemId: 'id' - }); - - await expect(removeItemFn).rejects.toThrowError(removeItemResponse.error); - await expect(removeItemFn).rejects.toMatchObject({ - status: 500 - }); - }); - - it('should throw validation error', async () => { - const removeItemResponse = { - message: 'order_item_id must exist + more', - errors: { - 'order_item_id': [ - 'order_item_id must be a string', - 'order_item_id must exist' - ], - 'test_error': [ - 'test_error must exist' - ], - 'quantity': [ - 'quantity must be numeric' - ] - } - }; - const fetchResponse = createFetchResponse(removeItemResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const removeItemFn = sdk.cart.removeItem({ - orderItemId: 'id' - }); - - await expect(removeItemFn).rejects.toThrowError('orderItemId must exist + more'); - await expect(removeItemFn).rejects.toMatchObject({ - status: 500, - errors: { - 'orderItemId': [ - 'orderItemId must be a string', - 'orderItemId must exist' - ], - 'testError': [ - 'testError must exist' - ], - 'quantity': [ - 'quantity must be numeric' - ] - } - }); - }); + it('should make valid fetch', async () => { + const request = { + orderItemId: 'id' + }; + const result = await sdk.cart.removeItem(request); + // Verify request isn't modified by SDK + expect(request).toStrictEqual({ + orderItemId: 'id' + }); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/remove-item`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify({ + order_item_id: 'id', + order_id: CART_TOKEN + }) + }) + ); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should throw error', async () => { + const fetchResponse = createFetchResponse({}, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const removeItemFn = sdk.cart.removeItem({ + orderItemId: 'id' + }); + + await expect(removeItemFn).rejects.toThrowError(STATUS_TEXT); + await expect(removeItemFn).rejects.toMatchObject({ + status: 500 + }); + }); + + it('should throw internal error', async () => { + const removeItemResponse = { + error: 'error message' + }; + const fetchResponse = createFetchResponse(removeItemResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const removeItemFn = sdk.cart.removeItem({ + orderItemId: 'id' + }); + + await expect(removeItemFn).rejects.toThrowError(removeItemResponse.error); + await expect(removeItemFn).rejects.toMatchObject({ + status: 500 + }); + }); + + it('should throw validation error', async () => { + const removeItemResponse = { + message: 'order_item_id must exist + more', + errors: { + 'order_item_id': [ + 'order_item_id must be a string', + 'order_item_id must exist' + ], + 'test_error': [ + 'test_error must exist' + ], + 'quantity': [ + 'quantity must be numeric' + ] + } + }; + const fetchResponse = createFetchResponse(removeItemResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const removeItemFn = sdk.cart.removeItem({ + orderItemId: 'id' + }); + + await expect(removeItemFn).rejects.toThrowError('orderItemId must exist + more'); + await expect(removeItemFn).rejects.toMatchObject({ + status: 500, + errors: { + 'orderItemId': [ + 'orderItemId must be a string', + 'orderItemId must exist' + ], + 'testError': [ + 'testError must exist' + ], + 'quantity': [ + 'quantity must be numeric' + ] + } + }); + }); }); describe('Put fulfillment', () => { - it('should make valid fetch', async () => { - const request = { - fulfillment: { - fulfillmentType: FulfillmentType.SHIPMENT - } - }; - const result = await sdk.cart.putFulfillment(request); - // Verify request isn't modified by SDK - expect(request).toStrictEqual({ - fulfillment: { - fulfillmentType: FulfillmentType.SHIPMENT - } - }); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/${CART_TOKEN}/fulfillment`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest('PUT'), - body: JSON.stringify({ - fulfillment: { - fulfillment_type: FulfillmentType.SHIPMENT - }, - }) - }) - ); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should make valid fetch with location id included', async () => { - const request = { - fulfillment: { - fulfillmentType: FulfillmentType.DELIVERY, - deliveryDetails: { - scheduleType: ScheduleType.ASAP, - recipient: { - address: { - addressLine1: '14515 Northeast 17th Street', - addressLine2: 'Apt 111', - locality: 'Bellevue', - postalCode: '98004', - country: 'US', - administrativeDistrictLevel1: 'WA', - }, - }, - noContactDelivery: true, - }, - }, - locationId: 'location' - }; - const result = await sdk.cart.putFulfillment(request); - // Verify request isn't modified by SDK - expect(request).toStrictEqual({ - fulfillment: { - fulfillmentType: FulfillmentType.DELIVERY, - deliveryDetails: { - scheduleType: 'ASAP', - recipient: { - address: { - addressLine1: '14515 Northeast 17th Street', - addressLine2: 'Apt 111', - locality: 'Bellevue', - postalCode: '98004', - country: 'US', - administrativeDistrictLevel1: 'WA', - }, - }, - noContactDelivery: true, - }, - }, - locationId: 'location' - }); - - expect(fetch).toHaveBeenCalledWith( - `${CART_API_URL}/${CART_TOKEN}/fulfillment`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest('PUT'), - body: JSON.stringify({ - fulfillment: { - fulfillment_type: FulfillmentType.DELIVERY, - delivery_details: { - schedule_type: 'ASAP', - recipient: { - address: { - address_line_1: '14515 Northeast 17th Street', - address_line_2: 'Apt 111', - locality: 'Bellevue', - postal_code: '98004', - country: 'US', - administrative_district_level_1: 'WA', - }, - }, - no_contact_delivery: true, - }, - }, - location_id: 'location', - }) - }) - ); - - expect(result).toStrictEqual(okFetchResult); - }); - - it('should throw error', async () => { - const fetchResponse = createFetchResponse({}, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const putFulfillmentFn = sdk.cart.putFulfillment({ - fulfillment: { - fulfillmentType: FulfillmentType.SHIPMENT - } - }); - - await expect(putFulfillmentFn).rejects.toThrowError(STATUS_TEXT); - await expect(putFulfillmentFn).rejects.toMatchObject({ - status: 500 - }); - }); - - it('should throw internal error', async () => { - const putFulfillmentResponse = { - error: 'error message' - }; - const fetchResponse = createFetchResponse(putFulfillmentResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const putFulfillmentFn = sdk.cart.putFulfillment({ - fulfillment: { - fulfillmentType: FulfillmentType.SHIPMENT - } - }); - - await expect(putFulfillmentFn).rejects.toThrowError(putFulfillmentResponse.error); - await expect(putFulfillmentFn).rejects.toMatchObject({ - status: 500 - }); - }); - - it('should throw validation error', async () => { - const putFulfillmentResponse = { - message: 'fulfillment is required (and 1 more error)', - errors: { - 'fulfillment': [ - 'fulfillment is required' - ], - 'fulfillment.fulfillment_type': [ - 'fulfillment.fulfillment type is required' - ] - } - }; - const fetchResponse = createFetchResponse(putFulfillmentResponse, false, 500); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const putFulfillmentFn = sdk.cart.putFulfillment({ - fulfillment: { - fulfillmentType: FulfillmentType.SHIPMENT - } - }); - - await expect(putFulfillmentFn).rejects.toThrowError('fulfillment is required (and 1 more error)'); - await expect(putFulfillmentFn).rejects.toMatchObject({ - status: 500, - errors: { - 'fulfillment': [ - 'fulfillment is required' - ], - 'fulfillment.fulfillmentType': [ - 'fulfillment.fulfillment type is required' - ] - } - }); - }); + it('should make valid fetch', async () => { + const request = { + fulfillment: { + fulfillmentType: FulfillmentType.SHIPMENT + } + }; + const result = await sdk.cart.putFulfillment(request); + // Verify request isn't modified by SDK + expect(request).toStrictEqual({ + fulfillment: { + fulfillmentType: FulfillmentType.SHIPMENT + } + }); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/${CART_TOKEN}/fulfillment`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest('PUT'), + body: JSON.stringify({ + fulfillment: { + fulfillment_type: FulfillmentType.SHIPMENT + }, + }) + }) + ); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should make valid fetch with location id included', async () => { + const request = { + fulfillment: { + fulfillmentType: FulfillmentType.DELIVERY, + deliveryDetails: { + scheduleType: ScheduleType.ASAP, + recipient: { + address: { + addressLine1: '14515 Northeast 17th Street', + addressLine2: 'Apt 111', + locality: 'Bellevue', + postalCode: '98004', + country: 'US', + administrativeDistrictLevel1: 'WA', + }, + }, + noContactDelivery: true, + }, + }, + locationId: 'location' + }; + const result = await sdk.cart.putFulfillment(request); + // Verify request isn't modified by SDK + expect(request).toStrictEqual({ + fulfillment: { + fulfillmentType: FulfillmentType.DELIVERY, + deliveryDetails: { + scheduleType: 'ASAP', + recipient: { + address: { + addressLine1: '14515 Northeast 17th Street', + addressLine2: 'Apt 111', + locality: 'Bellevue', + postalCode: '98004', + country: 'US', + administrativeDistrictLevel1: 'WA', + }, + }, + noContactDelivery: true, + }, + }, + locationId: 'location' + }); + + expect(fetch).toHaveBeenCalledWith( + `${CART_API_URL}/${CART_TOKEN}/fulfillment`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest('PUT'), + body: JSON.stringify({ + fulfillment: { + fulfillment_type: FulfillmentType.DELIVERY, + delivery_details: { + schedule_type: 'ASAP', + recipient: { + address: { + address_line_1: '14515 Northeast 17th Street', + address_line_2: 'Apt 111', + locality: 'Bellevue', + postal_code: '98004', + country: 'US', + administrative_district_level_1: 'WA', + }, + }, + no_contact_delivery: true, + }, + }, + location_id: 'location', + }) + }) + ); + + expect(result).toStrictEqual(okFetchResult); + }); + + it('should throw error', async () => { + const fetchResponse = createFetchResponse({}, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const putFulfillmentFn = sdk.cart.putFulfillment({ + fulfillment: { + fulfillmentType: FulfillmentType.SHIPMENT + } + }); + + await expect(putFulfillmentFn).rejects.toThrowError(STATUS_TEXT); + await expect(putFulfillmentFn).rejects.toMatchObject({ + status: 500 + }); + }); + + it('should throw internal error', async () => { + const putFulfillmentResponse = { + error: 'error message' + }; + const fetchResponse = createFetchResponse(putFulfillmentResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const putFulfillmentFn = sdk.cart.putFulfillment({ + fulfillment: { + fulfillmentType: FulfillmentType.SHIPMENT + } + }); + + await expect(putFulfillmentFn).rejects.toThrowError(putFulfillmentResponse.error); + await expect(putFulfillmentFn).rejects.toMatchObject({ + status: 500 + }); + }); + + it('should throw validation error', async () => { + const putFulfillmentResponse = { + message: 'fulfillment is required (and 1 more error)', + errors: { + 'fulfillment': [ + 'fulfillment is required' + ], + 'fulfillment.fulfillment_type': [ + 'fulfillment.fulfillment type is required' + ] + } + }; + const fetchResponse = createFetchResponse(putFulfillmentResponse, false, 500); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const putFulfillmentFn = sdk.cart.putFulfillment({ + fulfillment: { + fulfillmentType: FulfillmentType.SHIPMENT + } + }); + + await expect(putFulfillmentFn).rejects.toThrowError('fulfillment is required (and 1 more error)'); + await expect(putFulfillmentFn).rejects.toMatchObject({ + status: 500, + errors: { + 'fulfillment': [ + 'fulfillment is required' + ], + 'fulfillment.fulfillmentType': [ + 'fulfillment.fulfillment type is required' + ] + } + }); + }); }); \ No newline at end of file diff --git a/test/customers/coordinates.test.ts b/test/customers/coordinates.test.ts index e989bc7..9f308a2 100644 --- a/test/customers/coordinates.test.ts +++ b/test/customers/coordinates.test.ts @@ -10,19 +10,19 @@ const CURRENT_URL = 'currentUrl'; const GET_COORDINATES_URL = '/app/website/cms/api/v1/users/1/customers/coordinates'; const okCoordinatesResponse = { - data: {} + data: {} }; const okFetchResponse = createFetchResponse(okCoordinatesResponse, true, 200); function createHeadersAndMethodForRequest(method: string = 'GET'): LooseObject { - return { - headers: { - 'Accept': 'application/json', - 'content-type' : 'application/json; charset=UTF-8', - 'X-CSRF-TOKEN': CSRF_TOKEN - }, - method: method, - }; + return { + headers: { + 'Accept': 'application/json', + 'content-type' : 'application/json; charset=UTF-8', + 'X-CSRF-TOKEN': CSRF_TOKEN + }, + method: method, + }; } const mockWindowHrefSet = vi.fn(); @@ -30,73 +30,73 @@ const mockWindowHrefSet = vi.fn(); const sdk = getTestSiteThemeSDK(); beforeEach(() => { - const documentMock = { - querySelector: () => { - return { - content: CSRF_TOKEN, - }; - }, - cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` - }; - - const windowMock = { - location: { - get href() { return CURRENT_URL; }, - set href(url) { - mockWindowHrefSet(url); - } - } - }; - - vi.stubGlobal('document', documentMock); - vi.stubGlobal('window', windowMock); - - global.fetch = vi.fn(); - vi.mocked(fetch).mockResolvedValue(okFetchResponse); + const documentMock = { + querySelector: () => { + return { + content: CSRF_TOKEN, + }; + }, + cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` + }; + + const windowMock = { + location: { + get href() { return CURRENT_URL; }, + set href(url) { + mockWindowHrefSet(url); + } + } + }; + + vi.stubGlobal('document', documentMock); + vi.stubGlobal('window', windowMock); + + global.fetch = vi.fn(); + vi.mocked(fetch).mockResolvedValue(okFetchResponse); }); describe('Customers Coordinates request', () => { - it('should make valid coordinates lookup', async () => { - const result = await sdk.customers.getCoordinates(); + it('should make valid coordinates lookup', async () => { + const result = await sdk.customers.getCoordinates(); - expect(fetch).toHaveBeenCalledWith( - `${GET_COORDINATES_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest() - }) - ); + expect(fetch).toHaveBeenCalledWith( + `${GET_COORDINATES_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest() + }) + ); - expect(fetch).toHaveBeenCalledOnce(); + expect(fetch).toHaveBeenCalledOnce(); - expect(result).toStrictEqual(okCoordinatesResponse); - }); + expect(result).toStrictEqual(okCoordinatesResponse); + }); - it('should make valid coordinates lookup with cdnDomain', async () => { - const cdnDomain = 'cdn3.editmysite.com'; - const sdk = getTestSiteThemeSDK({ - cdnDomain - }); - const result = await sdk.customers.getCoordinates(); + it('should make valid coordinates lookup with cdnDomain', async () => { + const cdnDomain = 'cdn3.editmysite.com'; + const sdk = getTestSiteThemeSDK({ + cdnDomain + }); + const result = await sdk.customers.getCoordinates(); - expect(fetch).toHaveBeenCalledWith( - `${GET_COORDINATES_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest() - }) - ); + expect(fetch).toHaveBeenCalledWith( + `${GET_COORDINATES_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest() + }) + ); - expect(fetch).toHaveBeenCalledOnce(); + expect(fetch).toHaveBeenCalledOnce(); - expect(result).toStrictEqual(okCoordinatesResponse); - }); + expect(result).toStrictEqual(okCoordinatesResponse); + }); - it('should return empty object on empty array', async () => { - const getCoordinatesResponse: never[] | LooseObject = []; - const fetchResponse = createFetchResponse(getCoordinatesResponse, true, 200); - vi.mocked(fetch).mockResolvedValue(fetchResponse); + it('should return empty object on empty array', async () => { + const getCoordinatesResponse: never[] | LooseObject = []; + const fetchResponse = createFetchResponse(getCoordinatesResponse, true, 200); + vi.mocked(fetch).mockResolvedValue(fetchResponse); - const result = await sdk.customers.getCoordinates(); + const result = await sdk.customers.getCoordinates(); - expect(result).toStrictEqual({}); - }); + expect(result).toStrictEqual({}); + }); }); diff --git a/test/customers/loyalty.test.ts b/test/customers/loyalty.test.ts index 1cde01c..01cce76 100644 --- a/test/customers/loyalty.test.ts +++ b/test/customers/loyalty.test.ts @@ -14,47 +14,47 @@ const GET_LOYALTY_ACCOUNT_URL = '/app/accounts/v1/loyalty/account/search'; const PING_URL = '/app/accounts/v1/ping'; const loyaltyAccount = { - id: '', - program_id: '', - balance: 7, - lifetime_points: 1337, + id: '', + program_id: '', + balance: 7, + lifetime_points: 1337, }; const okLoyaltyAccountResponse: GetLoyaltyAccountResponse = { - data: loyaltyAccount + data: loyaltyAccount }; const okBSLoyaltyAccountResponse: BSGetLoyaltyAccountResponse = { - data: { - loyalty_account: loyaltyAccount - } + data: { + loyalty_account: loyaltyAccount + } }; const okFetchResponse = createFetchResponse(okBSLoyaltyAccountResponse, true, 200); const notFoundFetchResponse = createFetchResponse( - {message: 'Square Loyalty not found.'}, - false, - 404); + {message: 'Square Loyalty not found.'}, + false, + 404); const pingFetchResponse = createFetchResponse(null, true, 201); const serverErrorFetchResponse = createFetchResponse(null, false, 500); const xsrfTokenMismatchResponse = createFetchResponse( - {error: 'CSRF Token mismatch'}, - false, - 419); + {error: 'CSRF Token mismatch'}, + false, + 419); function createRequestOptions(body: object|null = null, method: string = 'GET'): LooseObject { - const options: LooseObject = { - headers: { - 'Accept': 'application/json', - 'Content-Type' : 'application/json; charset=UTF-8', - 'X-XSRF-TOKEN': XSRF_TOKEN, - 'Square-Merchant-Token': MERCHANT_ID, - }, - method: method, - }; - if (body) { - options.body = JSON.stringify(body); - } - return options; + const options: LooseObject = { + headers: { + 'Accept': 'application/json', + 'Content-Type' : 'application/json; charset=UTF-8', + 'X-XSRF-TOKEN': XSRF_TOKEN, + 'Square-Merchant-Token': MERCHANT_ID, + }, + method: method, + }; + if (body) { + options.body = JSON.stringify(body); + } + return options; } const mockWindowHrefSet = vi.fn(); @@ -62,156 +62,156 @@ const mockWindowHrefSet = vi.fn(); const sdk = getTestSiteThemeSDK(); beforeEach(() => { - const documentMock = { - cookie: `test_cookie=test; ${XSRF_COOKIE_NAME}=${XSRF_TOKEN};` - }; - - const windowMock = { - location: { - get href() { return CURRENT_URL; }, - set href(url) { - mockWindowHrefSet(url); - } - } - }; - - vi.stubGlobal('document', documentMock); - vi.stubGlobal('window', windowMock); - - global.fetch = vi.fn(); - vi.mocked(fetch).mockResolvedValue(okFetchResponse); + const documentMock = { + cookie: `test_cookie=test; ${XSRF_COOKIE_NAME}=${XSRF_TOKEN};` + }; + + const windowMock = { + location: { + get href() { return CURRENT_URL; }, + set href(url) { + mockWindowHrefSet(url); + } + } + }; + + vi.stubGlobal('document', documentMock); + vi.stubGlobal('window', windowMock); + + global.fetch = vi.fn(); + vi.mocked(fetch).mockResolvedValue(okFetchResponse); }); describe('Loyalty account request', () => { - it('should return loyalty account if account found', async () => { - const request: GetLoyaltyAccountRequest = { - phone: '1234567890' - }; - const result = await sdk.customers.getLoyaltyAccount(request); - - expect(fetch).toHaveBeenCalledWith( - `${GET_LOYALTY_ACCOUNT_URL}`, - expect.objectContaining({ - ...createRequestOptions(request, 'POST'), - }) - ); - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okLoyaltyAccountResponse); - }); - - it('should return empty if no loyalty account found', async () => { - vi.mocked(fetch).mockResolvedValue(notFoundFetchResponse); - - const request: GetLoyaltyAccountRequest = { - phone: '1234567890' - }; - const result = await sdk.customers.getLoyaltyAccount(request); - - expect(fetch).toHaveBeenCalledWith( - `${GET_LOYALTY_ACCOUNT_URL}`, - expect.objectContaining({ - ...createRequestOptions(request, 'POST'), - }) - ); - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual({}); - }); - - it('should call ping if no xsrf cookie exists', async () => { - vi.stubGlobal('document', { cookie: '' }); - vi.mocked(fetch) - .mockImplementationOnce(() => { - // Can't actually set cookies so just pretend by resetting the document stub - vi.stubGlobal('document', {cookie: `${XSRF_COOKIE_NAME}=${XSRF_TOKEN};`}); - return Promise.resolve(pingFetchResponse); - }) - .mockResolvedValueOnce(okFetchResponse); - - const request: GetLoyaltyAccountRequest = { - phone: '1234567890' - }; - const result = await sdk.customers.getLoyaltyAccount(request); - - expect(fetch).nthCalledWith(1, PING_URL); - expect(fetch).nthCalledWith(2, - GET_LOYALTY_ACCOUNT_URL, - expect.objectContaining({ - ...createRequestOptions(request, 'POST'), - }) - ); - - expect(fetch).toBeCalledTimes(2); - - expect(result).toStrictEqual(okLoyaltyAccountResponse); - }); - - it('should call ping if server responds with 419', async () => { - vi.mocked(fetch) - .mockResolvedValueOnce(xsrfTokenMismatchResponse) - .mockImplementationOnce(() => { - // Can't actually set cookies so just pretend by resetting the document stub - vi.stubGlobal('document', {cookie: `${XSRF_COOKIE_NAME}=${XSRF_TOKEN};`}); - return Promise.resolve(pingFetchResponse); - }) - .mockResolvedValueOnce(okFetchResponse); - - const request: GetLoyaltyAccountRequest = { - phone: '1234567890' - }; - const result = await sdk.customers.getLoyaltyAccount(request); - - expect(fetch).nthCalledWith(1, - GET_LOYALTY_ACCOUNT_URL, - expect.objectContaining({ - ...createRequestOptions(request, 'POST'), - }) - ); - expect(fetch).nthCalledWith(2, PING_URL); - expect(fetch).nthCalledWith(3, - GET_LOYALTY_ACCOUNT_URL, - expect.objectContaining({ - ...createRequestOptions(request, 'POST'), - }) - ); - expect(fetch).toBeCalledTimes(3); - - expect(result).toStrictEqual(okLoyaltyAccountResponse); - }); - - it('should throw error if request fails', async () => { - vi.mocked(fetch).mockResolvedValue(serverErrorFetchResponse); - const request: GetLoyaltyAccountRequest = { - phone: '1234567890' - }; - const result = sdk.customers.getLoyaltyAccount(request); - - expect(fetch).toHaveBeenCalledWith( - `${GET_LOYALTY_ACCOUNT_URL}`, - expect.objectContaining({ - ...createRequestOptions(request, 'POST'), - }) - ); - expect(fetch).toHaveBeenCalledOnce(); - - await expect(result).rejects.toThrowError(`Error 500: ${STATUS_TEXT}`); - }); - - it('should throw error if cookies disabled', async () => { - // As opposed to a stack overflow from recursively re-requesting - vi.stubGlobal('document', { cookie: '' }); - vi.mocked(fetch) - .mockResolvedValueOnce(pingFetchResponse) - .mockResolvedValueOnce(xsrfTokenMismatchResponse) - .mockResolvedValueOnce(pingFetchResponse) - .mockResolvedValueOnce(pingFetchResponse) - .mockResolvedValueOnce(xsrfTokenMismatchResponse); - - const request: GetLoyaltyAccountRequest = { - phone: '1234567890' - }; - const result = sdk.customers.getLoyaltyAccount(request); - await expect(result).rejects.toThrowError(`Error 419: ${STATUS_TEXT}`); - expect(fetch).toBeCalledTimes(5); - }); + it('should return loyalty account if account found', async () => { + const request: GetLoyaltyAccountRequest = { + phone: '1234567890' + }; + const result = await sdk.customers.getLoyaltyAccount(request); + + expect(fetch).toHaveBeenCalledWith( + `${GET_LOYALTY_ACCOUNT_URL}`, + expect.objectContaining({ + ...createRequestOptions(request, 'POST'), + }) + ); + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okLoyaltyAccountResponse); + }); + + it('should return empty if no loyalty account found', async () => { + vi.mocked(fetch).mockResolvedValue(notFoundFetchResponse); + + const request: GetLoyaltyAccountRequest = { + phone: '1234567890' + }; + const result = await sdk.customers.getLoyaltyAccount(request); + + expect(fetch).toHaveBeenCalledWith( + `${GET_LOYALTY_ACCOUNT_URL}`, + expect.objectContaining({ + ...createRequestOptions(request, 'POST'), + }) + ); + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual({}); + }); + + it('should call ping if no xsrf cookie exists', async () => { + vi.stubGlobal('document', { cookie: '' }); + vi.mocked(fetch) + .mockImplementationOnce(() => { + // Can't actually set cookies so just pretend by resetting the document stub + vi.stubGlobal('document', {cookie: `${XSRF_COOKIE_NAME}=${XSRF_TOKEN};`}); + return Promise.resolve(pingFetchResponse); + }) + .mockResolvedValueOnce(okFetchResponse); + + const request: GetLoyaltyAccountRequest = { + phone: '1234567890' + }; + const result = await sdk.customers.getLoyaltyAccount(request); + + expect(fetch).nthCalledWith(1, PING_URL); + expect(fetch).nthCalledWith(2, + GET_LOYALTY_ACCOUNT_URL, + expect.objectContaining({ + ...createRequestOptions(request, 'POST'), + }) + ); + + expect(fetch).toBeCalledTimes(2); + + expect(result).toStrictEqual(okLoyaltyAccountResponse); + }); + + it('should call ping if server responds with 419', async () => { + vi.mocked(fetch) + .mockResolvedValueOnce(xsrfTokenMismatchResponse) + .mockImplementationOnce(() => { + // Can't actually set cookies so just pretend by resetting the document stub + vi.stubGlobal('document', {cookie: `${XSRF_COOKIE_NAME}=${XSRF_TOKEN};`}); + return Promise.resolve(pingFetchResponse); + }) + .mockResolvedValueOnce(okFetchResponse); + + const request: GetLoyaltyAccountRequest = { + phone: '1234567890' + }; + const result = await sdk.customers.getLoyaltyAccount(request); + + expect(fetch).nthCalledWith(1, + GET_LOYALTY_ACCOUNT_URL, + expect.objectContaining({ + ...createRequestOptions(request, 'POST'), + }) + ); + expect(fetch).nthCalledWith(2, PING_URL); + expect(fetch).nthCalledWith(3, + GET_LOYALTY_ACCOUNT_URL, + expect.objectContaining({ + ...createRequestOptions(request, 'POST'), + }) + ); + expect(fetch).toBeCalledTimes(3); + + expect(result).toStrictEqual(okLoyaltyAccountResponse); + }); + + it('should throw error if request fails', async () => { + vi.mocked(fetch).mockResolvedValue(serverErrorFetchResponse); + const request: GetLoyaltyAccountRequest = { + phone: '1234567890' + }; + const result = sdk.customers.getLoyaltyAccount(request); + + expect(fetch).toHaveBeenCalledWith( + `${GET_LOYALTY_ACCOUNT_URL}`, + expect.objectContaining({ + ...createRequestOptions(request, 'POST'), + }) + ); + expect(fetch).toHaveBeenCalledOnce(); + + await expect(result).rejects.toThrowError(`Error 500: ${STATUS_TEXT}`); + }); + + it('should throw error if cookies disabled', async () => { + // As opposed to a stack overflow from recursively re-requesting + vi.stubGlobal('document', { cookie: '' }); + vi.mocked(fetch) + .mockResolvedValueOnce(pingFetchResponse) + .mockResolvedValueOnce(xsrfTokenMismatchResponse) + .mockResolvedValueOnce(pingFetchResponse) + .mockResolvedValueOnce(pingFetchResponse) + .mockResolvedValueOnce(xsrfTokenMismatchResponse); + + const request: GetLoyaltyAccountRequest = { + phone: '1234567890' + }; + const result = sdk.customers.getLoyaltyAccount(request); + await expect(result).rejects.toThrowError(`Error 419: ${STATUS_TEXT}`); + expect(fetch).toBeCalledTimes(5); + }); }); diff --git a/test/helpers.item.test.ts b/test/helpers.item.test.ts index f23e9fd..034be14 100644 --- a/test/helpers.item.test.ts +++ b/test/helpers.item.test.ts @@ -4,27 +4,27 @@ import { describe, expect, it, vi } from 'vitest'; import { getTestSiteThemeSDK } from './helpers'; function createTestItem( - { - variationType = 'multiple', - setSoldOut = false, - setAllSoldOut = false, - setInventoryZero = false, - setAllInventoryZero = false, - setInventoryAboveZero = false, - choiceModifierMax = 2, - choiceModifierMin = 0, - textModifierMax = 2, - textModifierMin = 0, - zeroChoiceModifiers = false, - soldOutChoiceModifier = false, - perOrderMax = null, - squareOnlineType = 'PHYSICAL', - itemTypeDetailsEndDate = null, - itemTypeDetailsEndTime = null, - itemTypeDetailsOffset = null, - preorderingCutoff = null, - currency = 'USD', - }: { + { + variationType = 'multiple', + setSoldOut = false, + setAllSoldOut = false, + setInventoryZero = false, + setAllInventoryZero = false, + setInventoryAboveZero = false, + choiceModifierMax = 2, + choiceModifierMin = 0, + textModifierMax = 2, + textModifierMin = 0, + zeroChoiceModifiers = false, + soldOutChoiceModifier = false, + perOrderMax = null, + squareOnlineType = 'PHYSICAL', + itemTypeDetailsEndDate = null, + itemTypeDetailsEndTime = null, + itemTypeDetailsOffset = null, + preorderingCutoff = null, + currency = 'USD', + }: { variationType?: 'multiple' | 'single' | 'flat'; setSoldOut?: boolean; setAllSoldOut?: boolean; @@ -46,383 +46,383 @@ function createTestItem( currency?: string; } ) { - const testItem: Item = { - id: 'testItemId', - variations: [], - per_order_max: perOrderMax, - square_online_type: 'PHYSICAL', - item_type_details: {}, - preordering: { - 'PICKUP': false - }, - fulfillment_availability: { - 'PICKUP': [] - } - }; - - testItem.modifier_lists = [ - { - id: 'modifierListGiftWrapId', - max_selected_modifiers: 1, - min_selected_modifiers: 0, - modifiers: [ - { - id: 'modifierGiftWrapId1', - name: 'modifierGiftWrap1', - price_money: { - amount: 1000, - currency: currency, - formatted: '$10.00' - }, - sold_out: false - } - ], - name: 'modifierListGiftWrap', - type: ModifierType.GIFT_WRAP - }, - { - id: 'modifierListChoiceId', - max_selected_modifiers: choiceModifierMax, - min_selected_modifiers: choiceModifierMin, - modifiers: [ - { - id: 'modifierChoiceId1', - name: 'modifierChoice1', - price_money: { - amount: 100, - currency: currency, - formatted: '$1.00' - }, - sold_out: false - }, - { - id: 'modifierChoiceId2', - name: 'modifierChoice2', - price_money: { - amount: 200, - currency: currency, - formatted: '$2.00' - }, - sold_out: soldOutChoiceModifier ? true : false - }, - { - id: 'modifierChoiceId3', - name: 'modifierChoice3', - price_money: { - amount: 300, - currency: currency, - formatted: '$3.00' - }, - sold_out: false - } - ], - name: 'modifierListChoice', - type: ModifierType.CHOICE - }, - { - id: 'modifierListGiftMessageId', - max_selected_modifiers: 220, - min_selected_modifiers: 0, - name: 'modifierListGiftMessage', - type: ModifierType.GIFT_MESSAGE - }, - { - id: 'modifierListTextId', - max_selected_modifiers: textModifierMax, - min_selected_modifiers: textModifierMin, - name: 'modifierListText', - type: ModifierType.TEXT - }, - ]; - - if (zeroChoiceModifiers) { - testItem.modifier_lists[1].modifiers = []; - testItem.modifier_lists[1].max_selected_modifiers = 0; - testItem.modifier_lists[1].min_selected_modifiers = 0; - } - - if (!variationType || variationType === 'multiple') { - testItem.item_options = [ - { - choices: ['choice1', 'choice2'], - id: 'itemOptionId1', - name: 'itemOption1' - }, - { - choices: ['choice1', 'choice2'], - id: 'itemOptionId2', - name: 'itemOption2' - }, - ]; - testItem.variations = [ - { - id: 'variation1', - item_option_values: { - 'itemOptionId1': { - choice: 'choice1', - name: 'itemOption1' - }, - 'itemOptionId2': { - choice: 'choice1', - name: 'itemOption2' - } - }, - sold_out: setAllSoldOut, - inventory_tracking_enabled: setAllInventoryZero, - price: { - regular: { - amount: 200, - currency: currency, - formatted: '$2.00' - }, - sale: { - amount: 100, - currency: currency, - formatted: '$1.00' - }, - } - }, - { - id: 'variation2', - item_option_values: { - 'itemOptionId1': { - choice: 'choice1', - name: 'itemOption1' - }, - 'itemOptionId2': { - choice: 'choice2', - name: 'itemOption2' - } - }, - sold_out: setAllSoldOut, - inventory_tracking_enabled: setAllInventoryZero, - price: { - regular: { - amount: 300, - currency: currency, - formatted: '$3.00' - }, - sale: { - amount: 200, - currency: currency, - formatted: '$2.00' - } - } - }, - { - id: 'variation3', - item_option_values: { - 'itemOptionId1': { - choice: 'choice2', - name: 'itemOption1' - }, - 'itemOptionId2': { - choice: 'choice1', - name: 'itemOption2' - } - }, - sold_out: setAllSoldOut, - inventory_tracking_enabled: setAllInventoryZero, - price: { - regular: { - amount: 400, - currency: currency, - formatted: '$4.00' - }, - sale: { - amount: 300, - currency: currency, - formatted: '$3.00' - } - } - }, - { - id: 'variation4', - item_option_values: { - 'itemOptionId1': { - choice: 'choice2', - name: 'itemOption1' - }, - 'itemOptionId2': { - choice: 'choice2', - name: 'itemOption2' - } - }, - sold_out: setSoldOut || setAllSoldOut, - inventory_tracking_enabled: setInventoryZero || setAllInventoryZero || setInventoryAboveZero, - price: { - regular: { - amount: 500, - currency: currency, - formatted: '$5.00' - }, - sale: { - amount: 400, - currency: currency, - formatted: '$4.00' - }, - } - }, - ]; - if (setInventoryZero) { - testItem.variations[3].inventory = 0; - } else if (setInventoryAboveZero) { - testItem.variations[3].inventory = 1; - } else if (setAllInventoryZero) { - testItem.variations.forEach(v => v.inventory = 0); - } - } else if (variationType == 'flat') { - testItem.variations = [ - { - id: 'variation1', - sold_out: setAllSoldOut, - inventory_tracking_enabled: setAllInventoryZero, - price: { - regular: { - amount: 200, - currency: currency, - formatted: '$2.00' - }, - sale: { - amount: 100, - currency: currency, - formatted: '$1.00' - }, - } - }, - { - id: 'variation2', - sold_out: setAllSoldOut, - inventory_tracking_enabled: setAllInventoryZero, - price: { - regular: { - amount: 300, - currency: currency, - formatted: '$3.00' - }, - sale: { - amount: 200, - currency: currency, - formatted: '$2.00' - }, - } - }, - { - id: 'variation3', - sold_out: setAllSoldOut, - inventory_tracking_enabled: setAllInventoryZero, - price: { - regular: { - amount: 300, - currency: currency, - formatted: '$3.00' - }, - sale: { - amount: 400, - currency: currency, - formatted: '$4.00' - } - } - }, - { - id: 'variation4', - sold_out: setSoldOut || setAllSoldOut, - inventory_tracking_enabled: setInventoryZero || setAllInventoryZero || setInventoryAboveZero, - price: { - regular: { - amount: 400, - currency: currency, - formatted: '$4.00' - }, - sale: { - amount: 500, - currency: currency, - formatted: '$5.00' - } - } - } - ]; - if (setInventoryZero) { - testItem.variations[3].inventory = 0; - } else if (setInventoryAboveZero) { - testItem.variations[3].inventory = 1; - } else if (setAllInventoryZero) { - testItem.variations.forEach(v => v.inventory = 0); - } - } else { - testItem.variations = [ - { - id: 'variation1', - sold_out: setSoldOut || setAllSoldOut, - inventory_tracking_enabled: setInventoryZero || setAllInventoryZero || setInventoryAboveZero, - price: { - regular: { - amount: 200, - currency: currency, - formatted: '$2.00' - }, - sale: { - amount: 100, - currency: currency, - formatted: '$1.00' - } - } - }, - ]; - if (setInventoryZero || setAllInventoryZero) { - testItem.variations[0].inventory = 0; - } else if (setInventoryAboveZero) { - testItem.variations[0].inventory = 1; - } - } - - if (squareOnlineType) { - testItem.square_online_type = squareOnlineType; - } - - if (itemTypeDetailsEndDate || itemTypeDetailsEndTime || itemTypeDetailsOffset) { - testItem.item_type_details = { - end_date: itemTypeDetailsEndDate!, - end_time: itemTypeDetailsEndTime!, - timezone_info: { - utc_offset_string: itemTypeDetailsOffset! - } - }; - } - - if (preorderingCutoff) { - testItem.preordering = { - 'PICKUP': true - }; - testItem.fulfillment_availability = { - 'PICKUP': [ - { - availability_cutoff_at: preorderingCutoff - } - ] - }; - } - - return testItem; + const testItem: Item = { + id: 'testItemId', + variations: [], + per_order_max: perOrderMax, + square_online_type: 'PHYSICAL', + item_type_details: {}, + preordering: { + 'PICKUP': false + }, + fulfillment_availability: { + 'PICKUP': [] + } + }; + + testItem.modifier_lists = [ + { + id: 'modifierListGiftWrapId', + max_selected_modifiers: 1, + min_selected_modifiers: 0, + modifiers: [ + { + id: 'modifierGiftWrapId1', + name: 'modifierGiftWrap1', + price_money: { + amount: 1000, + currency: currency, + formatted: '$10.00' + }, + sold_out: false + } + ], + name: 'modifierListGiftWrap', + type: ModifierType.GIFT_WRAP + }, + { + id: 'modifierListChoiceId', + max_selected_modifiers: choiceModifierMax, + min_selected_modifiers: choiceModifierMin, + modifiers: [ + { + id: 'modifierChoiceId1', + name: 'modifierChoice1', + price_money: { + amount: 100, + currency: currency, + formatted: '$1.00' + }, + sold_out: false + }, + { + id: 'modifierChoiceId2', + name: 'modifierChoice2', + price_money: { + amount: 200, + currency: currency, + formatted: '$2.00' + }, + sold_out: soldOutChoiceModifier ? true : false + }, + { + id: 'modifierChoiceId3', + name: 'modifierChoice3', + price_money: { + amount: 300, + currency: currency, + formatted: '$3.00' + }, + sold_out: false + } + ], + name: 'modifierListChoice', + type: ModifierType.CHOICE + }, + { + id: 'modifierListGiftMessageId', + max_selected_modifiers: 220, + min_selected_modifiers: 0, + name: 'modifierListGiftMessage', + type: ModifierType.GIFT_MESSAGE + }, + { + id: 'modifierListTextId', + max_selected_modifiers: textModifierMax, + min_selected_modifiers: textModifierMin, + name: 'modifierListText', + type: ModifierType.TEXT + }, + ]; + + if (zeroChoiceModifiers) { + testItem.modifier_lists[1].modifiers = []; + testItem.modifier_lists[1].max_selected_modifiers = 0; + testItem.modifier_lists[1].min_selected_modifiers = 0; + } + + if (!variationType || variationType === 'multiple') { + testItem.item_options = [ + { + choices: ['choice1', 'choice2'], + id: 'itemOptionId1', + name: 'itemOption1' + }, + { + choices: ['choice1', 'choice2'], + id: 'itemOptionId2', + name: 'itemOption2' + }, + ]; + testItem.variations = [ + { + id: 'variation1', + item_option_values: { + 'itemOptionId1': { + choice: 'choice1', + name: 'itemOption1' + }, + 'itemOptionId2': { + choice: 'choice1', + name: 'itemOption2' + } + }, + sold_out: setAllSoldOut, + inventory_tracking_enabled: setAllInventoryZero, + price: { + regular: { + amount: 200, + currency: currency, + formatted: '$2.00' + }, + sale: { + amount: 100, + currency: currency, + formatted: '$1.00' + }, + } + }, + { + id: 'variation2', + item_option_values: { + 'itemOptionId1': { + choice: 'choice1', + name: 'itemOption1' + }, + 'itemOptionId2': { + choice: 'choice2', + name: 'itemOption2' + } + }, + sold_out: setAllSoldOut, + inventory_tracking_enabled: setAllInventoryZero, + price: { + regular: { + amount: 300, + currency: currency, + formatted: '$3.00' + }, + sale: { + amount: 200, + currency: currency, + formatted: '$2.00' + } + } + }, + { + id: 'variation3', + item_option_values: { + 'itemOptionId1': { + choice: 'choice2', + name: 'itemOption1' + }, + 'itemOptionId2': { + choice: 'choice1', + name: 'itemOption2' + } + }, + sold_out: setAllSoldOut, + inventory_tracking_enabled: setAllInventoryZero, + price: { + regular: { + amount: 400, + currency: currency, + formatted: '$4.00' + }, + sale: { + amount: 300, + currency: currency, + formatted: '$3.00' + } + } + }, + { + id: 'variation4', + item_option_values: { + 'itemOptionId1': { + choice: 'choice2', + name: 'itemOption1' + }, + 'itemOptionId2': { + choice: 'choice2', + name: 'itemOption2' + } + }, + sold_out: setSoldOut || setAllSoldOut, + inventory_tracking_enabled: setInventoryZero || setAllInventoryZero || setInventoryAboveZero, + price: { + regular: { + amount: 500, + currency: currency, + formatted: '$5.00' + }, + sale: { + amount: 400, + currency: currency, + formatted: '$4.00' + }, + } + }, + ]; + if (setInventoryZero) { + testItem.variations[3].inventory = 0; + } else if (setInventoryAboveZero) { + testItem.variations[3].inventory = 1; + } else if (setAllInventoryZero) { + testItem.variations.forEach(v => v.inventory = 0); + } + } else if (variationType == 'flat') { + testItem.variations = [ + { + id: 'variation1', + sold_out: setAllSoldOut, + inventory_tracking_enabled: setAllInventoryZero, + price: { + regular: { + amount: 200, + currency: currency, + formatted: '$2.00' + }, + sale: { + amount: 100, + currency: currency, + formatted: '$1.00' + }, + } + }, + { + id: 'variation2', + sold_out: setAllSoldOut, + inventory_tracking_enabled: setAllInventoryZero, + price: { + regular: { + amount: 300, + currency: currency, + formatted: '$3.00' + }, + sale: { + amount: 200, + currency: currency, + formatted: '$2.00' + }, + } + }, + { + id: 'variation3', + sold_out: setAllSoldOut, + inventory_tracking_enabled: setAllInventoryZero, + price: { + regular: { + amount: 300, + currency: currency, + formatted: '$3.00' + }, + sale: { + amount: 400, + currency: currency, + formatted: '$4.00' + } + } + }, + { + id: 'variation4', + sold_out: setSoldOut || setAllSoldOut, + inventory_tracking_enabled: setInventoryZero || setAllInventoryZero || setInventoryAboveZero, + price: { + regular: { + amount: 400, + currency: currency, + formatted: '$4.00' + }, + sale: { + amount: 500, + currency: currency, + formatted: '$5.00' + } + } + } + ]; + if (setInventoryZero) { + testItem.variations[3].inventory = 0; + } else if (setInventoryAboveZero) { + testItem.variations[3].inventory = 1; + } else if (setAllInventoryZero) { + testItem.variations.forEach(v => v.inventory = 0); + } + } else { + testItem.variations = [ + { + id: 'variation1', + sold_out: setSoldOut || setAllSoldOut, + inventory_tracking_enabled: setInventoryZero || setAllInventoryZero || setInventoryAboveZero, + price: { + regular: { + amount: 200, + currency: currency, + formatted: '$2.00' + }, + sale: { + amount: 100, + currency: currency, + formatted: '$1.00' + } + } + }, + ]; + if (setInventoryZero || setAllInventoryZero) { + testItem.variations[0].inventory = 0; + } else if (setInventoryAboveZero) { + testItem.variations[0].inventory = 1; + } + } + + if (squareOnlineType) { + testItem.square_online_type = squareOnlineType; + } + + if (itemTypeDetailsEndDate || itemTypeDetailsEndTime || itemTypeDetailsOffset) { + testItem.item_type_details = { + end_date: itemTypeDetailsEndDate!, + end_time: itemTypeDetailsEndTime!, + timezone_info: { + utc_offset_string: itemTypeDetailsOffset! + } + }; + } + + if (preorderingCutoff) { + testItem.preordering = { + 'PICKUP': true + }; + testItem.fulfillment_availability = { + 'PICKUP': [ + { + availability_cutoff_at: preorderingCutoff + } + ] + }; + } + + return testItem; } function createOptionSelection(itemOption: ItemOption, choiceIndex: number): OptionSelection { - return { - itemOptionId: itemOption.id, - choice: itemOption.choices[choiceIndex] - }; + return { + itemOptionId: itemOption.id, + choice: itemOption.choices[choiceIndex] + }; } function createModifierSelection({ - excludeGiftWrap = false, - excludeChoice = false, - excludeGiftMessage = false, - excludeText = false, - choiceSelectionsCount = 2, - textSelectionCount = 2, - invalidChoice = false, - useChoiceSelectionObject = false, - useMultipleChoiceSelectionQuantity = false, + excludeGiftWrap = false, + excludeChoice = false, + excludeGiftMessage = false, + excludeText = false, + choiceSelectionsCount = 2, + textSelectionCount = 2, + invalidChoice = false, + useChoiceSelectionObject = false, + useMultipleChoiceSelectionQuantity = false, }: { excludeGiftWrap?: boolean; excludeChoice?: boolean; @@ -434,59 +434,59 @@ function createModifierSelection({ useChoiceSelectionObject?: boolean; useMultipleChoiceSelectionQuantity?: boolean; }) { - const selectedModifiers: AddItemModifier[] = []; - if (!excludeGiftWrap) { - selectedModifiers.push({ - id: 'modifierListGiftWrapId', - type: ModifierType.GIFT_WRAP, - choiceSelections: useChoiceSelectionObject ? [ - { id: 'modifierGiftWrapId1', quantity: 1 } - ] : [ - 'modifierGiftWrapId1' - ] - }); - } - if (!excludeChoice) { - const choiceModifier = { - id: 'modifierListChoiceId', - type: ModifierType.CHOICE, - choiceSelections: useChoiceSelectionObject ? [ - invalidChoice ? { id: 'invalidChoiceId1', quantity: 1 } : { id: 'modifierChoiceId1', quantity: useMultipleChoiceSelectionQuantity ? 2 : 1 }, - { id: 'modifierChoiceId2', quantity: 1 }, - ]: [ - invalidChoice ? 'invalidChoiceId1' : 'modifierChoiceId1', - 'modifierChoiceId2', - ] - }; - if (choiceSelectionsCount === 1) { - choiceModifier.choiceSelections.pop(); - } else if (choiceSelectionsCount === 0) { - choiceModifier.choiceSelections = []; - } else if (choiceSelectionsCount === 3) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any - (choiceModifier.choiceSelections).push(useChoiceSelectionObject ? { id: 'modifierChoiceId3', quantity: 1 } : 'modifierChoiceId3'); - } - selectedModifiers.push(choiceModifier); - } - if (!excludeGiftMessage) { - selectedModifiers.push({ - id: 'modifierListGiftMessageId', - type: ModifierType.GIFT_MESSAGE, - textEntry: 'This is test text' - }); - } - if (!excludeText) { - let textEntry = ''; - for (let i = 0; i < textSelectionCount; i++) { - textEntry += 'a'; - } - selectedModifiers.push({ - id: 'modifierListTextId', - type: ModifierType.TEXT, - textEntry: textEntry - }); - } - return selectedModifiers; + const selectedModifiers: AddItemModifier[] = []; + if (!excludeGiftWrap) { + selectedModifiers.push({ + id: 'modifierListGiftWrapId', + type: ModifierType.GIFT_WRAP, + choiceSelections: useChoiceSelectionObject ? [ + { id: 'modifierGiftWrapId1', quantity: 1 } + ] : [ + 'modifierGiftWrapId1' + ] + }); + } + if (!excludeChoice) { + const choiceModifier = { + id: 'modifierListChoiceId', + type: ModifierType.CHOICE, + choiceSelections: useChoiceSelectionObject ? [ + invalidChoice ? { id: 'invalidChoiceId1', quantity: 1 } : { id: 'modifierChoiceId1', quantity: useMultipleChoiceSelectionQuantity ? 2 : 1 }, + { id: 'modifierChoiceId2', quantity: 1 }, + ]: [ + invalidChoice ? 'invalidChoiceId1' : 'modifierChoiceId1', + 'modifierChoiceId2', + ] + }; + if (choiceSelectionsCount === 1) { + choiceModifier.choiceSelections.pop(); + } else if (choiceSelectionsCount === 0) { + choiceModifier.choiceSelections = []; + } else if (choiceSelectionsCount === 3) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any + (choiceModifier.choiceSelections).push(useChoiceSelectionObject ? { id: 'modifierChoiceId3', quantity: 1 } : 'modifierChoiceId3'); + } + selectedModifiers.push(choiceModifier); + } + if (!excludeGiftMessage) { + selectedModifiers.push({ + id: 'modifierListGiftMessageId', + type: ModifierType.GIFT_MESSAGE, + textEntry: 'This is test text' + }); + } + if (!excludeText) { + let textEntry = ''; + for (let i = 0; i < textSelectionCount; i++) { + textEntry += 'a'; + } + selectedModifiers.push({ + id: 'modifierListTextId', + type: ModifierType.TEXT, + textEntry: textEntry + }); + } + return selectedModifiers; } const sdk = getTestSiteThemeSDK(); @@ -494,1430 +494,1430 @@ const sdk = getTestSiteThemeSDK(); const defaultItem = createTestItem({ }); describe('Getters', () => { - it('should get variations', () => { - const result = sdk.helpers.item.getVariations(defaultItem); - expect(result).toStrictEqual(defaultItem.variations); - }); - - it('should get item options', () => { - const result = sdk.helpers.item.getItemOptions(defaultItem); - expect(result).toStrictEqual(defaultItem.item_options); - }); - - it('should get modifier lists', () => { - const result = sdk.helpers.item.getModifierLists(defaultItem); - expect(result).toStrictEqual(defaultItem.modifier_lists); - }); + it('should get variations', () => { + const result = sdk.helpers.item.getVariations(defaultItem); + expect(result).toStrictEqual(defaultItem.variations); + }); + + it('should get item options', () => { + const result = sdk.helpers.item.getItemOptions(defaultItem); + expect(result).toStrictEqual(defaultItem.item_options); + }); + + it('should get modifier lists', () => { + const result = sdk.helpers.item.getModifierLists(defaultItem); + expect(result).toStrictEqual(defaultItem.modifier_lists); + }); }); describe('In stock variations', () => { - it('should return single variation for single variation', () => { - const singleVariationItem = createTestItem({ variationType: 'single' }); - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem }); - expect(result).toStrictEqual([ singleVariationItem.variations[0] ]); - }); - - it('should return empty for single variation sold out', () => { - const singleVariationItem = createTestItem({ variationType: 'single', setSoldOut: true }); - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem }); - expect(result).toStrictEqual([]); - const result2 = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem, skipStockCheck: true }); - expect(result2).toStrictEqual([ singleVariationItem.variations[0] ]); - }); - - it('should return empty for single variation with inventory 0', () => { - const singleVariationItem = createTestItem({ variationType: 'single', setInventoryZero: true }); - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem }); - expect(result).toStrictEqual([]); - const result2 = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem, skipStockCheck: true }); - expect(result2).toStrictEqual([ singleVariationItem.variations[0] ]); - }); - - it('should return single variation with inventory above 0', () => { - const singleVariationItem = createTestItem({ variationType: 'single', setInventoryAboveZero: true }); - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem, skipStockCheck: true }); - expect(result).toStrictEqual([ singleVariationItem.variations[0] ]); - }); - - it('should return all variations for no selected options', () => { - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: defaultItem }); - expect(result).toStrictEqual(defaultItem.variations); - }); - - it('should return single variation for all selected options', () => { - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 0), - createOptionSelection(defaultItem.item_options![1], 0) - ]; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: defaultItem, selectedOptions }); - expect(result).toStrictEqual([ defaultItem.variations[0] ]); - }); - - it('should return single variation for selected variation id', () => { - const selectedVariationId = defaultItem.variations[3].id; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: defaultItem, selectedVariationId }); - expect(result).toStrictEqual([ defaultItem.variations[3] ]); - }); - - it('should return other variations for single selected option', () => { - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 0), - ]; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: defaultItem, selectedOptions }); - expect(result).toStrictEqual([ defaultItem.variations[0], defaultItem.variations[1] ]); - }); - - it('should exclude sold out variations for single selected option', () => { - const withSoldOutItem = createTestItem({ setSoldOut: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 1), - ]; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withSoldOutItem, selectedOptions }); - expect(result).toStrictEqual([ withSoldOutItem.variations[2] ]); - const result2 = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withSoldOutItem, selectedOptions, skipStockCheck: true }); - expect(result2).toStrictEqual([ withSoldOutItem.variations[2], withSoldOutItem.variations[3] ]); - }); - - it('should exclude inventory 0 variations for single selected option', () => { - const withInventoryZeroItem = createTestItem({ setInventoryZero: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 1), - ]; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withInventoryZeroItem, selectedOptions }); - expect(result).toStrictEqual([ withInventoryZeroItem.variations[2] ]); - const result2 = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withInventoryZeroItem, selectedOptions, skipStockCheck: true }); - expect(result2).toStrictEqual([ withInventoryZeroItem.variations[2], withInventoryZeroItem.variations[3] ]); - }); - - it('should include inventory above 0 variations for single selected option', () => { - const withInventoryAboveZeroItem = createTestItem({ setInventoryAboveZero: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 1), - ]; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withInventoryAboveZeroItem, selectedOptions }); - expect(result).toStrictEqual([ withInventoryAboveZeroItem.variations[2], withInventoryAboveZeroItem.variations[3] ]); - }); - - it('should include selected variation for flat variations', () => { - const flatItem = createTestItem({ variationType: 'flat' }); - const selectedVariationId = flatItem.variations[3].id; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); - expect(result).toStrictEqual([ flatItem.variations[3] ]); - }); - - it('should exclude sold out for selected variation for flat variations', () => { - const flatItem = createTestItem({ variationType: 'flat', setSoldOut: true }); - const selectedVariationId = flatItem.variations[3].id; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); - expect(result).toStrictEqual([]); - }); - - it('should exclude inventory 0 for selected variation for flat variations', () => { - const flatItem = createTestItem({ variationType: 'flat', setInventoryZero: true }); - const selectedVariationId = flatItem.variations[3].id; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); - expect(result).toStrictEqual([]); - }); - - it('should include inventory above 0 for selected variation for flat variations', () => { - const flatItem = createTestItem({ variationType: 'flat', setInventoryAboveZero: true }); - const selectedVariationId = flatItem.variations[3].id; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); - expect(result).toStrictEqual([ flatItem.variations[3] ]); - }); - - it('should return empty for invalid selected variation for flat variations', () => { - const flatItem = createTestItem({ variationType: 'flat' }); - const selectedVariationId = 'doesnotexist'; - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); - expect(result).toStrictEqual([ ]); - }); - - it('should return empty for no selected variation for flat variations', () => { - const flatItem = createTestItem({ variationType: 'flat' }); - const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem }); - expect(result).toStrictEqual([ ]); - }); + it('should return single variation for single variation', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem }); + expect(result).toStrictEqual([ singleVariationItem.variations[0] ]); + }); + + it('should return empty for single variation sold out', () => { + const singleVariationItem = createTestItem({ variationType: 'single', setSoldOut: true }); + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem }); + expect(result).toStrictEqual([]); + const result2 = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem, skipStockCheck: true }); + expect(result2).toStrictEqual([ singleVariationItem.variations[0] ]); + }); + + it('should return empty for single variation with inventory 0', () => { + const singleVariationItem = createTestItem({ variationType: 'single', setInventoryZero: true }); + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem }); + expect(result).toStrictEqual([]); + const result2 = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem, skipStockCheck: true }); + expect(result2).toStrictEqual([ singleVariationItem.variations[0] ]); + }); + + it('should return single variation with inventory above 0', () => { + const singleVariationItem = createTestItem({ variationType: 'single', setInventoryAboveZero: true }); + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: singleVariationItem, skipStockCheck: true }); + expect(result).toStrictEqual([ singleVariationItem.variations[0] ]); + }); + + it('should return all variations for no selected options', () => { + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: defaultItem }); + expect(result).toStrictEqual(defaultItem.variations); + }); + + it('should return single variation for all selected options', () => { + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 0), + createOptionSelection(defaultItem.item_options![1], 0) + ]; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: defaultItem, selectedOptions }); + expect(result).toStrictEqual([ defaultItem.variations[0] ]); + }); + + it('should return single variation for selected variation id', () => { + const selectedVariationId = defaultItem.variations[3].id; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: defaultItem, selectedVariationId }); + expect(result).toStrictEqual([ defaultItem.variations[3] ]); + }); + + it('should return other variations for single selected option', () => { + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 0), + ]; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: defaultItem, selectedOptions }); + expect(result).toStrictEqual([ defaultItem.variations[0], defaultItem.variations[1] ]); + }); + + it('should exclude sold out variations for single selected option', () => { + const withSoldOutItem = createTestItem({ setSoldOut: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 1), + ]; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withSoldOutItem, selectedOptions }); + expect(result).toStrictEqual([ withSoldOutItem.variations[2] ]); + const result2 = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withSoldOutItem, selectedOptions, skipStockCheck: true }); + expect(result2).toStrictEqual([ withSoldOutItem.variations[2], withSoldOutItem.variations[3] ]); + }); + + it('should exclude inventory 0 variations for single selected option', () => { + const withInventoryZeroItem = createTestItem({ setInventoryZero: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 1), + ]; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withInventoryZeroItem, selectedOptions }); + expect(result).toStrictEqual([ withInventoryZeroItem.variations[2] ]); + const result2 = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withInventoryZeroItem, selectedOptions, skipStockCheck: true }); + expect(result2).toStrictEqual([ withInventoryZeroItem.variations[2], withInventoryZeroItem.variations[3] ]); + }); + + it('should include inventory above 0 variations for single selected option', () => { + const withInventoryAboveZeroItem = createTestItem({ setInventoryAboveZero: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 1), + ]; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: withInventoryAboveZeroItem, selectedOptions }); + expect(result).toStrictEqual([ withInventoryAboveZeroItem.variations[2], withInventoryAboveZeroItem.variations[3] ]); + }); + + it('should include selected variation for flat variations', () => { + const flatItem = createTestItem({ variationType: 'flat' }); + const selectedVariationId = flatItem.variations[3].id; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); + expect(result).toStrictEqual([ flatItem.variations[3] ]); + }); + + it('should exclude sold out for selected variation for flat variations', () => { + const flatItem = createTestItem({ variationType: 'flat', setSoldOut: true }); + const selectedVariationId = flatItem.variations[3].id; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); + expect(result).toStrictEqual([]); + }); + + it('should exclude inventory 0 for selected variation for flat variations', () => { + const flatItem = createTestItem({ variationType: 'flat', setInventoryZero: true }); + const selectedVariationId = flatItem.variations[3].id; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); + expect(result).toStrictEqual([]); + }); + + it('should include inventory above 0 for selected variation for flat variations', () => { + const flatItem = createTestItem({ variationType: 'flat', setInventoryAboveZero: true }); + const selectedVariationId = flatItem.variations[3].id; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); + expect(result).toStrictEqual([ flatItem.variations[3] ]); + }); + + it('should return empty for invalid selected variation for flat variations', () => { + const flatItem = createTestItem({ variationType: 'flat' }); + const selectedVariationId = 'doesnotexist'; + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem, selectedVariationId }); + expect(result).toStrictEqual([ ]); + }); + + it('should return empty for no selected variation for flat variations', () => { + const flatItem = createTestItem({ variationType: 'flat' }); + const result = sdk.helpers.item.getInStockVariationsForSelectedOptionsOrVariation({ item: flatItem }); + expect(result).toStrictEqual([ ]); + }); }); describe('Option choice disabled', () => { - it('should return false for all options with no selections', () => { - const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), []); - expect(result).toStrictEqual(false); - const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), []); - expect(result2).toStrictEqual(false); - const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 0), []); - expect(result3).toStrictEqual(false); - const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 1), []); - expect(result4).toStrictEqual(false); - }); - - it('should return false for all options with single option selection', () => { - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 0), - ]; - const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), selectedOptions); - expect(result).toStrictEqual(false); - const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), selectedOptions); - expect(result2).toStrictEqual(false); - const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 0), selectedOptions); - expect(result3).toStrictEqual(false); - const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 1), selectedOptions); - expect(result4).toStrictEqual(false); - // Check removeMatchingOptionSet false - const result5 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), selectedOptions, false); - expect(result5).toStrictEqual(false); - const result6 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), selectedOptions, false); - expect(result6).toStrictEqual(true); - }); - - it('should return false for selected options with all option selections', () => { - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 0), - createOptionSelection(defaultItem.item_options![1], 0), - ]; - const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), selectedOptions); - expect(result).toStrictEqual(false); - const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), selectedOptions); - expect(result2).toStrictEqual(false); - const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 0), selectedOptions); - expect(result3).toStrictEqual(false); - const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 1), selectedOptions); - expect(result4).toStrictEqual(false); - // Check removeMatchingOptionSet false - const result5 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), selectedOptions, false); - expect(result5).toStrictEqual(false); - const result6 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), selectedOptions, false); - expect(result6).toStrictEqual(true); - const result7 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 0), selectedOptions, false); - expect(result7).toStrictEqual(false); - const result8 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 1), selectedOptions, false); - expect(result8).toStrictEqual(true); - }); - - it('should return true for sold out variant with single option selected', () => { - const multipleVariationsItem = createTestItem({ setSoldOut: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationsItem.item_options![0], 1), - ]; - const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 0), selectedOptions); - expect(result).toStrictEqual(false); - const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 1), selectedOptions); - expect(result2).toStrictEqual(false); - const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 0), selectedOptions); - expect(result3).toStrictEqual(false); - const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 1), selectedOptions); - expect(result4).toStrictEqual(true); - }); - - it('should return true with inventory 0 variant with single option selected', () => { - const multipleVariationsItem = createTestItem({ setInventoryZero: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationsItem.item_options![0], 1), - ]; - const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 0), selectedOptions); - expect(result).toStrictEqual(false); - const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 1), selectedOptions); - expect(result2).toStrictEqual(false); - const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 0), selectedOptions); - expect(result3).toStrictEqual(false); - const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 1), selectedOptions); - expect(result4).toStrictEqual(true); - }); - - it('should return false with inventory above 0 variant with single option selected', () => { - const multipleVariationsItem = createTestItem({ setInventoryAboveZero: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationsItem.item_options![0], 1), - ]; - const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 0), selectedOptions); - expect(result).toStrictEqual(false); - const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 1), selectedOptions); - expect(result2).toStrictEqual(false); - const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 0), selectedOptions); - expect(result3).toStrictEqual(false); - const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 1), selectedOptions); - expect(result4).toStrictEqual(false); - }); - - it('should return true for all with sold out options selected', () => { - const multipleVariationsItem = createTestItem({ setSoldOut: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationsItem.item_options![0], 1), - createOptionSelection(multipleVariationsItem.item_options![1], 1), - ]; - const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 0), selectedOptions); - expect(result).toStrictEqual(false); - const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 1), selectedOptions); - expect(result2).toStrictEqual(true); - const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 0), selectedOptions); - expect(result3).toStrictEqual(false); - const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 1), selectedOptions); - expect(result4).toStrictEqual(true); - }); + it('should return false for all options with no selections', () => { + const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), []); + expect(result).toStrictEqual(false); + const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), []); + expect(result2).toStrictEqual(false); + const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 0), []); + expect(result3).toStrictEqual(false); + const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 1), []); + expect(result4).toStrictEqual(false); + }); + + it('should return false for all options with single option selection', () => { + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 0), + ]; + const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), selectedOptions); + expect(result).toStrictEqual(false); + const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), selectedOptions); + expect(result2).toStrictEqual(false); + const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 0), selectedOptions); + expect(result3).toStrictEqual(false); + const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 1), selectedOptions); + expect(result4).toStrictEqual(false); + // Check removeMatchingOptionSet false + const result5 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), selectedOptions, false); + expect(result5).toStrictEqual(false); + const result6 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), selectedOptions, false); + expect(result6).toStrictEqual(true); + }); + + it('should return false for selected options with all option selections', () => { + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 0), + createOptionSelection(defaultItem.item_options![1], 0), + ]; + const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), selectedOptions); + expect(result).toStrictEqual(false); + const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), selectedOptions); + expect(result2).toStrictEqual(false); + const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 0), selectedOptions); + expect(result3).toStrictEqual(false); + const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 1), selectedOptions); + expect(result4).toStrictEqual(false); + // Check removeMatchingOptionSet false + const result5 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 0), selectedOptions, false); + expect(result5).toStrictEqual(false); + const result6 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![0], 1), selectedOptions, false); + expect(result6).toStrictEqual(true); + const result7 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 0), selectedOptions, false); + expect(result7).toStrictEqual(false); + const result8 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(defaultItem, createOptionSelection(defaultItem.item_options![1], 1), selectedOptions, false); + expect(result8).toStrictEqual(true); + }); + + it('should return true for sold out variant with single option selected', () => { + const multipleVariationsItem = createTestItem({ setSoldOut: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationsItem.item_options![0], 1), + ]; + const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 0), selectedOptions); + expect(result).toStrictEqual(false); + const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 1), selectedOptions); + expect(result2).toStrictEqual(false); + const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 0), selectedOptions); + expect(result3).toStrictEqual(false); + const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 1), selectedOptions); + expect(result4).toStrictEqual(true); + }); + + it('should return true with inventory 0 variant with single option selected', () => { + const multipleVariationsItem = createTestItem({ setInventoryZero: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationsItem.item_options![0], 1), + ]; + const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 0), selectedOptions); + expect(result).toStrictEqual(false); + const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 1), selectedOptions); + expect(result2).toStrictEqual(false); + const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 0), selectedOptions); + expect(result3).toStrictEqual(false); + const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 1), selectedOptions); + expect(result4).toStrictEqual(true); + }); + + it('should return false with inventory above 0 variant with single option selected', () => { + const multipleVariationsItem = createTestItem({ setInventoryAboveZero: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationsItem.item_options![0], 1), + ]; + const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 0), selectedOptions); + expect(result).toStrictEqual(false); + const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 1), selectedOptions); + expect(result2).toStrictEqual(false); + const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 0), selectedOptions); + expect(result3).toStrictEqual(false); + const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 1), selectedOptions); + expect(result4).toStrictEqual(false); + }); + + it('should return true for all with sold out options selected', () => { + const multipleVariationsItem = createTestItem({ setSoldOut: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationsItem.item_options![0], 1), + createOptionSelection(multipleVariationsItem.item_options![1], 1), + ]; + const result = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 0), selectedOptions); + expect(result).toStrictEqual(false); + const result2 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![0], 1), selectedOptions); + expect(result2).toStrictEqual(true); + const result3 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 0), selectedOptions); + expect(result3).toStrictEqual(false); + const result4 = sdk.helpers.item.isOptionChoiceDisabledForSelectedOptions(multipleVariationsItem, createOptionSelection(multipleVariationsItem.item_options![1], 1), selectedOptions); + expect(result4).toStrictEqual(true); + }); }); describe('Modifier list valid', () => { - it('should return valid for all with no selections', () => { - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![0], []); - expect(result).toStrictEqual(true); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], []); - expect(result2).toStrictEqual(true); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![2], []); - expect(result3).toStrictEqual(true); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![3], []); - expect(result4).toStrictEqual(true); - }); - - it('should return valid for all with valid modifiers selections', () => { - const modifierSelections = createModifierSelection({}); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![0], modifierSelections); - expect(result).toStrictEqual(true); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); - expect(result2).toStrictEqual(true); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![2], modifierSelections); - expect(result3).toStrictEqual(true); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![3], modifierSelections); - expect(result4).toStrictEqual(true); - }); - - it('should return valid for all with valid modifiers selections using ChoiceSelection', () => { - const modifierSelections = createModifierSelection({useChoiceSelectionObject: true}); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![0], modifierSelections); - expect(result).toStrictEqual(true); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); - expect(result2).toStrictEqual(true); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![2], modifierSelections); - expect(result3).toStrictEqual(true); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![3], modifierSelections); - expect(result4).toStrictEqual(true); - }); - - it('should return false for invalid choice modifier selections', () => { - const modifierSelections = createModifierSelection({ invalidChoice: true }); - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); - expect(result).toStrictEqual(false); - const modifierSelections2 = createModifierSelection({ choiceSelectionsCount: 1 }); - const zeroChoiceModifiersItem = createTestItem({ zeroChoiceModifiers: true }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(zeroChoiceModifiersItem.modifier_lists![1], modifierSelections2); - expect(result2).toStrictEqual(false); - }); - - it('should return false for invalid choice modifier selections using ChoiceSelection', () => { - const modifierSelections = createModifierSelection({ invalidChoice: true, useChoiceSelectionObject: true }); - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); - expect(result).toStrictEqual(false); - const modifierSelections2 = createModifierSelection({ choiceSelectionsCount: 1, useChoiceSelectionObject: true }); - const zeroChoiceModifiersItem = createTestItem({ zeroChoiceModifiers: true }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(zeroChoiceModifiersItem.modifier_lists![1], modifierSelections2); - expect(result2).toStrictEqual(false); - }); - - it('should return false for sold out choice modifier selections', () => { - const soldOutChoiceModifiersItem = createTestItem({ soldOutChoiceModifier: true }); - const modifierSelections = createModifierSelection({ choiceSelectionsCount: 1 }); - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections); - expect(result).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections2); - expect(result2).toStrictEqual(false); - }); - - it('should return false for sold out choice modifier selections using ChoiceSelection', () => { - const soldOutChoiceModifiersItem = createTestItem({ soldOutChoiceModifier: true}); - const modifierSelections = createModifierSelection({ choiceSelectionsCount: 1, useChoiceSelectionObject: true }); - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections); - expect(result).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({ useChoiceSelectionObject: true }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections2); - expect(result2).toStrictEqual(false); - }); - - it('should verify choice modifier with same min/max 0', () => { - const sameChoiceMinMaxItem = createTestItem({ choiceModifierMin: 0, choiceModifierMax: 0 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], []); - expect(result).toStrictEqual(true); - const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections0); - expect(result2).toStrictEqual(true); - const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections2); - expect(result4).toStrictEqual(true); - }); - - it('should verify choice modifier with same min/max 1', () => { - const sameChoiceMinMaxItem = createTestItem({ choiceModifierMin: 1, choiceModifierMax: 1 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], []); - expect(result).toStrictEqual(false); - const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections0); - expect(result2).toStrictEqual(false); - const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections2); - expect(result4).toStrictEqual(false); - }); - - it('should verify choice modifier with min/max larger than 0', () => { - const minMaxLargerThan0Item = createTestItem({ choiceModifierMin: 1, choiceModifierMax: 2 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], []); - expect(result).toStrictEqual(false); - const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], modifierSelections0); - expect(result2).toStrictEqual(false); - const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], modifierSelections2); - expect(result4).toStrictEqual(true); - const modifierSelections3 = createModifierSelection({ choiceSelectionsCount: 3 }); - const result5 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], modifierSelections3); - expect(result5).toStrictEqual(false); - }); - - it('should verify choice modifier with min larger than 0', () => { - const minLargerThan0Item = createTestItem({ choiceModifierMin: 1, choiceModifierMax: 0 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![1], []); - expect(result).toStrictEqual(false); - const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![1], modifierSelections0); - expect(result2).toStrictEqual(false); - const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![1], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![1], modifierSelections2); - expect(result4).toStrictEqual(true); - }); - - it('should verify choice modifier with max larger than 0', () => { - const maxLargerThan0Item = createTestItem({ choiceModifierMin: 0, choiceModifierMax: 1 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![1], []); - expect(result).toStrictEqual(true); - const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![1], modifierSelections0); - expect(result2).toStrictEqual(true); - const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![1], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![1], modifierSelections2); - expect(result4).toStrictEqual(false); - }); - - it('should verify text modifier with same min/max 0', () => { - const sameChoiceMinMaxItem = createTestItem({ textModifierMin: 0, textModifierMax: 0 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], []); - expect(result).toStrictEqual(true); - const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections0); - expect(result2).toStrictEqual(true); - const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections2); - expect(result4).toStrictEqual(true); - }); - - it('should verify text modifier with same min/max 1', () => { - const sameChoiceMinMaxItem = createTestItem({ textModifierMin: 1, textModifierMax: 1 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], []); - expect(result).toStrictEqual(false); - const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections0); - expect(result2).toStrictEqual(false); - const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections2); - expect(result4).toStrictEqual(false); - }); - - it('should verify text modifier with min/max larger than 0', () => { - const minMaxLargerThan0Item = createTestItem({ textModifierMin: 1, textModifierMax: 2 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], []); - expect(result).toStrictEqual(false); - const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], modifierSelections0); - expect(result2).toStrictEqual(false); - const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], modifierSelections2); - expect(result4).toStrictEqual(true); - const modifierSelections3 = createModifierSelection({ textSelectionCount: 3 }); - const result5 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], modifierSelections3); - expect(result5).toStrictEqual(false); - }); - - it('should verify text modifier with min larger than 0', () => { - const minLargerThan0Item = createTestItem({ textModifierMin: 1, textModifierMax: 0 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![3], []); - expect(result).toStrictEqual(false); - const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![3], modifierSelections0); - expect(result2).toStrictEqual(false); - const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![3], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![3], modifierSelections2); - expect(result4).toStrictEqual(true); - }); - - it('should verify text modifier with max larger than 0', () => { - const maxLargerThan0Item = createTestItem({ textModifierMin: 0, textModifierMax: 1 }); - - const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![3], []); - expect(result).toStrictEqual(true); - const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); - const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![3], modifierSelections0); - expect(result2).toStrictEqual(true); - const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); - const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![3], modifierSelections1); - expect(result3).toStrictEqual(true); - const modifierSelections2 = createModifierSelection({}); - const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![3], modifierSelections2); - expect(result4).toStrictEqual(false); - }); + it('should return valid for all with no selections', () => { + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![0], []); + expect(result).toStrictEqual(true); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], []); + expect(result2).toStrictEqual(true); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![2], []); + expect(result3).toStrictEqual(true); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![3], []); + expect(result4).toStrictEqual(true); + }); + + it('should return valid for all with valid modifiers selections', () => { + const modifierSelections = createModifierSelection({}); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![0], modifierSelections); + expect(result).toStrictEqual(true); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); + expect(result2).toStrictEqual(true); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![2], modifierSelections); + expect(result3).toStrictEqual(true); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![3], modifierSelections); + expect(result4).toStrictEqual(true); + }); + + it('should return valid for all with valid modifiers selections using ChoiceSelection', () => { + const modifierSelections = createModifierSelection({useChoiceSelectionObject: true}); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![0], modifierSelections); + expect(result).toStrictEqual(true); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); + expect(result2).toStrictEqual(true); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![2], modifierSelections); + expect(result3).toStrictEqual(true); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![3], modifierSelections); + expect(result4).toStrictEqual(true); + }); + + it('should return false for invalid choice modifier selections', () => { + const modifierSelections = createModifierSelection({ invalidChoice: true }); + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); + expect(result).toStrictEqual(false); + const modifierSelections2 = createModifierSelection({ choiceSelectionsCount: 1 }); + const zeroChoiceModifiersItem = createTestItem({ zeroChoiceModifiers: true }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(zeroChoiceModifiersItem.modifier_lists![1], modifierSelections2); + expect(result2).toStrictEqual(false); + }); + + it('should return false for invalid choice modifier selections using ChoiceSelection', () => { + const modifierSelections = createModifierSelection({ invalidChoice: true, useChoiceSelectionObject: true }); + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); + expect(result).toStrictEqual(false); + const modifierSelections2 = createModifierSelection({ choiceSelectionsCount: 1, useChoiceSelectionObject: true }); + const zeroChoiceModifiersItem = createTestItem({ zeroChoiceModifiers: true }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(zeroChoiceModifiersItem.modifier_lists![1], modifierSelections2); + expect(result2).toStrictEqual(false); + }); + + it('should return false for sold out choice modifier selections', () => { + const soldOutChoiceModifiersItem = createTestItem({ soldOutChoiceModifier: true }); + const modifierSelections = createModifierSelection({ choiceSelectionsCount: 1 }); + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections); + expect(result).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections2); + expect(result2).toStrictEqual(false); + }); + + it('should return false for sold out choice modifier selections using ChoiceSelection', () => { + const soldOutChoiceModifiersItem = createTestItem({ soldOutChoiceModifier: true}); + const modifierSelections = createModifierSelection({ choiceSelectionsCount: 1, useChoiceSelectionObject: true }); + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections); + expect(result).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({ useChoiceSelectionObject: true }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections2); + expect(result2).toStrictEqual(false); + }); + + it('should verify choice modifier with same min/max 0', () => { + const sameChoiceMinMaxItem = createTestItem({ choiceModifierMin: 0, choiceModifierMax: 0 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], []); + expect(result).toStrictEqual(true); + const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections0); + expect(result2).toStrictEqual(true); + const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections2); + expect(result4).toStrictEqual(true); + }); + + it('should verify choice modifier with same min/max 1', () => { + const sameChoiceMinMaxItem = createTestItem({ choiceModifierMin: 1, choiceModifierMax: 1 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], []); + expect(result).toStrictEqual(false); + const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections0); + expect(result2).toStrictEqual(false); + const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![1], modifierSelections2); + expect(result4).toStrictEqual(false); + }); + + it('should verify choice modifier with min/max larger than 0', () => { + const minMaxLargerThan0Item = createTestItem({ choiceModifierMin: 1, choiceModifierMax: 2 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], []); + expect(result).toStrictEqual(false); + const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], modifierSelections0); + expect(result2).toStrictEqual(false); + const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], modifierSelections2); + expect(result4).toStrictEqual(true); + const modifierSelections3 = createModifierSelection({ choiceSelectionsCount: 3 }); + const result5 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![1], modifierSelections3); + expect(result5).toStrictEqual(false); + }); + + it('should verify choice modifier with min larger than 0', () => { + const minLargerThan0Item = createTestItem({ choiceModifierMin: 1, choiceModifierMax: 0 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![1], []); + expect(result).toStrictEqual(false); + const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![1], modifierSelections0); + expect(result2).toStrictEqual(false); + const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![1], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![1], modifierSelections2); + expect(result4).toStrictEqual(true); + }); + + it('should verify choice modifier with max larger than 0', () => { + const maxLargerThan0Item = createTestItem({ choiceModifierMin: 0, choiceModifierMax: 1 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![1], []); + expect(result).toStrictEqual(true); + const modifierSelections0 = createModifierSelection({ choiceSelectionsCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![1], modifierSelections0); + expect(result2).toStrictEqual(true); + const modifierSelections1 = createModifierSelection({ choiceSelectionsCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![1], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![1], modifierSelections2); + expect(result4).toStrictEqual(false); + }); + + it('should verify text modifier with same min/max 0', () => { + const sameChoiceMinMaxItem = createTestItem({ textModifierMin: 0, textModifierMax: 0 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], []); + expect(result).toStrictEqual(true); + const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections0); + expect(result2).toStrictEqual(true); + const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections2); + expect(result4).toStrictEqual(true); + }); + + it('should verify text modifier with same min/max 1', () => { + const sameChoiceMinMaxItem = createTestItem({ textModifierMin: 1, textModifierMax: 1 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], []); + expect(result).toStrictEqual(false); + const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections0); + expect(result2).toStrictEqual(false); + const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(sameChoiceMinMaxItem.modifier_lists![3], modifierSelections2); + expect(result4).toStrictEqual(false); + }); + + it('should verify text modifier with min/max larger than 0', () => { + const minMaxLargerThan0Item = createTestItem({ textModifierMin: 1, textModifierMax: 2 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], []); + expect(result).toStrictEqual(false); + const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], modifierSelections0); + expect(result2).toStrictEqual(false); + const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], modifierSelections2); + expect(result4).toStrictEqual(true); + const modifierSelections3 = createModifierSelection({ textSelectionCount: 3 }); + const result5 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minMaxLargerThan0Item.modifier_lists![3], modifierSelections3); + expect(result5).toStrictEqual(false); + }); + + it('should verify text modifier with min larger than 0', () => { + const minLargerThan0Item = createTestItem({ textModifierMin: 1, textModifierMax: 0 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![3], []); + expect(result).toStrictEqual(false); + const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![3], modifierSelections0); + expect(result2).toStrictEqual(false); + const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![3], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(minLargerThan0Item.modifier_lists![3], modifierSelections2); + expect(result4).toStrictEqual(true); + }); + + it('should verify text modifier with max larger than 0', () => { + const maxLargerThan0Item = createTestItem({ textModifierMin: 0, textModifierMax: 1 }); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![3], []); + expect(result).toStrictEqual(true); + const modifierSelections0 = createModifierSelection({ textSelectionCount: 0 }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![3], modifierSelections0); + expect(result2).toStrictEqual(true); + const modifierSelections1 = createModifierSelection({ textSelectionCount: 1 }); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![3], modifierSelections1); + expect(result3).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({}); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(maxLargerThan0Item.modifier_lists![3], modifierSelections2); + expect(result4).toStrictEqual(false); + }); }); describe('Get disabled option choices', () => { - it('should return empty for all options with no selections', () => { - const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], []); - expect(result).toStrictEqual([]); - const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![1], []); - expect(result2).toStrictEqual([]); - }); - - it('should return empty for other options with single option selection', () => { - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 0), - ]; - const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], selectedOptions); - expect(result).toStrictEqual([]); - const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![1], selectedOptions); - expect(result2).toStrictEqual([]); - const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], selectedOptions, false); - expect(result3).toStrictEqual([ defaultItem.item_options![0].choices[1] ]); - }); - - it('should return invalid choices for selected options with all option selections', () => { - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 0), - createOptionSelection(defaultItem.item_options![1], 0), - ]; - const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], selectedOptions); - expect(result).toStrictEqual([]); - const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![1], selectedOptions); - expect(result2).toStrictEqual([]); - const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], selectedOptions, false); - expect(result3).toStrictEqual([ defaultItem.item_options![0].choices[1] ]); - const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![1], selectedOptions, false); - expect(result4).toStrictEqual([ defaultItem.item_options![1].choices[1] ]); - }); - - it('should return invalid choice for sold out variant with single option selected', () => { - const multipleVariationsItem = createTestItem({ setSoldOut: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationsItem.item_options![0], 1), - ]; - const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions); - expect(result).toStrictEqual([]); - const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions); - expect(result2).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); - const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions, false); - expect(result3).toStrictEqual([ multipleVariationsItem.item_options![0].choices[0] ]); - const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions, false); - expect(result4).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); - }); - - it('should return invalid choice with inventory 0 variant with single option selected', () => { - const multipleVariationsItem = createTestItem({ setInventoryZero: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationsItem.item_options![0], 1), - ]; - const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions); - expect(result).toStrictEqual([]); - const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions); - expect(result2).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); - const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions, false); - expect(result3).toStrictEqual([ multipleVariationsItem.item_options![0].choices[0] ]); - const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions, false); - expect(result4).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); - }); - - it('should return empty with inventory above 0 variant with single option selected', () => { - const multipleVariationsItem = createTestItem({ setInventoryAboveZero: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationsItem.item_options![0], 1), - ]; - const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions); - expect(result).toStrictEqual([]); - const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions); - expect(result2).toStrictEqual([]); - const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions, false); - expect(result3).toStrictEqual([ multipleVariationsItem.item_options![0].choices[0] ]); - const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions, false); - expect(result4).toStrictEqual([]); - }); - - it('should return all choices with sold out options selected', () => { - const multipleVariationsItem = createTestItem({ setSoldOut: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationsItem.item_options![0], 1), - createOptionSelection(multipleVariationsItem.item_options![1], 1), - ]; - const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions); - expect(result).toStrictEqual([ multipleVariationsItem.item_options![0].choices[1] ]); - const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions); - expect(result2).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); - const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions, false); - expect(result3).toStrictEqual([ multipleVariationsItem.item_options![0].choices[0], multipleVariationsItem.item_options![0].choices[1] ]); - const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions, false); - expect(result4).toStrictEqual([ multipleVariationsItem.item_options![1].choices[0], multipleVariationsItem.item_options![1].choices[1] ]); - }); + it('should return empty for all options with no selections', () => { + const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], []); + expect(result).toStrictEqual([]); + const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![1], []); + expect(result2).toStrictEqual([]); + }); + + it('should return empty for other options with single option selection', () => { + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 0), + ]; + const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], selectedOptions); + expect(result).toStrictEqual([]); + const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![1], selectedOptions); + expect(result2).toStrictEqual([]); + const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], selectedOptions, false); + expect(result3).toStrictEqual([ defaultItem.item_options![0].choices[1] ]); + }); + + it('should return invalid choices for selected options with all option selections', () => { + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 0), + createOptionSelection(defaultItem.item_options![1], 0), + ]; + const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], selectedOptions); + expect(result).toStrictEqual([]); + const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![1], selectedOptions); + expect(result2).toStrictEqual([]); + const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![0], selectedOptions, false); + expect(result3).toStrictEqual([ defaultItem.item_options![0].choices[1] ]); + const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(defaultItem, defaultItem.item_options![1], selectedOptions, false); + expect(result4).toStrictEqual([ defaultItem.item_options![1].choices[1] ]); + }); + + it('should return invalid choice for sold out variant with single option selected', () => { + const multipleVariationsItem = createTestItem({ setSoldOut: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationsItem.item_options![0], 1), + ]; + const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions); + expect(result).toStrictEqual([]); + const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions); + expect(result2).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); + const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions, false); + expect(result3).toStrictEqual([ multipleVariationsItem.item_options![0].choices[0] ]); + const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions, false); + expect(result4).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); + }); + + it('should return invalid choice with inventory 0 variant with single option selected', () => { + const multipleVariationsItem = createTestItem({ setInventoryZero: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationsItem.item_options![0], 1), + ]; + const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions); + expect(result).toStrictEqual([]); + const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions); + expect(result2).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); + const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions, false); + expect(result3).toStrictEqual([ multipleVariationsItem.item_options![0].choices[0] ]); + const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions, false); + expect(result4).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); + }); + + it('should return empty with inventory above 0 variant with single option selected', () => { + const multipleVariationsItem = createTestItem({ setInventoryAboveZero: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationsItem.item_options![0], 1), + ]; + const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions); + expect(result).toStrictEqual([]); + const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions); + expect(result2).toStrictEqual([]); + const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions, false); + expect(result3).toStrictEqual([ multipleVariationsItem.item_options![0].choices[0] ]); + const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions, false); + expect(result4).toStrictEqual([]); + }); + + it('should return all choices with sold out options selected', () => { + const multipleVariationsItem = createTestItem({ setSoldOut: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationsItem.item_options![0], 1), + createOptionSelection(multipleVariationsItem.item_options![1], 1), + ]; + const result = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions); + expect(result).toStrictEqual([ multipleVariationsItem.item_options![0].choices[1] ]); + const result2 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions); + expect(result2).toStrictEqual([ multipleVariationsItem.item_options![1].choices[1] ]); + const result3 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![0], selectedOptions, false); + expect(result3).toStrictEqual([ multipleVariationsItem.item_options![0].choices[0], multipleVariationsItem.item_options![0].choices[1] ]); + const result4 = sdk.helpers.item.getDisabledOptionChoicesForSelectedOptions(multipleVariationsItem, multipleVariationsItem.item_options![1], selectedOptions, false); + expect(result4).toStrictEqual([ multipleVariationsItem.item_options![1].choices[0], multipleVariationsItem.item_options![1].choices[1] ]); + }); }); describe('Validate item', () => { - it('should return for valid item with variations', () => { - const selectedVariationId = defaultItem.variations[3].id; - const selectedModifiers = createModifierSelection({}); - const result = sdk.helpers.item.validateItem({ item: defaultItem, selectedVariationId, selectedModifiers }); - expect(result).toStrictEqual({ - itemId: defaultItem.id, - variationId: defaultItem.variations[3].id, - modifiers: selectedModifiers - }); - }); - - it('should return for valid item with variations and selected variation id', () => { - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 1), - createOptionSelection(defaultItem.item_options![1], 1), - ]; - const selectedModifiers = createModifierSelection({}); - const result = sdk.helpers.item.validateItem({ item: defaultItem, selectedOptions, selectedModifiers }); - expect(result).toStrictEqual({ - itemId: defaultItem.id, - variationId: defaultItem.variations[3].id, - modifiers: selectedModifiers - }); - }); - - it('should return for valid item with single variation', () => { - const singleVariationItem = createTestItem({ variationType: 'single' }); - const selectedModifiers = createModifierSelection({}); - const result = sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers }); - expect(result).toStrictEqual({ - itemId: singleVariationItem.id, - variationId: singleVariationItem.variations[0].id, - modifiers: selectedModifiers - }); - }); - - it('should return with invalid selected variation with single variation', () => { - const singleVariationItem = createTestItem({ variationType: 'single' }); - const selectedModifiers = createModifierSelection({}); - const result = sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers, selectedVariationId: 'doesnotexist' }); - expect(result).toStrictEqual({ - itemId: singleVariationItem.id, - variationId: singleVariationItem.variations[0].id, - modifiers: selectedModifiers - }); - }); - - it('should return for valid variation item with flat variations', () => { - const flatVariationItem = createTestItem({ variationType: 'flat' }); - const selectedVariationId = flatVariationItem.variations[3].id; - const selectedModifiers = createModifierSelection({}); - const result = sdk.helpers.item.validateItem({ item: flatVariationItem, selectedVariationId, selectedModifiers }); - expect(result).toStrictEqual({ - itemId: flatVariationItem.id, - variationId: selectedVariationId, - modifiers: selectedModifiers - }); - }); - - it('should throw with invalid selected variation for flat variations', () => { - const flatVariationItem = createTestItem({ variationType: 'flat' }); - const validateItemFn = () => sdk.helpers.item.validateItem({ item: flatVariationItem, selectedVariationId: 'doesnotexist' }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).variationId).toBeUndefined(); - expect((ex).quantityErrorType).toBeUndefined(); - expect((ex).modifierListIds).toBeUndefined(); - } - }); - - it('should throw with no selected variation for flat variations', () => { - const flatVariationItem = createTestItem({ variationType: 'flat' }); - const validateItemFn = () => sdk.helpers.item.validateItem({ item: flatVariationItem }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).flatVariationSelectionMissing).toBe(true); - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).variationId).toBeUndefined(); - expect((ex).modifierListIds).toBeUndefined(); - } - }); - - it('should throw missing all required options', () => { - const validateItemFn = () => sdk.helpers.item.validateItem({ item: defaultItem }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).itemOptionIds).toStrictEqual([ + it('should return for valid item with variations', () => { + const selectedVariationId = defaultItem.variations[3].id; + const selectedModifiers = createModifierSelection({}); + const result = sdk.helpers.item.validateItem({ item: defaultItem, selectedVariationId, selectedModifiers }); + expect(result).toStrictEqual({ + itemId: defaultItem.id, + variationId: defaultItem.variations[3].id, + modifiers: selectedModifiers + }); + }); + + it('should return for valid item with variations and selected variation id', () => { + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 1), + createOptionSelection(defaultItem.item_options![1], 1), + ]; + const selectedModifiers = createModifierSelection({}); + const result = sdk.helpers.item.validateItem({ item: defaultItem, selectedOptions, selectedModifiers }); + expect(result).toStrictEqual({ + itemId: defaultItem.id, + variationId: defaultItem.variations[3].id, + modifiers: selectedModifiers + }); + }); + + it('should return for valid item with single variation', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const selectedModifiers = createModifierSelection({}); + const result = sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers }); + expect(result).toStrictEqual({ + itemId: singleVariationItem.id, + variationId: singleVariationItem.variations[0].id, + modifiers: selectedModifiers + }); + }); + + it('should return with invalid selected variation with single variation', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const selectedModifiers = createModifierSelection({}); + const result = sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers, selectedVariationId: 'doesnotexist' }); + expect(result).toStrictEqual({ + itemId: singleVariationItem.id, + variationId: singleVariationItem.variations[0].id, + modifiers: selectedModifiers + }); + }); + + it('should return for valid variation item with flat variations', () => { + const flatVariationItem = createTestItem({ variationType: 'flat' }); + const selectedVariationId = flatVariationItem.variations[3].id; + const selectedModifiers = createModifierSelection({}); + const result = sdk.helpers.item.validateItem({ item: flatVariationItem, selectedVariationId, selectedModifiers }); + expect(result).toStrictEqual({ + itemId: flatVariationItem.id, + variationId: selectedVariationId, + modifiers: selectedModifiers + }); + }); + + it('should throw with invalid selected variation for flat variations', () => { + const flatVariationItem = createTestItem({ variationType: 'flat' }); + const validateItemFn = () => sdk.helpers.item.validateItem({ item: flatVariationItem, selectedVariationId: 'doesnotexist' }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).quantityErrorType).toBeUndefined(); + expect((ex).modifierListIds).toBeUndefined(); + } + }); + + it('should throw with no selected variation for flat variations', () => { + const flatVariationItem = createTestItem({ variationType: 'flat' }); + const validateItemFn = () => sdk.helpers.item.validateItem({ item: flatVariationItem }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).flatVariationSelectionMissing).toBe(true); + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).modifierListIds).toBeUndefined(); + } + }); + + it('should throw missing all required options', () => { + const validateItemFn = () => sdk.helpers.item.validateItem({ item: defaultItem }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toStrictEqual([ defaultItem.item_options![0].id, defaultItem.item_options![1].id - ]); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toBeUndefined(); - expect((ex).modifierListIds).toBeUndefined(); - } - }); - - it('should throw missed required option', () => { - const selectedOptions: OptionSelection[] = [ - createOptionSelection(defaultItem.item_options![0], 1), - ]; - const validateItemFn = () => sdk.helpers.item.validateItem({ item: defaultItem, selectedOptions }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).itemOptionIds).toStrictEqual([defaultItem.item_options![1].id]); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toBeUndefined(); - expect((ex).modifierListIds).toBeUndefined(); - } - }); - - it('should throw variation sold out for single variation', () => { - const singleVariationItem = createTestItem({ variationType: 'single', setSoldOut: true }); - const validateItemFn = () => sdk.helpers.item.validateItem({ item: singleVariationItem }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toStrictEqual(singleVariationItem.variations[0].id); - expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.SOLD_OUT); - expect((ex).modifierListIds).toBeUndefined(); - } - }); - - it('should throw variation sold out for multiple variation', () => { - const multipleVariationItem = createTestItem({ setSoldOut: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationItem.item_options![0], 1), - createOptionSelection(multipleVariationItem.item_options![1], 1), - ]; - const validateItemFn = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); - expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.SOLD_OUT); - expect((ex).modifierListIds).toBeUndefined(); - } - // Verify skipStockCheck works - const result = sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, skipStockCheck: true }); - expect(result).toStrictEqual({ - itemId: multipleVariationItem.id, - variationId: multipleVariationItem.variations[3].id, - modifiers: [] - }); - // Verify skipStockCheck still fails with quantity - const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, skipStockCheck: true, quantity: 1 }); - expect(validateItemFn2).toThrowError(); - try { - validateItemFn2(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); - expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.SOLD_OUT); - expect((ex).modifierListIds).toBeUndefined(); - } - // Verify in stock works - const selectedInStockOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationItem.item_options![0], 0), - createOptionSelection(multipleVariationItem.item_options![1], 0), - ]; - const result3 = sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions: selectedInStockOptions }); - expect(result3).toStrictEqual({ - itemId: multipleVariationItem.id, - variationId: multipleVariationItem.variations[0].id, - modifiers: [] - }); - }); - - it('should throw variation invalid quantity for single variation', () => { - const singleVariationItem = createTestItem({ variationType: 'single' }); - const validateItemFn = () => sdk.helpers.item.validateItem({ item: singleVariationItem, quantity: 0 }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toStrictEqual(singleVariationItem.variations[0].id); - expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.INVALID_QUANTITY); - expect((ex).modifierListIds).toBeUndefined(); - } - const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: singleVariationItem, quantity: -1 }); - expect(validateItemFn2).toThrowError(); - try { - validateItemFn2(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toStrictEqual(singleVariationItem.variations[0].id); - expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.INVALID_QUANTITY); - expect((ex).modifierListIds).toBeUndefined(); - } - }); - - it('should throw variation stock exceeded for multiple variation', () => { - const multipleVariationItem = createTestItem({ setInventoryAboveZero: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationItem.item_options![0], 1), - createOptionSelection(multipleVariationItem.item_options![1], 1), - ]; - const validateItemFn = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, quantity: 2 }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); - expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.STOCK_EXCEEDED); - expect((ex).modifierListIds).toBeUndefined(); - } - // Verify skipStockCheck still fails with quantity - const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, skipStockCheck: true, quantity: 2 }); - expect(validateItemFn2).toThrowError(); - try { - validateItemFn2(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); - expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.STOCK_EXCEEDED); - expect((ex).modifierListIds).toBeUndefined(); - } - // Verify stock match works - const selectedInStockOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationItem.item_options![0], 0), - createOptionSelection(multipleVariationItem.item_options![1], 0), - ]; - const result3 = sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions: selectedInStockOptions, quantity: 1 }); - expect(result3).toStrictEqual({ - itemId: multipleVariationItem.id, - variationId: multipleVariationItem.variations[0].id, - modifiers: [], - quantity: 1 - }); - }); - - it('should throw variation per order max exceeded for multiple variation', () => { - const multipleVariationItem = createTestItem({ perOrderMax: 2 }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationItem.item_options![0], 1), - createOptionSelection(multipleVariationItem.item_options![1], 1), - ]; - const validateItemFn = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, quantity: 3 }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); - expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.PER_ORDER_MAX_EXCEEDED); - expect((ex).modifierListIds).toBeUndefined(); - } - // Verify skipStockCheck still fails with quantity - const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, skipStockCheck: true, quantity: 3 }); - expect(validateItemFn2).toThrowError(); - try { - validateItemFn2(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); - expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.PER_ORDER_MAX_EXCEEDED); - expect((ex).modifierListIds).toBeUndefined(); - } - // Verify per order max match works - const selectedInStockOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationItem.item_options![0], 0), - createOptionSelection(multipleVariationItem.item_options![1], 0), - ]; - const result3 = sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions: selectedInStockOptions, quantity: 2 }); - expect(result3).toStrictEqual({ - itemId: multipleVariationItem.id, - variationId: multipleVariationItem.variations[0].id, - modifiers: [], - quantity: 2 - }); - }); - - it('should throw invalid or missing modifiers', () => { - const singleVariationItem = createTestItem({ variationType: 'single' }); - const invalidChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 3 }); - const validateItemFn = () => sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers: invalidChoiceModifiers }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toBeUndefined(); - expect((ex).modifierListIds).toStrictEqual([singleVariationItem.modifier_lists![1].id]); - } - const singleVariationItem2 = createTestItem({ variationType: 'single', choiceModifierMin: 1, choiceModifierMax: 1 }); - const missingChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 0 }); - const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: singleVariationItem2, selectedModifiers: missingChoiceModifiers }); - expect(validateItemFn2).toThrowError(); - try { - validateItemFn2(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toBeUndefined(); - expect((ex).modifierListIds).toStrictEqual([singleVariationItem.modifier_lists![1].id]); - } - const invalidTextModifiers = createModifierSelection({ textSelectionCount: 3 }); - const validateItemFn3 = () => sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers: invalidTextModifiers }); - expect(validateItemFn3).toThrowError(); - try { - validateItemFn3(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toBeUndefined(); - expect((ex).modifierListIds).toStrictEqual([singleVariationItem.modifier_lists![3].id]); - } - const singleVariationItem3 = createTestItem( - { variationType: 'single', choiceModifierMin: 1, choiceModifierMax: 1, textModifierMin: 1, textModifierMax: 1 } - ); - const validateItemFn4 = () => sdk.helpers.item.validateItem({ item: singleVariationItem3 }); - expect(validateItemFn4).toThrowError(); - try { - validateItemFn4(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toBeUndefined(); - expect((ex).modifierListIds).toStrictEqual([ + ]); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).modifierListIds).toBeUndefined(); + } + }); + + it('should throw missed required option', () => { + const selectedOptions: OptionSelection[] = [ + createOptionSelection(defaultItem.item_options![0], 1), + ]; + const validateItemFn = () => sdk.helpers.item.validateItem({ item: defaultItem, selectedOptions }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toStrictEqual([defaultItem.item_options![1].id]); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).modifierListIds).toBeUndefined(); + } + }); + + it('should throw variation sold out for single variation', () => { + const singleVariationItem = createTestItem({ variationType: 'single', setSoldOut: true }); + const validateItemFn = () => sdk.helpers.item.validateItem({ item: singleVariationItem }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toStrictEqual(singleVariationItem.variations[0].id); + expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.SOLD_OUT); + expect((ex).modifierListIds).toBeUndefined(); + } + }); + + it('should throw variation sold out for multiple variation', () => { + const multipleVariationItem = createTestItem({ setSoldOut: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationItem.item_options![0], 1), + createOptionSelection(multipleVariationItem.item_options![1], 1), + ]; + const validateItemFn = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); + expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.SOLD_OUT); + expect((ex).modifierListIds).toBeUndefined(); + } + // Verify skipStockCheck works + const result = sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, skipStockCheck: true }); + expect(result).toStrictEqual({ + itemId: multipleVariationItem.id, + variationId: multipleVariationItem.variations[3].id, + modifiers: [] + }); + // Verify skipStockCheck still fails with quantity + const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, skipStockCheck: true, quantity: 1 }); + expect(validateItemFn2).toThrowError(); + try { + validateItemFn2(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); + expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.SOLD_OUT); + expect((ex).modifierListIds).toBeUndefined(); + } + // Verify in stock works + const selectedInStockOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationItem.item_options![0], 0), + createOptionSelection(multipleVariationItem.item_options![1], 0), + ]; + const result3 = sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions: selectedInStockOptions }); + expect(result3).toStrictEqual({ + itemId: multipleVariationItem.id, + variationId: multipleVariationItem.variations[0].id, + modifiers: [] + }); + }); + + it('should throw variation invalid quantity for single variation', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const validateItemFn = () => sdk.helpers.item.validateItem({ item: singleVariationItem, quantity: 0 }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toStrictEqual(singleVariationItem.variations[0].id); + expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.INVALID_QUANTITY); + expect((ex).modifierListIds).toBeUndefined(); + } + const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: singleVariationItem, quantity: -1 }); + expect(validateItemFn2).toThrowError(); + try { + validateItemFn2(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toStrictEqual(singleVariationItem.variations[0].id); + expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.INVALID_QUANTITY); + expect((ex).modifierListIds).toBeUndefined(); + } + }); + + it('should throw variation stock exceeded for multiple variation', () => { + const multipleVariationItem = createTestItem({ setInventoryAboveZero: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationItem.item_options![0], 1), + createOptionSelection(multipleVariationItem.item_options![1], 1), + ]; + const validateItemFn = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, quantity: 2 }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); + expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.STOCK_EXCEEDED); + expect((ex).modifierListIds).toBeUndefined(); + } + // Verify skipStockCheck still fails with quantity + const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, skipStockCheck: true, quantity: 2 }); + expect(validateItemFn2).toThrowError(); + try { + validateItemFn2(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); + expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.STOCK_EXCEEDED); + expect((ex).modifierListIds).toBeUndefined(); + } + // Verify stock match works + const selectedInStockOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationItem.item_options![0], 0), + createOptionSelection(multipleVariationItem.item_options![1], 0), + ]; + const result3 = sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions: selectedInStockOptions, quantity: 1 }); + expect(result3).toStrictEqual({ + itemId: multipleVariationItem.id, + variationId: multipleVariationItem.variations[0].id, + modifiers: [], + quantity: 1 + }); + }); + + it('should throw variation per order max exceeded for multiple variation', () => { + const multipleVariationItem = createTestItem({ perOrderMax: 2 }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationItem.item_options![0], 1), + createOptionSelection(multipleVariationItem.item_options![1], 1), + ]; + const validateItemFn = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, quantity: 3 }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); + expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.PER_ORDER_MAX_EXCEEDED); + expect((ex).modifierListIds).toBeUndefined(); + } + // Verify skipStockCheck still fails with quantity + const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions, skipStockCheck: true, quantity: 3 }); + expect(validateItemFn2).toThrowError(); + try { + validateItemFn2(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toStrictEqual(multipleVariationItem.variations[3].id); + expect((ex).quantityErrorType).toStrictEqual(QuantityErrorType.PER_ORDER_MAX_EXCEEDED); + expect((ex).modifierListIds).toBeUndefined(); + } + // Verify per order max match works + const selectedInStockOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationItem.item_options![0], 0), + createOptionSelection(multipleVariationItem.item_options![1], 0), + ]; + const result3 = sdk.helpers.item.validateItem({ item: multipleVariationItem, selectedOptions: selectedInStockOptions, quantity: 2 }); + expect(result3).toStrictEqual({ + itemId: multipleVariationItem.id, + variationId: multipleVariationItem.variations[0].id, + modifiers: [], + quantity: 2 + }); + }); + + it('should throw invalid or missing modifiers', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const invalidChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 3 }); + const validateItemFn = () => sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers: invalidChoiceModifiers }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).modifierListIds).toStrictEqual([singleVariationItem.modifier_lists![1].id]); + } + const singleVariationItem2 = createTestItem({ variationType: 'single', choiceModifierMin: 1, choiceModifierMax: 1 }); + const missingChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 0 }); + const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: singleVariationItem2, selectedModifiers: missingChoiceModifiers }); + expect(validateItemFn2).toThrowError(); + try { + validateItemFn2(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).modifierListIds).toStrictEqual([singleVariationItem.modifier_lists![1].id]); + } + const invalidTextModifiers = createModifierSelection({ textSelectionCount: 3 }); + const validateItemFn3 = () => sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers: invalidTextModifiers }); + expect(validateItemFn3).toThrowError(); + try { + validateItemFn3(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).modifierListIds).toStrictEqual([singleVariationItem.modifier_lists![3].id]); + } + const singleVariationItem3 = createTestItem( + { variationType: 'single', choiceModifierMin: 1, choiceModifierMax: 1, textModifierMin: 1, textModifierMax: 1 } + ); + const validateItemFn4 = () => sdk.helpers.item.validateItem({ item: singleVariationItem3 }); + expect(validateItemFn4).toThrowError(); + try { + validateItemFn4(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).modifierListIds).toStrictEqual([ singleVariationItem.modifier_lists![1].id, singleVariationItem.modifier_lists![3].id - ]); - } - }); - - it('should throw invalid or missing modifiers using ChoiceSelection', () => { - const singleVariationItem = createTestItem({ variationType: 'single' }); - const invalidChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 3, useChoiceSelectionObject: true }); - const validateItemFn = () => sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers: invalidChoiceModifiers }); - expect(validateItemFn).toThrowError(); - try { - validateItemFn(); - } catch (ex) { - expect((ex).itemOptionIds).toBeUndefined(); - expect((ex).flatVariationSelectionMissing).toBeUndefined(); - expect((ex).variationId).toBeUndefined(); - expect((ex).modifierListIds).toStrictEqual([singleVariationItem.modifier_lists![1].id]); - } - const singleVariationItem2 = createTestItem({ variationType: 'single', choiceModifierMin: 1, choiceModifierMax: 1 }); - const missingChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 0, useChoiceSelectionObject: true }); - const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: singleVariationItem2, selectedModifiers: missingChoiceModifiers }); - expect(validateItemFn2).toThrowError(); - }); + ]); + } + }); + + it('should throw invalid or missing modifiers using ChoiceSelection', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const invalidChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 3, useChoiceSelectionObject: true }); + const validateItemFn = () => sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers: invalidChoiceModifiers }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).modifierListIds).toStrictEqual([singleVariationItem.modifier_lists![1].id]); + } + const singleVariationItem2 = createTestItem({ variationType: 'single', choiceModifierMin: 1, choiceModifierMax: 1 }); + const missingChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 0, useChoiceSelectionObject: true }); + const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: singleVariationItem2, selectedModifiers: missingChoiceModifiers }); + expect(validateItemFn2).toThrowError(); + }); }); describe('Get item price', () => { - it('should return null on invalid item', () => { - const multipleVariationItem = createTestItem({ setSoldOut: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationItem.item_options![0], 1), - createOptionSelection(multipleVariationItem.item_options![1], 1), - ]; - const result = sdk.helpers.item.getItemPrice({ item: multipleVariationItem, selectedOptions }); - expect(result).toStrictEqual(null); - }); - - it('should return price on sold out item with skipStockCheck', () => { - const multipleVariationItem = createTestItem({ setSoldOut: true }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(multipleVariationItem.item_options![0], 1), - createOptionSelection(multipleVariationItem.item_options![1], 1), - ]; - const result = sdk.helpers.item.getItemPrice({ item: multipleVariationItem, selectedOptions, skipStockCheck: true }); - expect(result).toStrictEqual({ - regular: { - amount: multipleVariationItem.variations[3].price.regular.amount, - formatted: '', - currency: multipleVariationItem.variations[3].price.regular.currency - }, - sale: { - amount: multipleVariationItem.variations[3].price.sale.amount, - formatted: '', - currency: multipleVariationItem.variations[3].price.sale.currency - } - }); - }); - - it('should return price on invalid modifiers with skipModifierCheck', () => { - const invalidModifierItem = createTestItem({ choiceModifierMin: 1 }); - const selectedOptions: OptionSelection[] = [ - createOptionSelection(invalidModifierItem.item_options![0], 1), - createOptionSelection(invalidModifierItem.item_options![1], 1), - ]; - const result = sdk.helpers.item.getItemPrice({ item: invalidModifierItem, selectedOptions, skipModifierCheck: true }); - expect(result).toStrictEqual({ - regular: { - amount: invalidModifierItem.variations[3].price.regular.amount, - formatted: '', - currency: invalidModifierItem.variations[3].price.regular.currency - }, - sale: { - amount: invalidModifierItem.variations[3].price.sale.amount, - formatted: '', - currency: invalidModifierItem.variations[3].price.sale.currency - } - }); - }); - - it('should return price on modifiers', () => { - const singleVariationItem = createTestItem({ variationType: 'single' }); - const modifierSelections = createModifierSelection({}); - const modifierCost = singleVariationItem.modifier_lists![0].modifiers![0].price_money.amount + it('should return null on invalid item', () => { + const multipleVariationItem = createTestItem({ setSoldOut: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationItem.item_options![0], 1), + createOptionSelection(multipleVariationItem.item_options![1], 1), + ]; + const result = sdk.helpers.item.getItemPrice({ item: multipleVariationItem, selectedOptions }); + expect(result).toStrictEqual(null); + }); + + it('should return price on sold out item with skipStockCheck', () => { + const multipleVariationItem = createTestItem({ setSoldOut: true }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(multipleVariationItem.item_options![0], 1), + createOptionSelection(multipleVariationItem.item_options![1], 1), + ]; + const result = sdk.helpers.item.getItemPrice({ item: multipleVariationItem, selectedOptions, skipStockCheck: true }); + expect(result).toStrictEqual({ + regular: { + amount: multipleVariationItem.variations[3].price.regular.amount, + formatted: '', + currency: multipleVariationItem.variations[3].price.regular.currency + }, + sale: { + amount: multipleVariationItem.variations[3].price.sale.amount, + formatted: '', + currency: multipleVariationItem.variations[3].price.sale.currency + } + }); + }); + + it('should return price on invalid modifiers with skipModifierCheck', () => { + const invalidModifierItem = createTestItem({ choiceModifierMin: 1 }); + const selectedOptions: OptionSelection[] = [ + createOptionSelection(invalidModifierItem.item_options![0], 1), + createOptionSelection(invalidModifierItem.item_options![1], 1), + ]; + const result = sdk.helpers.item.getItemPrice({ item: invalidModifierItem, selectedOptions, skipModifierCheck: true }); + expect(result).toStrictEqual({ + regular: { + amount: invalidModifierItem.variations[3].price.regular.amount, + formatted: '', + currency: invalidModifierItem.variations[3].price.regular.currency + }, + sale: { + amount: invalidModifierItem.variations[3].price.sale.amount, + formatted: '', + currency: invalidModifierItem.variations[3].price.sale.currency + } + }); + }); + + it('should return price on modifiers', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const modifierSelections = createModifierSelection({}); + const modifierCost = singleVariationItem.modifier_lists![0].modifiers![0].price_money.amount + singleVariationItem.modifier_lists![1].modifiers![0].price_money.amount + singleVariationItem.modifier_lists![1].modifiers![1].price_money.amount; - const result = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections }); - expect(result).toStrictEqual({ - regular: { - amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, - formatted: '', - currency: singleVariationItem.variations[0].price.regular.currency - }, - sale: { - amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, - formatted: '', - currency: singleVariationItem.variations[0].price.sale.currency - } - }); - }); - - it('should return price on modifiers using ChoiceSelection', () => { - const singleVariationItem = createTestItem({ variationType: 'single' }); - const modifierSelections = createModifierSelection({ useChoiceSelectionObject: true, useMultipleChoiceSelectionQuantity: true }); - const modifierCost = singleVariationItem.modifier_lists![0].modifiers![0].price_money.amount + const result = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections }); + expect(result).toStrictEqual({ + regular: { + amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, + formatted: '', + currency: singleVariationItem.variations[0].price.regular.currency + }, + sale: { + amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, + formatted: '', + currency: singleVariationItem.variations[0].price.sale.currency + } + }); + }); + + it('should return price on modifiers using ChoiceSelection', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const modifierSelections = createModifierSelection({ useChoiceSelectionObject: true, useMultipleChoiceSelectionQuantity: true }); + const modifierCost = singleVariationItem.modifier_lists![0].modifiers![0].price_money.amount + (singleVariationItem.modifier_lists![1].modifiers![0].price_money.amount * 2) + singleVariationItem.modifier_lists![1].modifiers![1].price_money.amount; - const result = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections }); - expect(result).toStrictEqual({ - regular: { - amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, - formatted: '', - currency: singleVariationItem.variations[0].price.regular.currency - }, - sale: { - amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, - formatted: '', - currency: singleVariationItem.variations[0].price.sale.currency - } - }); - }); - - it('should return price on flat variation', () => { - const flatItem = createTestItem({ variationType: 'flat' }); - const selectedVariationId = flatItem.variations[3].id; - const result = sdk.helpers.item.getItemPrice({ item: flatItem, selectedVariationId }); - expect(result).toStrictEqual({ - regular: { - amount: flatItem.variations[3].price.regular.amount, - formatted: '', - currency: flatItem.variations[3].price.regular.currency - }, - sale: { - amount: flatItem.variations[3].price.sale.amount, - formatted: '', - currency: flatItem.variations[3].price.sale.currency - } - }); - }); - - it('should return formatted price on modifiers', () => { - const singleVariationItem = createTestItem({ variationType: 'single' }); - const modifierSelections = createModifierSelection({}); - const modifierCost = singleVariationItem.modifier_lists![0].modifiers![0].price_money.amount + const result = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections }); + expect(result).toStrictEqual({ + regular: { + amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, + formatted: '', + currency: singleVariationItem.variations[0].price.regular.currency + }, + sale: { + amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, + formatted: '', + currency: singleVariationItem.variations[0].price.sale.currency + } + }); + }); + + it('should return price on flat variation', () => { + const flatItem = createTestItem({ variationType: 'flat' }); + const selectedVariationId = flatItem.variations[3].id; + const result = sdk.helpers.item.getItemPrice({ item: flatItem, selectedVariationId }); + expect(result).toStrictEqual({ + regular: { + amount: flatItem.variations[3].price.regular.amount, + formatted: '', + currency: flatItem.variations[3].price.regular.currency + }, + sale: { + amount: flatItem.variations[3].price.sale.amount, + formatted: '', + currency: flatItem.variations[3].price.sale.currency + } + }); + }); + + it('should return formatted price on modifiers', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const modifierSelections = createModifierSelection({}); + const modifierCost = singleVariationItem.modifier_lists![0].modifiers![0].price_money.amount + singleVariationItem.modifier_lists![1].modifiers![0].price_money.amount + singleVariationItem.modifier_lists![1].modifiers![1].price_money.amount; - const result = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections, formattedLocale: 'en-US' }); - const formatter = Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }); - expect(result).toStrictEqual({ - regular: { - amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, - formatted: formatter.format(15), - currency: singleVariationItem.variations[0].price.regular.currency - }, - sale: { - amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, - formatted: formatter.format(14), - currency: singleVariationItem.variations[0].price.sale.currency - } - }); - const result2 = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections, formattedLocale: 'fr-FR' }); - const formatter2 = Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'USD' }); - expect(result2).toStrictEqual({ - regular: { - amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, - formatted: formatter2.format(15), - currency: singleVariationItem.variations[0].price.regular.currency - }, - sale: { - amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, - formatted: formatter2.format(14), - currency: singleVariationItem.variations[0].price.sale.currency - } - }); - // Test fallback to en-US - const result3 = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections, formattedLocale: 'ar_BH' }); - expect(result3).toStrictEqual({ - regular: { - amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, - formatted: formatter.format(15), - currency: singleVariationItem.variations[0].price.regular.currency - }, - sale: { - amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, - formatted: formatter.format(14), - currency: singleVariationItem.variations[0].price.sale.currency - } - }); - }); - - it('should return formatted price on modifiers with other currencies', () => { - const currencyList = [ - { - currency: 'CAD', - formattedRegular: 15, - formattedSale: 14 - }, - { - currency: 'AUD', - formattedRegular: 15, - formattedSale: 14 - }, - { - currency: 'JPY', - formattedRegular: 1500, - formattedSale: 1400 - }, - { - currency: 'GBP', - formattedRegular: 15, - formattedSale: 14 - }, - { - currency: 'EUR', - formattedRegular: 15, - formattedSale: 14 - }, - // Not supported by Square, but additional tests - { - currency: 'IDR', - formattedRegular: 15, - formattedSale: 14 - }, - { - currency: 'BHD', - formattedRegular: 1.5, - formattedSale: 1.4 - } - ]; - - currencyList.forEach(c => { - const singleVariationItem = createTestItem({ variationType: 'single', currency: c.currency }); - const modifierSelections = createModifierSelection({}); - const modifierCost = singleVariationItem.modifier_lists![0].modifiers![0].price_money.amount + const result = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections, formattedLocale: 'en-US' }); + const formatter = Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }); + expect(result).toStrictEqual({ + regular: { + amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, + formatted: formatter.format(15), + currency: singleVariationItem.variations[0].price.regular.currency + }, + sale: { + amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, + formatted: formatter.format(14), + currency: singleVariationItem.variations[0].price.sale.currency + } + }); + const result2 = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections, formattedLocale: 'fr-FR' }); + const formatter2 = Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'USD' }); + expect(result2).toStrictEqual({ + regular: { + amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, + formatted: formatter2.format(15), + currency: singleVariationItem.variations[0].price.regular.currency + }, + sale: { + amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, + formatted: formatter2.format(14), + currency: singleVariationItem.variations[0].price.sale.currency + } + }); + // Test fallback to en-US + const result3 = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections, formattedLocale: 'ar_BH' }); + expect(result3).toStrictEqual({ + regular: { + amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, + formatted: formatter.format(15), + currency: singleVariationItem.variations[0].price.regular.currency + }, + sale: { + amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, + formatted: formatter.format(14), + currency: singleVariationItem.variations[0].price.sale.currency + } + }); + }); + + it('should return formatted price on modifiers with other currencies', () => { + const currencyList = [ + { + currency: 'CAD', + formattedRegular: 15, + formattedSale: 14 + }, + { + currency: 'AUD', + formattedRegular: 15, + formattedSale: 14 + }, + { + currency: 'JPY', + formattedRegular: 1500, + formattedSale: 1400 + }, + { + currency: 'GBP', + formattedRegular: 15, + formattedSale: 14 + }, + { + currency: 'EUR', + formattedRegular: 15, + formattedSale: 14 + }, + // Not supported by Square, but additional tests + { + currency: 'IDR', + formattedRegular: 15, + formattedSale: 14 + }, + { + currency: 'BHD', + formattedRegular: 1.5, + formattedSale: 1.4 + } + ]; + + currencyList.forEach(c => { + const singleVariationItem = createTestItem({ variationType: 'single', currency: c.currency }); + const modifierSelections = createModifierSelection({}); + const modifierCost = singleVariationItem.modifier_lists![0].modifiers![0].price_money.amount + singleVariationItem.modifier_lists![1].modifiers![0].price_money.amount + singleVariationItem.modifier_lists![1].modifiers![1].price_money.amount; - const result = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections, formattedLocale: 'en-US' }); - const formatter = Intl.NumberFormat('en-US', { style: 'currency', currency: c.currency }); - expect(result).toStrictEqual({ - regular: { - amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, - formatted: formatter.format(c.formattedRegular), - currency: singleVariationItem.variations[0].price.regular.currency - }, - sale: { - amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, - formatted: formatter.format(c.formattedSale), - currency: singleVariationItem.variations[0].price.sale.currency - } - }); - }); - }); + const result = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections, formattedLocale: 'en-US' }); + const formatter = Intl.NumberFormat('en-US', { style: 'currency', currency: c.currency }); + expect(result).toStrictEqual({ + regular: { + amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, + formatted: formatter.format(c.formattedRegular), + currency: singleVariationItem.variations[0].price.regular.currency + }, + sale: { + amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, + formatted: formatter.format(c.formattedSale), + currency: singleVariationItem.variations[0].price.sale.currency + } + }); + }); + }); }); describe('Get item sold out', () => { - it('should return false', () => { - const result = sdk.helpers.item.isItemSoldOut(defaultItem); - expect(result).toStrictEqual(false); - const flatItem = createTestItem({ variationType: 'flat' }); - const result2 = sdk.helpers.item.isItemSoldOut(flatItem); - expect(result2).toStrictEqual(false); - const singleItem = createTestItem({ variationType: 'single' }); - const result3 = sdk.helpers.item.isItemSoldOut(singleItem); - expect(result3).toStrictEqual(false); - const multiSoldOutSingleItem = createTestItem({ setSoldOut: true }); - const result4 = sdk.helpers.item.isItemSoldOut(multiSoldOutSingleItem); - expect(result4).toStrictEqual(false); - const flatSoldOutSingleItem = createTestItem({ variationType: 'flat', setSoldOut: true }); - const result5 = sdk.helpers.item.isItemSoldOut(flatSoldOutSingleItem); - expect(result5).toStrictEqual(false); - const multiInventoryZeroSingleItem = createTestItem({ setInventoryZero: true }); - const result6 = sdk.helpers.item.isItemSoldOut(multiInventoryZeroSingleItem); - expect(result6).toStrictEqual(false); - const flatInventoryZeroSingleItem = createTestItem({ variationType: 'flat', setInventoryZero: true }); - const result7 = sdk.helpers.item.isItemSoldOut(flatInventoryZeroSingleItem); - expect(result7).toStrictEqual(false); - const multiInventoryAboveZeroSingleItem = createTestItem({ setInventoryAboveZero: true }); - const result8 = sdk.helpers.item.isItemSoldOut(multiInventoryAboveZeroSingleItem); - expect(result8).toStrictEqual(false); - const flatInventoryAboveZeroSingleItem = createTestItem({ variationType: 'flat', setInventoryAboveZero: true }); - const result9 = sdk.helpers.item.isItemSoldOut(flatInventoryAboveZeroSingleItem); - expect(result9).toStrictEqual(false); - }); - - it('should return true', () => { - const multiSoldOutItem = createTestItem({ setAllSoldOut: true }); - const result = sdk.helpers.item.isItemSoldOut(multiSoldOutItem); - expect(result).toStrictEqual(true); - const flatSoldOutItem = createTestItem({ variationType: 'flat', setAllSoldOut: true }); - const result2 = sdk.helpers.item.isItemSoldOut(flatSoldOutItem); - expect(result2).toStrictEqual(true); - const singleSoldOutItem = createTestItem({ variationType: 'single', setAllSoldOut: true }); - const result3 = sdk.helpers.item.isItemSoldOut(singleSoldOutItem); - expect(result3).toStrictEqual(true); - const multiInventoryZeroItem = createTestItem({ setAllInventoryZero: true }); - const result4 = sdk.helpers.item.isItemSoldOut(multiInventoryZeroItem); - expect(result4).toStrictEqual(true); - const flatInventoryZeroItem = createTestItem({ variationType: 'flat', setAllInventoryZero: true }); - const result5 = sdk.helpers.item.isItemSoldOut(flatInventoryZeroItem); - expect(result5).toStrictEqual(true); - const singleInventoryZeroItem = createTestItem({ variationType: 'single', setAllInventoryZero: true }); - const result6 = sdk.helpers.item.isItemSoldOut(singleInventoryZeroItem); - expect(result6).toStrictEqual(true); - }); + it('should return false', () => { + const result = sdk.helpers.item.isItemSoldOut(defaultItem); + expect(result).toStrictEqual(false); + const flatItem = createTestItem({ variationType: 'flat' }); + const result2 = sdk.helpers.item.isItemSoldOut(flatItem); + expect(result2).toStrictEqual(false); + const singleItem = createTestItem({ variationType: 'single' }); + const result3 = sdk.helpers.item.isItemSoldOut(singleItem); + expect(result3).toStrictEqual(false); + const multiSoldOutSingleItem = createTestItem({ setSoldOut: true }); + const result4 = sdk.helpers.item.isItemSoldOut(multiSoldOutSingleItem); + expect(result4).toStrictEqual(false); + const flatSoldOutSingleItem = createTestItem({ variationType: 'flat', setSoldOut: true }); + const result5 = sdk.helpers.item.isItemSoldOut(flatSoldOutSingleItem); + expect(result5).toStrictEqual(false); + const multiInventoryZeroSingleItem = createTestItem({ setInventoryZero: true }); + const result6 = sdk.helpers.item.isItemSoldOut(multiInventoryZeroSingleItem); + expect(result6).toStrictEqual(false); + const flatInventoryZeroSingleItem = createTestItem({ variationType: 'flat', setInventoryZero: true }); + const result7 = sdk.helpers.item.isItemSoldOut(flatInventoryZeroSingleItem); + expect(result7).toStrictEqual(false); + const multiInventoryAboveZeroSingleItem = createTestItem({ setInventoryAboveZero: true }); + const result8 = sdk.helpers.item.isItemSoldOut(multiInventoryAboveZeroSingleItem); + expect(result8).toStrictEqual(false); + const flatInventoryAboveZeroSingleItem = createTestItem({ variationType: 'flat', setInventoryAboveZero: true }); + const result9 = sdk.helpers.item.isItemSoldOut(flatInventoryAboveZeroSingleItem); + expect(result9).toStrictEqual(false); + }); + + it('should return true', () => { + const multiSoldOutItem = createTestItem({ setAllSoldOut: true }); + const result = sdk.helpers.item.isItemSoldOut(multiSoldOutItem); + expect(result).toStrictEqual(true); + const flatSoldOutItem = createTestItem({ variationType: 'flat', setAllSoldOut: true }); + const result2 = sdk.helpers.item.isItemSoldOut(flatSoldOutItem); + expect(result2).toStrictEqual(true); + const singleSoldOutItem = createTestItem({ variationType: 'single', setAllSoldOut: true }); + const result3 = sdk.helpers.item.isItemSoldOut(singleSoldOutItem); + expect(result3).toStrictEqual(true); + const multiInventoryZeroItem = createTestItem({ setAllInventoryZero: true }); + const result4 = sdk.helpers.item.isItemSoldOut(multiInventoryZeroItem); + expect(result4).toStrictEqual(true); + const flatInventoryZeroItem = createTestItem({ variationType: 'flat', setAllInventoryZero: true }); + const result5 = sdk.helpers.item.isItemSoldOut(flatInventoryZeroItem); + expect(result5).toStrictEqual(true); + const singleInventoryZeroItem = createTestItem({ variationType: 'single', setAllInventoryZero: true }); + const result6 = sdk.helpers.item.isItemSoldOut(singleInventoryZeroItem); + expect(result6).toStrictEqual(true); + }); }); describe('Is event item in the past', () => { - it('should return false for non event item', () => { - const result = sdk.helpers.item.isEventItemInThePast(defaultItem); - expect(result).toStrictEqual(false); - }); - - it('should test event in the future for pacific time', () => { - const date = '2023-07-18T22:00:00-07:00'; - vi.setSystemTime(new Date(date)); - - const item = createTestItem({ - squareOnlineType: 'EVENT', - itemTypeDetailsEndDate: '2023-07-19', - itemTypeDetailsEndTime: '1:00 AM', - itemTypeDetailsOffset: '-04:00' - }); - const result = sdk.helpers.item.isEventItemInThePast(item); - expect(result).toStrictEqual(true); - const item2 = createTestItem({ - squareOnlineType: 'EVENT', - itemTypeDetailsEndDate: '2023-07-19', - itemTypeDetailsEndTime: '12:59 AM', - itemTypeDetailsOffset: '-04:00' - }); - const result2 = sdk.helpers.item.isEventItemInThePast(item2); - expect(result2).toStrictEqual(true); - const item3 = createTestItem({ - squareOnlineType: 'EVENT', - itemTypeDetailsEndDate: '2023-07-19', - itemTypeDetailsEndTime: '1:01 AM', - itemTypeDetailsOffset: '-04:00' - }); - const result3 = sdk.helpers.item.isEventItemInThePast(item3); - expect(result3).toStrictEqual(false); - }); - - it('should test event in the future for eastern time', () => { - const date = '2023-07-18T16:00:00-04:00'; - vi.setSystemTime(new Date(date)); - - const item = createTestItem({ - squareOnlineType: 'EVENT', - itemTypeDetailsEndDate: '2023-07-18', - itemTypeDetailsEndTime: '4:00 PM', - itemTypeDetailsOffset: '-04:00' - }); - const result = sdk.helpers.item.isEventItemInThePast(item); - expect(result).toStrictEqual(true); - const item2 = createTestItem({ - squareOnlineType: 'EVENT', - itemTypeDetailsEndDate: '2023-07-18', - itemTypeDetailsEndTime: '3:59 PM', - itemTypeDetailsOffset: '-04:00' - }); - const result2 = sdk.helpers.item.isEventItemInThePast(item2); - expect(result2).toStrictEqual(true); - const item3 = createTestItem({ - squareOnlineType: 'EVENT', - itemTypeDetailsEndDate: '2023-07-18', - itemTypeDetailsEndTime: '4:01 PM', - itemTypeDetailsOffset: '-04:00' - }); - const result3 = sdk.helpers.item.isEventItemInThePast(item3); - expect(result3).toStrictEqual(false); - }); - - it('should test event in the future for eastern time at noon', () => { - const date = '2023-07-18T12:00:00-04:00'; - vi.setSystemTime(new Date(date)); - - const item = createTestItem({ - squareOnlineType: 'EVENT', - itemTypeDetailsEndDate: '2023-07-18', - itemTypeDetailsEndTime: '12:00 PM', - itemTypeDetailsOffset: '-04:00' - }); - const result = sdk.helpers.item.isEventItemInThePast(item); - expect(result).toStrictEqual(true); - const item2 = createTestItem({ - squareOnlineType: 'EVENT', - itemTypeDetailsEndDate: '2023-07-18', - itemTypeDetailsEndTime: '11:59 AM', - itemTypeDetailsOffset: '-04:00' - }); - const result2 = sdk.helpers.item.isEventItemInThePast(item2); - expect(result2).toStrictEqual(true); - const item3 = createTestItem({ - squareOnlineType: 'EVENT', - itemTypeDetailsEndDate: '2023-07-18', - itemTypeDetailsEndTime: '12:01 PM', - itemTypeDetailsOffset: '-04:00' - }); - const result3 = sdk.helpers.item.isEventItemInThePast(item3); - expect(result3).toStrictEqual(false); - }); + it('should return false for non event item', () => { + const result = sdk.helpers.item.isEventItemInThePast(defaultItem); + expect(result).toStrictEqual(false); + }); + + it('should test event in the future for pacific time', () => { + const date = '2023-07-18T22:00:00-07:00'; + vi.setSystemTime(new Date(date)); + + const item = createTestItem({ + squareOnlineType: 'EVENT', + itemTypeDetailsEndDate: '2023-07-19', + itemTypeDetailsEndTime: '1:00 AM', + itemTypeDetailsOffset: '-04:00' + }); + const result = sdk.helpers.item.isEventItemInThePast(item); + expect(result).toStrictEqual(true); + const item2 = createTestItem({ + squareOnlineType: 'EVENT', + itemTypeDetailsEndDate: '2023-07-19', + itemTypeDetailsEndTime: '12:59 AM', + itemTypeDetailsOffset: '-04:00' + }); + const result2 = sdk.helpers.item.isEventItemInThePast(item2); + expect(result2).toStrictEqual(true); + const item3 = createTestItem({ + squareOnlineType: 'EVENT', + itemTypeDetailsEndDate: '2023-07-19', + itemTypeDetailsEndTime: '1:01 AM', + itemTypeDetailsOffset: '-04:00' + }); + const result3 = sdk.helpers.item.isEventItemInThePast(item3); + expect(result3).toStrictEqual(false); + }); + + it('should test event in the future for eastern time', () => { + const date = '2023-07-18T16:00:00-04:00'; + vi.setSystemTime(new Date(date)); + + const item = createTestItem({ + squareOnlineType: 'EVENT', + itemTypeDetailsEndDate: '2023-07-18', + itemTypeDetailsEndTime: '4:00 PM', + itemTypeDetailsOffset: '-04:00' + }); + const result = sdk.helpers.item.isEventItemInThePast(item); + expect(result).toStrictEqual(true); + const item2 = createTestItem({ + squareOnlineType: 'EVENT', + itemTypeDetailsEndDate: '2023-07-18', + itemTypeDetailsEndTime: '3:59 PM', + itemTypeDetailsOffset: '-04:00' + }); + const result2 = sdk.helpers.item.isEventItemInThePast(item2); + expect(result2).toStrictEqual(true); + const item3 = createTestItem({ + squareOnlineType: 'EVENT', + itemTypeDetailsEndDate: '2023-07-18', + itemTypeDetailsEndTime: '4:01 PM', + itemTypeDetailsOffset: '-04:00' + }); + const result3 = sdk.helpers.item.isEventItemInThePast(item3); + expect(result3).toStrictEqual(false); + }); + + it('should test event in the future for eastern time at noon', () => { + const date = '2023-07-18T12:00:00-04:00'; + vi.setSystemTime(new Date(date)); + + const item = createTestItem({ + squareOnlineType: 'EVENT', + itemTypeDetailsEndDate: '2023-07-18', + itemTypeDetailsEndTime: '12:00 PM', + itemTypeDetailsOffset: '-04:00' + }); + const result = sdk.helpers.item.isEventItemInThePast(item); + expect(result).toStrictEqual(true); + const item2 = createTestItem({ + squareOnlineType: 'EVENT', + itemTypeDetailsEndDate: '2023-07-18', + itemTypeDetailsEndTime: '11:59 AM', + itemTypeDetailsOffset: '-04:00' + }); + const result2 = sdk.helpers.item.isEventItemInThePast(item2); + expect(result2).toStrictEqual(true); + const item3 = createTestItem({ + squareOnlineType: 'EVENT', + itemTypeDetailsEndDate: '2023-07-18', + itemTypeDetailsEndTime: '12:01 PM', + itemTypeDetailsOffset: '-04:00' + }); + const result3 = sdk.helpers.item.isEventItemInThePast(item3); + expect(result3).toStrictEqual(false); + }); }); describe('Is preorder item cutoff in the past', () => { - it('should return false for no preorder', () => { - const result = sdk.helpers.item.isPreorderItemCutoffInThePast(defaultItem); - expect(result).toStrictEqual(false); - }); - - it('should test preorder cutoff times in pacific', () => { - const date = '2023-07-18T22:00:00-07:00'; - vi.setSystemTime(new Date(date)); - - const item = createTestItem({ - preorderingCutoff: '2023-07-19T01:00:00-04:00' - }); - const result = sdk.helpers.item.isPreorderItemCutoffInThePast(item); - expect(result).toStrictEqual(true); - const item2 = createTestItem({ - preorderingCutoff: '2023-07-19T00:59:00-04:00' - }); - const result2 = sdk.helpers.item.isPreorderItemCutoffInThePast(item2); - expect(result2).toStrictEqual(true); - const item3 = createTestItem({ - preorderingCutoff: '2023-07-19T01:01:01-04:00' - }); - const result3 = sdk.helpers.item.isPreorderItemCutoffInThePast(item3); - expect(result3).toStrictEqual(false); - }); - - it('should test preorder cutoff times in eastern', () => { - const date = '2023-07-18T16:00:00-04:00'; - vi.setSystemTime(new Date(date)); - - const item = createTestItem({ - preorderingCutoff: '2023-07-18T16:00:00-04:00' - }); - const result = sdk.helpers.item.isPreorderItemCutoffInThePast(item); - expect(result).toStrictEqual(true); - const item2 = createTestItem({ - preorderingCutoff: '2023-07-18T15:59:00-04:00' - }); - const result2 = sdk.helpers.item.isPreorderItemCutoffInThePast(item2); - expect(result2).toStrictEqual(true); - const item3 = createTestItem({ - preorderingCutoff: '2023-07-18T16:01:00-04:00' - }); - const result3 = sdk.helpers.item.isPreorderItemCutoffInThePast(item3); - expect(result3).toStrictEqual(false); - }); + it('should return false for no preorder', () => { + const result = sdk.helpers.item.isPreorderItemCutoffInThePast(defaultItem); + expect(result).toStrictEqual(false); + }); + + it('should test preorder cutoff times in pacific', () => { + const date = '2023-07-18T22:00:00-07:00'; + vi.setSystemTime(new Date(date)); + + const item = createTestItem({ + preorderingCutoff: '2023-07-19T01:00:00-04:00' + }); + const result = sdk.helpers.item.isPreorderItemCutoffInThePast(item); + expect(result).toStrictEqual(true); + const item2 = createTestItem({ + preorderingCutoff: '2023-07-19T00:59:00-04:00' + }); + const result2 = sdk.helpers.item.isPreorderItemCutoffInThePast(item2); + expect(result2).toStrictEqual(true); + const item3 = createTestItem({ + preorderingCutoff: '2023-07-19T01:01:01-04:00' + }); + const result3 = sdk.helpers.item.isPreorderItemCutoffInThePast(item3); + expect(result3).toStrictEqual(false); + }); + + it('should test preorder cutoff times in eastern', () => { + const date = '2023-07-18T16:00:00-04:00'; + vi.setSystemTime(new Date(date)); + + const item = createTestItem({ + preorderingCutoff: '2023-07-18T16:00:00-04:00' + }); + const result = sdk.helpers.item.isPreorderItemCutoffInThePast(item); + expect(result).toStrictEqual(true); + const item2 = createTestItem({ + preorderingCutoff: '2023-07-18T15:59:00-04:00' + }); + const result2 = sdk.helpers.item.isPreorderItemCutoffInThePast(item2); + expect(result2).toStrictEqual(true); + const item3 = createTestItem({ + preorderingCutoff: '2023-07-18T16:01:00-04:00' + }); + const result3 = sdk.helpers.item.isPreorderItemCutoffInThePast(item3); + expect(result3).toStrictEqual(false); + }); }); describe('Is item prep time correctly parsed', () => { - it.each([ - { value: 'PT20M', expected: { value: 20, unit: 'M', is_time: true } }, - { value: 'PT60M', expected: { value: 60, unit: 'M', is_time: true } }, - { value: 'PT90M', expected: { value: 90, unit: 'M', is_time: true } }, - { value: 'PT120M', expected: { value: 120, unit: 'M', is_time: true } }, - { value: 'PT1H', expected: { value: 1, unit: 'H', is_time: true } }, - { value: 'PT2H', expected: { value: 2, unit: 'H', is_time: true } }, - { value: 'P1D', expected: { value: 1, unit: 'D', is_time: false } }, - { value: 'P2D', expected: { value: 2, unit: 'D', is_time: false } }, - { value: 'P7D', expected: { value: 7, unit: 'D', is_time: false } }, - { value: 'P1W', expected: { value: 1, unit: 'W', is_time: false } }, - { value: 'P3W', expected: { value: 3, unit: 'W', is_time: false } }, - { value: 'P8W', expected: { value: 8, unit: 'W', is_time: false } }, - { value: 'P1M', expected: { value: 1, unit: 'M', is_time: false } }, - { value: 'P3M', expected: { value: 3, unit: 'M', is_time: false } }, - { value: 'P1Y', expected: { value: 1, unit: 'Y', is_time: false } }, - { value: 'P3Y', expected: { value: 3, unit: 'Y', is_time: false } }, - { value: 'P3', expected: null }, - { value: 'PD', expected: null }, - { value: 'PT3', expected: null }, - { value: 'PTD', expected: null }, - ])('should return $expected for value $value', ({ value, expected }) => { - expect(sdk.helpers.item.parsePrepTime(value)).toStrictEqual(expected); - }); + it.each([ + { value: 'PT20M', expected: { value: 20, unit: 'M', is_time: true } }, + { value: 'PT60M', expected: { value: 60, unit: 'M', is_time: true } }, + { value: 'PT90M', expected: { value: 90, unit: 'M', is_time: true } }, + { value: 'PT120M', expected: { value: 120, unit: 'M', is_time: true } }, + { value: 'PT1H', expected: { value: 1, unit: 'H', is_time: true } }, + { value: 'PT2H', expected: { value: 2, unit: 'H', is_time: true } }, + { value: 'P1D', expected: { value: 1, unit: 'D', is_time: false } }, + { value: 'P2D', expected: { value: 2, unit: 'D', is_time: false } }, + { value: 'P7D', expected: { value: 7, unit: 'D', is_time: false } }, + { value: 'P1W', expected: { value: 1, unit: 'W', is_time: false } }, + { value: 'P3W', expected: { value: 3, unit: 'W', is_time: false } }, + { value: 'P8W', expected: { value: 8, unit: 'W', is_time: false } }, + { value: 'P1M', expected: { value: 1, unit: 'M', is_time: false } }, + { value: 'P3M', expected: { value: 3, unit: 'M', is_time: false } }, + { value: 'P1Y', expected: { value: 1, unit: 'Y', is_time: false } }, + { value: 'P3Y', expected: { value: 3, unit: 'Y', is_time: false } }, + { value: 'P3', expected: null }, + { value: 'PD', expected: null }, + { value: 'PT3', expected: null }, + { value: 'PTD', expected: null }, + ])('should return $expected for value $value', ({ value, expected }) => { + expect(sdk.helpers.item.parsePrepTime(value)).toStrictEqual(expected); + }); }); diff --git a/test/helpers.location.test.ts b/test/helpers.location.test.ts index 27c31f2..c3a8308 100644 --- a/test/helpers.location.test.ts +++ b/test/helpers.location.test.ts @@ -1,973 +1,973 @@ import type { - Location as LocationResource, + Location as LocationResource, } from '../src/types/helpers/location'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { getTestSiteThemeSDK } from './helpers'; function createTestLocation(): LocationResource { - const money = { - amount: 500, - currency: 'USD', - formatted: '$5.00', - }; - - const location: LocationResource = { - 'DELIVERY': { - 'enabled': true, - 'areas': [ - { - 'id': 'test_id', - 'type': 'zipcode', - 'radius_center': { - 'latitude': 0, - 'longitude': 0 - }, - 'radius_length': { - 'unit': 'mile', - 'length': 1 - }, - 'zipcode': '00000' - } - ], - 'couriers': [ - { - 'id': 'test_id', - 'name': 'seller', - 'is_choose_for_me_selection': false - } - ], - 'estimated_max_duration': { - 'in_minutes': 10, - 'rfc3339_interval': 'PT10M', - }, - 'estimated_min_duration': { - 'in_minutes': 10, - 'rfc3339_interval': 'PT10M', - }, - 'odd_fee_maximum_enabled': false, - 'odd_fee_maximum': money, - 'no_contact_enabled': false, - 'order_subtotal_minimum': money, - 'service_fee_money': money, - 'max_fee': money, - 'min_fee': money, - 'service_fee_percentage': 3, - 'free_fulfillment_conditions': [], - 'prep_time_duration': { - 'in_minutes': 10, - 'rfc3339_interval': 'PT10M', - }, - 'schedule_delivery_enabled': false, - 'hours': { - 'SUN': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'MON': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'TUE': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'WED': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'THU': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'FRI': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'SAT': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ] - } - }, - 'PICKUP': { - 'enabled': true, - 'curbside_pickup_enabled': false, - 'instructions': '', - 'order_subtotal_minimum': money, - 'prep_time_duration': { - 'in_minutes': 10, - 'rfc3339_interval': 'PT10M', - }, - 'schedule_pickup': { - 'enabled': false, - 'max_days': 90 - }, - 'timeslot_type': 'SPECIFIC', - 'minimum_days_required': 0, - 'auto_assign_time': true, - 'max_orders_per_window': 10, - 'cutoff_time': 'test', - 'hours': { - 'SUN': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'MON': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'TUE': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'WED': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'THU': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'FRI': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'SAT': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ] - } - }, - 'id': 'test_id', - 'resource_type': 'LOCATION', - 'square_online_id': 'test_id', - 'name': 'Test', - 'description': '', - 'country': 'US', - 'timezone': { - 'name': 'UTC', // Location timezone must be the same as the system timezone - 'offset_string': '+00:00', - 'offset_minutes': 0 - }, - 'service_fee': 3, - 'address': { - 'name': 'Location Tset', - 'country': '', - 'locality': '', - 'postal_code': '11211', - 'address_line_1': '', - 'administrative_district_level_1': '', - 'phone': '', - 'email': '' - }, - 'coordinates': { - 'latitude': 0, - 'longitude': 0 - }, - 'created_at': 'test', - 'is_shipping_location': false, - 'square_business_hours': { - 'SUN': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'MON': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'TUE': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'WED': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'THU': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'FRI': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ], - 'SAT': [ - { - 'open': '12:00:00', - 'close': '15:00:00', - 'open_formatted': '12:00 PM', - 'close_formatted': '3:00 PM' - }, - { - 'open': '16:00:00', - 'close': '22:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '10:00 PM' - } - ] - }, - 'fulfillment': { - 'no_eta_instructions': '', - 'no_eta_short_instructions': '' - } - }; - - return location; + const money = { + amount: 500, + currency: 'USD', + formatted: '$5.00', + }; + + const location: LocationResource = { + 'DELIVERY': { + 'enabled': true, + 'areas': [ + { + 'id': 'test_id', + 'type': 'zipcode', + 'radius_center': { + 'latitude': 0, + 'longitude': 0 + }, + 'radius_length': { + 'unit': 'mile', + 'length': 1 + }, + 'zipcode': '00000' + } + ], + 'couriers': [ + { + 'id': 'test_id', + 'name': 'seller', + 'is_choose_for_me_selection': false + } + ], + 'estimated_max_duration': { + 'in_minutes': 10, + 'rfc3339_interval': 'PT10M', + }, + 'estimated_min_duration': { + 'in_minutes': 10, + 'rfc3339_interval': 'PT10M', + }, + 'odd_fee_maximum_enabled': false, + 'odd_fee_maximum': money, + 'no_contact_enabled': false, + 'order_subtotal_minimum': money, + 'service_fee_money': money, + 'max_fee': money, + 'min_fee': money, + 'service_fee_percentage': 3, + 'free_fulfillment_conditions': [], + 'prep_time_duration': { + 'in_minutes': 10, + 'rfc3339_interval': 'PT10M', + }, + 'schedule_delivery_enabled': false, + 'hours': { + 'SUN': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'MON': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'TUE': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'WED': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'THU': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'FRI': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'SAT': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ] + } + }, + 'PICKUP': { + 'enabled': true, + 'curbside_pickup_enabled': false, + 'instructions': '', + 'order_subtotal_minimum': money, + 'prep_time_duration': { + 'in_minutes': 10, + 'rfc3339_interval': 'PT10M', + }, + 'schedule_pickup': { + 'enabled': false, + 'max_days': 90 + }, + 'timeslot_type': 'SPECIFIC', + 'minimum_days_required': 0, + 'auto_assign_time': true, + 'max_orders_per_window': 10, + 'cutoff_time': 'test', + 'hours': { + 'SUN': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'MON': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'TUE': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'WED': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'THU': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'FRI': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'SAT': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ] + } + }, + 'id': 'test_id', + 'resource_type': 'LOCATION', + 'square_online_id': 'test_id', + 'name': 'Test', + 'description': '', + 'country': 'US', + 'timezone': { + 'name': 'UTC', // Location timezone must be the same as the system timezone + 'offset_string': '+00:00', + 'offset_minutes': 0 + }, + 'service_fee': 3, + 'address': { + 'name': 'Location Tset', + 'country': '', + 'locality': '', + 'postal_code': '11211', + 'address_line_1': '', + 'administrative_district_level_1': '', + 'phone': '', + 'email': '' + }, + 'coordinates': { + 'latitude': 0, + 'longitude': 0 + }, + 'created_at': 'test', + 'is_shipping_location': false, + 'square_business_hours': { + 'SUN': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'MON': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'TUE': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'WED': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'THU': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'FRI': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ], + 'SAT': [ + { + 'open': '12:00:00', + 'close': '15:00:00', + 'open_formatted': '12:00 PM', + 'close_formatted': '3:00 PM' + }, + { + 'open': '16:00:00', + 'close': '22:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '10:00 PM' + } + ] + }, + 'fulfillment': { + 'no_eta_instructions': '', + 'no_eta_short_instructions': '' + } + }; + + return location; } const sdk = getTestSiteThemeSDK(); describe('Location fulfillment status day and time variations', () => { - beforeEach(() => { - // tell vitest we use mocked time - vi.useFakeTimers(); - }); - - afterEach(() => { - // restoring date after each test run - vi.useRealTimers(); - }); - - it('Should be currently open, open until 3pm', () => { - const location = createTestLocation(); - // Monday January 1, 2024 1:00:00 pm - const date = new Date(2024, 0, 1, 13); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '3:00 pm', - day: 'Monday', - }); - }); - - it('Should open later today, opens 4pm', () => { - const location = createTestLocation(); - // Monday January 1, 2024 3:30:00 pm - const date = new Date(2024, 0, 1, 15, 30); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, - time: '4:00 pm', - day: 'Monday', - }); - }); - - it('Should open another day, opens next available time Tuesday 12:00 pm', () => { - const location = createTestLocation(); - // Monday January 1, 2024 11:00:00 pm - const date = new Date(2024, 0, 1, 23, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.OPENS_ANOTHER_DAY, - time: '12:00 pm', - day: 'Tuesday', - }); - }); - - it('Should open later today, multiple future open intervals, should open at 12pm', () => { - const location = createTestLocation(); - // Monday January 1, 2024 10:00:00 am - const date = new Date(2024, 0, 1, 10, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, - time: '12:00 pm', - day: 'Monday', - }); - }); - - it('Should be currently open, but open/close interval spans between days (4:00pm - 4am)', () => { - const location = createTestLocation(); - location['PICKUP'].hours['MON'] = [ - { - 'open': '16:00:00', - 'close': '24:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '12:00 AM' - }, - ]; - location['PICKUP'].hours['TUE'] = [ - { - 'open': '00:00:00', - 'close': '04:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '4:00 AM' - }, - ]; - // Monday January 1, 2024 10:00:00 pm - const date = new Date(2024, 0, 1, 20, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '4:00 am', - day: 'Tuesday', - }); - }); - - it('Should be currently open, but open/close interval spans between days (4:00pm - 12:30am)', () => { - const location = createTestLocation(); - location['PICKUP'].hours['MON'] = [ - { - 'open': '16:00:00', - 'close': '24:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '12:00 AM' - }, - ]; - location['PICKUP'].hours['TUE'] = [ - { - 'open': '00:00:00', - 'close': '00:30:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:30 AM' - }, - ]; - // Monday January 1, 2024 10:00:00 am - const date = new Date(2024, 0, 1, 20); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '12:30 am', - day: 'Tuesday', - }); - }); - - it('Should be currently open, but open/close interval spans between days (4:00pm - 12:30am), current time is exactly midnight', () => { - const location = createTestLocation(); - location['PICKUP'].hours['MON'] = [ - { - 'open': '16:00:00', - 'close': '24:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '12:00 AM' - }, - ]; - location['PICKUP'].hours['TUE'] = [ - { - 'open': '00:00:00', - 'close': '00:30:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:30 AM' - }, - ]; - // Tuesday January 2, 2024 12:00:00 am - // time set as monday at 24 hours - const date = new Date(2024, 0, 1, 24, 0, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '12:30 am', - day: 'Tuesday', - }); - }); - - it('Should be currently open, but open/close interval spans between days (4:00pm - 12:30am), current time is exactly midnight', () => { - const location = createTestLocation(); - location['PICKUP'].hours['MON'] = [ - { - 'open': '16:00:00', - 'close': '24:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '12:00 AM' - }, - ]; - location['PICKUP'].hours['TUE'] = [ - { - 'open': '00:00:00', - 'close': '00:30:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:30 AM' - }, - ]; - // Tuesday January 2, 2024 12:00:00 am - // time set as tuesday at 0 hours - const date = new Date(2024, 0, 2, 0, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '12:30 am', - day: 'Tuesday', - }); - }); - - it('Should be currently open, interval is between 12:01 and 12:59 am', () => { - const location = createTestLocation(); - location['PICKUP'].hours['MON'] = [ - { - 'open': '00:01:00', - 'close': '00:59:00', - 'open_formatted': '12:01 AM', - 'close_formatted': '12:59 AM' - }, - ]; - // Monday January 1, 2024 12:10:00 am - const date = new Date(2024, 0, 1, 0, 10); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '12:59 am', - day: 'Monday', - }); - }); - - it('No fulfillment hours exist on any days', () => { - const location = createTestLocation(); - location['PICKUP'].hours['MON'] = []; - location['PICKUP'].hours['TUE'] = []; - location['PICKUP'].hours['WED'] = []; - location['PICKUP'].hours['THU'] = []; - location['PICKUP'].hours['FRI'] = []; - location['PICKUP'].hours['SAT'] = []; - location['PICKUP'].hours['SUN'] = []; - - // Monday January 1, 2024 10:00:00 am - const date = new Date(2024, 0, 1, 20, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual(null); - }); - - it('Pickup is available 24/7', () => { - const location = createTestLocation(); - location['PICKUP'].hours['MON'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['TUE'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['WED'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['THU'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['FRI'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['SAT'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['SUN'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - - // Monday January 1, 2024 10:00:00 am - const date = new Date(2024, 0, 1, 10); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '', - day: '', - }); - }); - - it('Pickup is available 24 hours a day, but closed on weekends', () => { - const location = createTestLocation(); - location['PICKUP'].hours['MON'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['TUE'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['WED'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['THU'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - location['PICKUP'].hours['FRI'] = [ - { - 'open': '00:00:00', - 'close': '24:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '12:00 AM' - } - ]; - - // Monday January 1, 2024 10:00:00 am - const date = new Date(2024, 0, 1, 10); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '12:00 am', - day: 'Saturday', - }); - }); - - it('For a location in a different timezone, should be currently open, open until 3pm', () => { - const location = createTestLocation(); - location.timezone = { - 'name': 'America/Los_Angeles', // Location timezone must be the same as the system timezone - 'offset_string': '-08:00', - 'offset_minutes': -480 - }; - // Monday January 1, 2024 1:00:00 pm (America/Los_Angeles timezone) - const date = new Date(2024, 0, 1, 21); // UTC is 9pm - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '3:00 pm', - day: 'Monday', - }); - }); - - it('For a location in a different timezone, should open later today, opens 4pm', () => { - const location = createTestLocation(); - location.timezone = { - 'name': 'America/Los_Angeles', // Location timezone must be the same as the system timezone - 'offset_string': '-08:00', - 'offset_minutes': -480 - }; - // Monday January 1, 2024 3:30:00 pm (America/Los_Angeles timezone) - const date = new Date(2024, 0, 1, 23, 30); // UTC is 11:30pm - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, - time: '4:00 pm', - day: 'Monday', - }); - }); - - it('For a location in a different timezone, should open another day, opens next available time Tuesday 12:00 pm', () => { - const location = createTestLocation(); - location.timezone = { - 'name': 'America/Los_Angeles', // Location timezone must be the same as the system timezone - 'offset_string': '-08:00', - 'offset_minutes': -480 - }; - // Monday January 1, 2024 11:00:00 pm (America/Los_Angeles timezone) - const date = new Date(2024, 0, 2, 7, 0); // UTC is 7am next day - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.OPENS_ANOTHER_DAY, - time: '12:00 pm', - day: 'Tuesday', - }); - }); + beforeEach(() => { + // tell vitest we use mocked time + vi.useFakeTimers(); + }); + + afterEach(() => { + // restoring date after each test run + vi.useRealTimers(); + }); + + it('Should be currently open, open until 3pm', () => { + const location = createTestLocation(); + // Monday January 1, 2024 1:00:00 pm + const date = new Date(2024, 0, 1, 13); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '3:00 pm', + day: 'Monday', + }); + }); + + it('Should open later today, opens 4pm', () => { + const location = createTestLocation(); + // Monday January 1, 2024 3:30:00 pm + const date = new Date(2024, 0, 1, 15, 30); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, + time: '4:00 pm', + day: 'Monday', + }); + }); + + it('Should open another day, opens next available time Tuesday 12:00 pm', () => { + const location = createTestLocation(); + // Monday January 1, 2024 11:00:00 pm + const date = new Date(2024, 0, 1, 23, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.OPENS_ANOTHER_DAY, + time: '12:00 pm', + day: 'Tuesday', + }); + }); + + it('Should open later today, multiple future open intervals, should open at 12pm', () => { + const location = createTestLocation(); + // Monday January 1, 2024 10:00:00 am + const date = new Date(2024, 0, 1, 10, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, + time: '12:00 pm', + day: 'Monday', + }); + }); + + it('Should be currently open, but open/close interval spans between days (4:00pm - 4am)', () => { + const location = createTestLocation(); + location['PICKUP'].hours['MON'] = [ + { + 'open': '16:00:00', + 'close': '24:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '12:00 AM' + }, + ]; + location['PICKUP'].hours['TUE'] = [ + { + 'open': '00:00:00', + 'close': '04:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '4:00 AM' + }, + ]; + // Monday January 1, 2024 10:00:00 pm + const date = new Date(2024, 0, 1, 20, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '4:00 am', + day: 'Tuesday', + }); + }); + + it('Should be currently open, but open/close interval spans between days (4:00pm - 12:30am)', () => { + const location = createTestLocation(); + location['PICKUP'].hours['MON'] = [ + { + 'open': '16:00:00', + 'close': '24:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '12:00 AM' + }, + ]; + location['PICKUP'].hours['TUE'] = [ + { + 'open': '00:00:00', + 'close': '00:30:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:30 AM' + }, + ]; + // Monday January 1, 2024 10:00:00 am + const date = new Date(2024, 0, 1, 20); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '12:30 am', + day: 'Tuesday', + }); + }); + + it('Should be currently open, but open/close interval spans between days (4:00pm - 12:30am), current time is exactly midnight', () => { + const location = createTestLocation(); + location['PICKUP'].hours['MON'] = [ + { + 'open': '16:00:00', + 'close': '24:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '12:00 AM' + }, + ]; + location['PICKUP'].hours['TUE'] = [ + { + 'open': '00:00:00', + 'close': '00:30:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:30 AM' + }, + ]; + // Tuesday January 2, 2024 12:00:00 am + // time set as monday at 24 hours + const date = new Date(2024, 0, 1, 24, 0, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '12:30 am', + day: 'Tuesday', + }); + }); + + it('Should be currently open, but open/close interval spans between days (4:00pm - 12:30am), current time is exactly midnight', () => { + const location = createTestLocation(); + location['PICKUP'].hours['MON'] = [ + { + 'open': '16:00:00', + 'close': '24:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '12:00 AM' + }, + ]; + location['PICKUP'].hours['TUE'] = [ + { + 'open': '00:00:00', + 'close': '00:30:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:30 AM' + }, + ]; + // Tuesday January 2, 2024 12:00:00 am + // time set as tuesday at 0 hours + const date = new Date(2024, 0, 2, 0, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '12:30 am', + day: 'Tuesday', + }); + }); + + it('Should be currently open, interval is between 12:01 and 12:59 am', () => { + const location = createTestLocation(); + location['PICKUP'].hours['MON'] = [ + { + 'open': '00:01:00', + 'close': '00:59:00', + 'open_formatted': '12:01 AM', + 'close_formatted': '12:59 AM' + }, + ]; + // Monday January 1, 2024 12:10:00 am + const date = new Date(2024, 0, 1, 0, 10); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '12:59 am', + day: 'Monday', + }); + }); + + it('No fulfillment hours exist on any days', () => { + const location = createTestLocation(); + location['PICKUP'].hours['MON'] = []; + location['PICKUP'].hours['TUE'] = []; + location['PICKUP'].hours['WED'] = []; + location['PICKUP'].hours['THU'] = []; + location['PICKUP'].hours['FRI'] = []; + location['PICKUP'].hours['SAT'] = []; + location['PICKUP'].hours['SUN'] = []; + + // Monday January 1, 2024 10:00:00 am + const date = new Date(2024, 0, 1, 20, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual(null); + }); + + it('Pickup is available 24/7', () => { + const location = createTestLocation(); + location['PICKUP'].hours['MON'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['TUE'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['WED'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['THU'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['FRI'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['SAT'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['SUN'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + + // Monday January 1, 2024 10:00:00 am + const date = new Date(2024, 0, 1, 10); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '', + day: '', + }); + }); + + it('Pickup is available 24 hours a day, but closed on weekends', () => { + const location = createTestLocation(); + location['PICKUP'].hours['MON'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['TUE'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['WED'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['THU'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + location['PICKUP'].hours['FRI'] = [ + { + 'open': '00:00:00', + 'close': '24:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '12:00 AM' + } + ]; + + // Monday January 1, 2024 10:00:00 am + const date = new Date(2024, 0, 1, 10); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '12:00 am', + day: 'Saturday', + }); + }); + + it('For a location in a different timezone, should be currently open, open until 3pm', () => { + const location = createTestLocation(); + location.timezone = { + 'name': 'America/Los_Angeles', // Location timezone must be the same as the system timezone + 'offset_string': '-08:00', + 'offset_minutes': -480 + }; + // Monday January 1, 2024 1:00:00 pm (America/Los_Angeles timezone) + const date = new Date(2024, 0, 1, 21); // UTC is 9pm + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '3:00 pm', + day: 'Monday', + }); + }); + + it('For a location in a different timezone, should open later today, opens 4pm', () => { + const location = createTestLocation(); + location.timezone = { + 'name': 'America/Los_Angeles', // Location timezone must be the same as the system timezone + 'offset_string': '-08:00', + 'offset_minutes': -480 + }; + // Monday January 1, 2024 3:30:00 pm (America/Los_Angeles timezone) + const date = new Date(2024, 0, 1, 23, 30); // UTC is 11:30pm + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, + time: '4:00 pm', + day: 'Monday', + }); + }); + + it('For a location in a different timezone, should open another day, opens next available time Tuesday 12:00 pm', () => { + const location = createTestLocation(); + location.timezone = { + 'name': 'America/Los_Angeles', // Location timezone must be the same as the system timezone + 'offset_string': '-08:00', + 'offset_minutes': -480 + }; + // Monday January 1, 2024 11:00:00 pm (America/Los_Angeles timezone) + const date = new Date(2024, 0, 2, 7, 0); // UTC is 7am next day + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.OPENS_ANOTHER_DAY, + time: '12:00 pm', + day: 'Tuesday', + }); + }); }); describe('Location business hours status day and time variations', () => { - beforeEach(() => { - // tell vitest we use mocked time - vi.useFakeTimers(); - }); - - afterEach(() => { - // restoring date after each test run - vi.useRealTimers(); - }); - - it('Should be currently open, open until 3pm', () => { - const location = createTestLocation(); - // Monday January 1, 2024 1:00:00 pm - const date = new Date(2024, 0, 1, 13); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '3:00 pm', - day: 'Monday', - }); - }); - - it('Should open later today, opens 4pm', () => { - const location = createTestLocation(); - // Monday January 1, 2024 3:30:00 pm - const date = new Date(2024, 0, 1, 15, 30); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, - time: '4:00 pm', - day: 'Monday', - }); - }); - - it('Should open another day, opens next available time Tuesday 12:00 pm', () => { - const location = createTestLocation(); - // Monday January 1, 2024 11:00:00 pm - const date = new Date(2024, 0, 1, 23, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.OPENS_ANOTHER_DAY, - time: '12:00 pm', - day: 'Tuesday', - }); - }); - - it('Should open later today, multiple future open intervals, should open at 12pm', () => { - const location = createTestLocation(); - // Monday January 1, 2024 10:00:00 am - const date = new Date(2024, 0, 1, 10, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, - time: '12:00 pm', - day: 'Monday', - }); - }); - - it('Should be currently open, but open/close interval spans between days (4:00pm - 4am)', () => { - const location = createTestLocation(); - location.square_business_hours['MON'] = [ - { - 'open': '16:00:00', - 'close': '24:00:00', - 'open_formatted': '4:00 PM', - 'close_formatted': '12:00 AM' - }, - ]; - location.square_business_hours['TUE'] = [ - { - 'open': '00:00:00', - 'close': '04:00:00', - 'open_formatted': '12:00 AM', - 'close_formatted': '4:00 AM' - }, - ]; - // Monday January 1, 2024 10:00:00 pm - const date = new Date(2024, 0, 1, 20, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); - expect(result).toStrictEqual({ - status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, - time: '4:00 am', - day: 'Tuesday', - }); - }); - - it('No fulfillment hours exist on any days', () => { - const location = createTestLocation(); - location.square_business_hours['MON'] = []; - location.square_business_hours['TUE'] = []; - location.square_business_hours['WED'] = []; - location.square_business_hours['THU'] = []; - location.square_business_hours['FRI'] = []; - location.square_business_hours['SAT'] = []; - location.square_business_hours['SUN'] = []; - - // Monday January 1, 2024 10:00:00 am - const date = new Date(2024, 0, 1, 20, 0); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); - expect(result).toStrictEqual(null); - }); + beforeEach(() => { + // tell vitest we use mocked time + vi.useFakeTimers(); + }); + + afterEach(() => { + // restoring date after each test run + vi.useRealTimers(); + }); + + it('Should be currently open, open until 3pm', () => { + const location = createTestLocation(); + // Monday January 1, 2024 1:00:00 pm + const date = new Date(2024, 0, 1, 13); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '3:00 pm', + day: 'Monday', + }); + }); + + it('Should open later today, opens 4pm', () => { + const location = createTestLocation(); + // Monday January 1, 2024 3:30:00 pm + const date = new Date(2024, 0, 1, 15, 30); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, + time: '4:00 pm', + day: 'Monday', + }); + }); + + it('Should open another day, opens next available time Tuesday 12:00 pm', () => { + const location = createTestLocation(); + // Monday January 1, 2024 11:00:00 pm + const date = new Date(2024, 0, 1, 23, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.OPENS_ANOTHER_DAY, + time: '12:00 pm', + day: 'Tuesday', + }); + }); + + it('Should open later today, multiple future open intervals, should open at 12pm', () => { + const location = createTestLocation(); + // Monday January 1, 2024 10:00:00 am + const date = new Date(2024, 0, 1, 10, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY, + time: '12:00 pm', + day: 'Monday', + }); + }); + + it('Should be currently open, but open/close interval spans between days (4:00pm - 4am)', () => { + const location = createTestLocation(); + location.square_business_hours['MON'] = [ + { + 'open': '16:00:00', + 'close': '24:00:00', + 'open_formatted': '4:00 PM', + 'close_formatted': '12:00 AM' + }, + ]; + location.square_business_hours['TUE'] = [ + { + 'open': '00:00:00', + 'close': '04:00:00', + 'open_formatted': '12:00 AM', + 'close_formatted': '4:00 AM' + }, + ]; + // Monday January 1, 2024 10:00:00 pm + const date = new Date(2024, 0, 1, 20, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); + expect(result).toStrictEqual({ + status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN, + time: '4:00 am', + day: 'Tuesday', + }); + }); + + it('No fulfillment hours exist on any days', () => { + const location = createTestLocation(); + location.square_business_hours['MON'] = []; + location.square_business_hours['TUE'] = []; + location.square_business_hours['WED'] = []; + location.square_business_hours['THU'] = []; + location.square_business_hours['FRI'] = []; + location.square_business_hours['SAT'] = []; + location.square_business_hours['SUN'] = []; + + // Monday January 1, 2024 10:00:00 am + const date = new Date(2024, 0, 1, 20, 0); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getLocationBusinessHoursOpenStatusDayAndTime(location, 'en-US'); + expect(result).toStrictEqual(null); + }); }); describe('Check open intervals for today', () => { - beforeEach(() => { - // tell vitest we use mocked time - vi.useFakeTimers(); - }); - - afterEach(() => { - // restoring date after each test run - vi.useRealTimers(); - }); - - it('Should get correct intervals array', () => { - const location = createTestLocation(); - // Monday January 1, 2024 1:00:00 pm - const date = new Date(2024, 0, 1, 13); - vi.setSystemTime(date); - - const result = sdk.helpers.location.getOpenIntervalsForToday( - 'en-US', - 'UTC', - location['PICKUP'].hours - ); - expect(result).toStrictEqual(location['PICKUP'].hours['MON']); - }); + beforeEach(() => { + // tell vitest we use mocked time + vi.useFakeTimers(); + }); + + afterEach(() => { + // restoring date after each test run + vi.useRealTimers(); + }); + + it('Should get correct intervals array', () => { + const location = createTestLocation(); + // Monday January 1, 2024 1:00:00 pm + const date = new Date(2024, 0, 1, 13); + vi.setSystemTime(date); + + const result = sdk.helpers.location.getOpenIntervalsForToday( + 'en-US', + 'UTC', + location['PICKUP'].hours + ); + expect(result).toStrictEqual(location['PICKUP'].hours['MON']); + }); }); diff --git a/test/helpers.money.test.ts b/test/helpers.money.test.ts index 243a527..ff426ed 100644 --- a/test/helpers.money.test.ts +++ b/test/helpers.money.test.ts @@ -4,94 +4,94 @@ import { getTestSiteThemeSDK } from './helpers'; const sdk = getTestSiteThemeSDK(); describe('convertFloatToSubunits', () => { - it('should properly convert', () => { - const currencyList = [ - { - currency: 'CAD', - float: 15.0, - subunits: 1500 - }, - { - currency: 'AUD', - float: 15.0, - subunits: 1500 - }, - { - currency: 'JPY', - float: 15, - subunits: 15 - }, - { - currency: 'GBP', - float: 15.0, - subunits: 1500 - }, - { - currency: 'EUR', - float: 15.0, - subunits: 1500 - }, - // Not supported by Square, but additional tests - { - currency: 'IDR', - float: 15.0, - subunits: 1500 - }, - { - currency: 'BHD', - float: 15, - subunits: 15000 - } - ]; + it('should properly convert', () => { + const currencyList = [ + { + currency: 'CAD', + float: 15.0, + subunits: 1500 + }, + { + currency: 'AUD', + float: 15.0, + subunits: 1500 + }, + { + currency: 'JPY', + float: 15, + subunits: 15 + }, + { + currency: 'GBP', + float: 15.0, + subunits: 1500 + }, + { + currency: 'EUR', + float: 15.0, + subunits: 1500 + }, + // Not supported by Square, but additional tests + { + currency: 'IDR', + float: 15.0, + subunits: 1500 + }, + { + currency: 'BHD', + float: 15, + subunits: 15000 + } + ]; - currencyList.forEach(({ currency, float, subunits }) => { - expect(sdk.helpers.money.convertFloatToSubunits(float, currency)).toBe(subunits); - }); - }); + currencyList.forEach(({ currency, float, subunits }) => { + expect(sdk.helpers.money.convertFloatToSubunits(float, currency)).toBe(subunits); + }); + }); }); describe('convertSubunitsToFloat', () => { - it('should properly convert', () => { - const currencyList = [ - { - currency: 'CAD', - float: 15.0, - subunits: 1500 - }, - { - currency: 'AUD', - float: 15.0, - subunits: 1500 - }, - { - currency: 'JPY', - float: 15, - subunits: 15 - }, - { - currency: 'GBP', - float: 15.0, - subunits: 1500 - }, - { - currency: 'EUR', - float: 15.0, - subunits: 1500 - }, - // Not supported by Square, but additional tests - { - currency: 'IDR', - float: 15.0, - subunits: 1500 - }, - { - currency: 'BHD', - float: 15, - subunits: 15000 - } - ]; + it('should properly convert', () => { + const currencyList = [ + { + currency: 'CAD', + float: 15.0, + subunits: 1500 + }, + { + currency: 'AUD', + float: 15.0, + subunits: 1500 + }, + { + currency: 'JPY', + float: 15, + subunits: 15 + }, + { + currency: 'GBP', + float: 15.0, + subunits: 1500 + }, + { + currency: 'EUR', + float: 15.0, + subunits: 1500 + }, + // Not supported by Square, but additional tests + { + currency: 'IDR', + float: 15.0, + subunits: 1500 + }, + { + currency: 'BHD', + float: 15, + subunits: 15000 + } + ]; - currencyList.forEach(({ currency, float, subunits }) => { - expect(sdk.helpers.money.convertSubunitsToFloat(subunits, currency)).toBe(float); - }); - }); + currencyList.forEach(({ currency, float, subunits }) => { + expect(sdk.helpers.money.convertSubunitsToFloat(subunits, currency)).toBe(float); + }); + }); }); \ No newline at end of file diff --git a/test/helpers.ts b/test/helpers.ts index aa4d8ca..7631dcc 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -3,41 +3,41 @@ import SiteThemeSDK from '../src'; import type LooseObject from '../src/types/looseobject'; export function getTestSiteThemeSDK(overrides: object = {}): SiteThemeSDK { - const defaults = { - userId: 1, - siteId: '1', - cmsSiteId: 'fakecmssiteid', - cdnDomain: 'testcdn.editmysite.com', - merchantId: 'merchant-id-123' - }; - return new SiteThemeSDK({...defaults, ...overrides}); + const defaults = { + userId: 1, + siteId: '1', + cmsSiteId: 'fakecmssiteid', + cdnDomain: 'testcdn.editmysite.com', + merchantId: 'merchant-id-123' + }; + return new SiteThemeSDK({...defaults, ...overrides}); } export const STATUS_TEXT = 'status'; export function createFetchResponse(data: LooseObject|null, ok: boolean, status: number, redirectUrl = ''): Response { - return { - ok, - headers: { - append: vi.fn(), - delete: vi.fn(), - get: vi.fn(), - has: vi.fn(), - set: vi.fn(), - forEach: vi.fn() - }, - redirected: !!redirectUrl, - status: status, - statusText: STATUS_TEXT, - type: 'default', - url: redirectUrl, - clone: vi.fn(), - body: null, - bodyUsed: false, - arrayBuffer: vi.fn(), - blob: vi.fn(), - formData: vi.fn(), - text: vi.fn(), - json: () => data !== null ? - Promise.resolve(data) : - Promise.reject(new SyntaxError('Unexpected end of JSON input')) - }; + return { + ok, + headers: { + append: vi.fn(), + delete: vi.fn(), + get: vi.fn(), + has: vi.fn(), + set: vi.fn(), + forEach: vi.fn() + }, + redirected: !!redirectUrl, + status: status, + statusText: STATUS_TEXT, + type: 'default', + url: redirectUrl, + clone: vi.fn(), + body: null, + bodyUsed: false, + arrayBuffer: vi.fn(), + blob: vi.fn(), + formData: vi.fn(), + text: vi.fn(), + json: () => data !== null ? + Promise.resolve(data) : + Promise.reject(new SyntaxError('Unexpected end of JSON input')) + }; } diff --git a/test/index.test.ts b/test/index.test.ts index 5753d13..5999cc6 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -4,43 +4,43 @@ import { describe, expect, it } from 'vitest'; import { getTestSiteThemeSDK } from './helpers'; describe('init error states', () => { - it('should fail on missing user id', () => { - expect(() => { - getTestSiteThemeSDK({ - userId: 0, - }); - }).toThrow(new Error('missing user id')); - }); + it('should fail on missing user id', () => { + expect(() => { + getTestSiteThemeSDK({ + userId: 0, + }); + }).toThrow(new Error('missing user id')); + }); - it('should fail on missing site id', () => { - expect(() => { - getTestSiteThemeSDK({ - siteId: '', - }); - }).toThrow(new Error('missing site id')); - }); + it('should fail on missing site id', () => { + expect(() => { + getTestSiteThemeSDK({ + siteId: '', + }); + }).toThrow(new Error('missing site id')); + }); - it('should fail on invalid user id', () => { - expect(() => { - getTestSiteThemeSDK({ - userId: 2.23423, - }); - }).toThrow(new Error('invalid user id')); - }); + it('should fail on invalid user id', () => { + expect(() => { + getTestSiteThemeSDK({ + userId: 2.23423, + }); + }).toThrow(new Error('invalid user id')); + }); - it('should fail on invalid site id', () => { - expect(() => { - getTestSiteThemeSDK({ - siteId: 'asdasf', - }); - }).toThrow(new Error('invalid site id')); - }); + it('should fail on invalid site id', () => { + expect(() => { + getTestSiteThemeSDK({ + siteId: 'asdasf', + }); + }).toThrow(new Error('invalid site id')); + }); - it('should fail on missing square merchant id', () => { - expect(() => { - getTestSiteThemeSDK({ - merchantId: '' - }); - }).toThrow(new Error('missing merchant id')); - }); + it('should fail on missing square merchant id', () => { + expect(() => { + getTestSiteThemeSDK({ + merchantId: '' + }); + }).toThrow(new Error('missing merchant id')); + }); }); diff --git a/test/orders.test.ts b/test/orders.test.ts index 4d19bc6..e1eca45 100644 --- a/test/orders.test.ts +++ b/test/orders.test.ts @@ -11,46 +11,46 @@ const CURRENT_URL = 'currentUrl'; const GET_ORDER_URL = '/app/cms/api/v1/sites/fakecmssiteid/order-again/fakejwt?location=fakelocation&fulfillments[]=shipping'; const okOrderResponse = { - data: {} + data: {} }; const okFetchResponse = createFetchResponse(okOrderResponse, true, 200); function createFetchResponse(data: LooseObject, ok: boolean, status: number, redirectUrl = ''): Response { - return { - ok, - headers: { - append: vi.fn(), - delete: vi.fn(), - get: vi.fn(), - has: vi.fn(), - set: vi.fn(), - forEach: vi.fn() - }, - redirected: !!redirectUrl, - status: status, - statusText: STATUS_TEXT, - type: 'default', - url: redirectUrl, - clone: vi.fn(), - body: null, - bodyUsed: false, - arrayBuffer: vi.fn(), - blob: vi.fn(), - formData: vi.fn(), - text: vi.fn(), - json: () => new Promise((resolve) => resolve(data)) - }; + return { + ok, + headers: { + append: vi.fn(), + delete: vi.fn(), + get: vi.fn(), + has: vi.fn(), + set: vi.fn(), + forEach: vi.fn() + }, + redirected: !!redirectUrl, + status: status, + statusText: STATUS_TEXT, + type: 'default', + url: redirectUrl, + clone: vi.fn(), + body: null, + bodyUsed: false, + arrayBuffer: vi.fn(), + blob: vi.fn(), + formData: vi.fn(), + text: vi.fn(), + json: () => new Promise((resolve) => resolve(data)) + }; } function createHeadersAndMethodForRequest(method: string = 'GET'): LooseObject { - return { - headers: { - 'Accept': 'application/json', - 'content-type' : 'application/json; charset=UTF-8', - 'X-CSRF-TOKEN': CSRF_TOKEN - }, - method: method, - }; + return { + headers: { + 'Accept': 'application/json', + 'content-type' : 'application/json; charset=UTF-8', + 'X-CSRF-TOKEN': CSRF_TOKEN + }, + method: method, + }; } const mockWindowHrefSet = vi.fn(); @@ -58,109 +58,109 @@ const mockWindowHrefSet = vi.fn(); const sdk = getTestSiteThemeSDK(); beforeEach(() => { - const documentMock = { - querySelector: () => { - return { - content: CSRF_TOKEN, - }; - }, - cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` - }; - - const windowMock = { - location: { - get href() { return CURRENT_URL; }, - set href(url) { - mockWindowHrefSet(url); - } - } - }; - - vi.stubGlobal('document', documentMock); - vi.stubGlobal('window', windowMock); - - global.fetch = vi.fn(); - vi.mocked(fetch).mockResolvedValue(okFetchResponse); + const documentMock = { + querySelector: () => { + return { + content: CSRF_TOKEN, + }; + }, + cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` + }; + + const windowMock = { + location: { + get href() { return CURRENT_URL; }, + set href(url) { + mockWindowHrefSet(url); + } + } + }; + + vi.stubGlobal('document', documentMock); + vi.stubGlobal('window', windowMock); + + global.fetch = vi.fn(); + vi.mocked(fetch).mockResolvedValue(okFetchResponse); }); describe('Order request', () => { - it('should make valid coordinates lookup', async () => { - const result = await sdk.orders.getOrder({ - jwtToken: 'fakejwt', - locationId: 'fakelocation', - fulfillments: ['shipping'] - }); - - expect(fetch).toHaveBeenCalledWith( - `${GET_ORDER_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest() - }) - ); - - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okOrderResponse); - }); - - it('should fail on missing jwt token', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await expect(sdk.orders.getOrder({ - locationId: 'fakelocation', - fulfillments: ['shipping'] - })).rejects.toThrow(new Error('missing jwtToken')); - }); - - it('should fail on missing location id', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await expect(sdk.orders.getOrder({ - jwtToken: 'fakejwt', - fulfillments: ['shipping'] - })).rejects.toThrow(new Error('missing locationId')); - }); - - it('should fail on missing fulfillments', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await expect(sdk.orders.getOrder({ - jwtToken: 'fakejwt', - locationId: 'fakelocation' - })).rejects.toThrow(new Error('missing fulfillments')); - }); - - it('should fail on invalid fulfillment entry', async () => { - await expect(sdk.orders.getOrder({ - jwtToken: 'fakejwt', - locationId: 'fakelocation', - fulfillments: ['foo'] - })).rejects.toThrow(new Error('invalid value in fulfillments array: foo')); - }); - - it('should fail on invalid fulfillment format', async () => { - await expect(sdk.orders.getOrder({ - jwtToken: 'fakejwt', - locationId: 'fakelocation', - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - fulfillments: 'shipping' - })).rejects.toThrow(new Error('fulfillments must be an array')); - }); - - it('should fail on missing cms site id', async () => { - const bootstrap = { - userId: 1, - siteId: '1', - cdnDomain: 'testcdn.editmysite.com', - merchantId: 'merchant-id-123' - }; - const brokenSdk = new SiteThemeSDK(bootstrap); - - await expect(brokenSdk.orders.getOrder({ - jwtToken: 'fakejwt', - locationId: 'fakelocation', - fulfillments: ['shipping'] - })).rejects.toThrow(new Error('missing cmsSiteId')); - }); + it('should make valid coordinates lookup', async () => { + const result = await sdk.orders.getOrder({ + jwtToken: 'fakejwt', + locationId: 'fakelocation', + fulfillments: ['shipping'] + }); + + expect(fetch).toHaveBeenCalledWith( + `${GET_ORDER_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest() + }) + ); + + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okOrderResponse); + }); + + it('should fail on missing jwt token', async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await expect(sdk.orders.getOrder({ + locationId: 'fakelocation', + fulfillments: ['shipping'] + })).rejects.toThrow(new Error('missing jwtToken')); + }); + + it('should fail on missing location id', async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await expect(sdk.orders.getOrder({ + jwtToken: 'fakejwt', + fulfillments: ['shipping'] + })).rejects.toThrow(new Error('missing locationId')); + }); + + it('should fail on missing fulfillments', async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await expect(sdk.orders.getOrder({ + jwtToken: 'fakejwt', + locationId: 'fakelocation' + })).rejects.toThrow(new Error('missing fulfillments')); + }); + + it('should fail on invalid fulfillment entry', async () => { + await expect(sdk.orders.getOrder({ + jwtToken: 'fakejwt', + locationId: 'fakelocation', + fulfillments: ['foo'] + })).rejects.toThrow(new Error('invalid value in fulfillments array: foo')); + }); + + it('should fail on invalid fulfillment format', async () => { + await expect(sdk.orders.getOrder({ + jwtToken: 'fakejwt', + locationId: 'fakelocation', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + fulfillments: 'shipping' + })).rejects.toThrow(new Error('fulfillments must be an array')); + }); + + it('should fail on missing cms site id', async () => { + const bootstrap = { + userId: 1, + siteId: '1', + cdnDomain: 'testcdn.editmysite.com', + merchantId: 'merchant-id-123' + }; + const brokenSdk = new SiteThemeSDK(bootstrap); + + await expect(brokenSdk.orders.getOrder({ + jwtToken: 'fakejwt', + locationId: 'fakelocation', + fulfillments: ['shipping'] + })).rejects.toThrow(new Error('missing cmsSiteId')); + }); }); diff --git a/test/places.test.ts b/test/places.test.ts index a7dc70a..e2da861 100644 --- a/test/places.test.ts +++ b/test/places.test.ts @@ -2,9 +2,9 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { describe, expect, it, vi, beforeEach } from 'vitest'; import { - AutocompletePlacesRequest, - AutocompletePlaceTypeEnum, - GetPlaceRequest + AutocompletePlacesRequest, + AutocompletePlaceTypeEnum, + GetPlaceRequest } from '../src/types/api/places'; import { getTestSiteThemeSDK } from './helpers'; import type LooseObject from '../src/types/looseobject'; @@ -18,65 +18,65 @@ const PLACES_AUTOCOMPLETE_ADDRESS_URL = '/app/store/api/v28/pub/users/1/sites/1/ const PLACES_LOOKUP_URL = '/app/store/api/v28/pub/users/1/sites/1/places/G:ChIJd0vnT0vCwokRNB851YLnt4s'; const okPlacesResponse = { - data: {} + data: {} }; const okFetchResponse = createFetchResponse(okPlacesResponse, true, 200); function createFetchResponse(data: LooseObject, ok: boolean, status: number, redirectUrl = ''): Response { - return { - ok, - headers: { - append: vi.fn(), - delete: vi.fn(), - get: vi.fn(), - has: vi.fn(), - set: vi.fn(), - forEach: vi.fn() - }, - redirected: !!redirectUrl, - status: status, - statusText: STATUS_TEXT, - type: 'default', - url: redirectUrl, - clone: vi.fn(), - body: null, - bodyUsed: false, - arrayBuffer: vi.fn(), - blob: vi.fn(), - formData: vi.fn(), - text: vi.fn(), - json: () => new Promise((resolve) => resolve(data)) - }; + return { + ok, + headers: { + append: vi.fn(), + delete: vi.fn(), + get: vi.fn(), + has: vi.fn(), + set: vi.fn(), + forEach: vi.fn() + }, + redirected: !!redirectUrl, + status: status, + statusText: STATUS_TEXT, + type: 'default', + url: redirectUrl, + clone: vi.fn(), + body: null, + bodyUsed: false, + arrayBuffer: vi.fn(), + blob: vi.fn(), + formData: vi.fn(), + text: vi.fn(), + json: () => new Promise((resolve) => resolve(data)) + }; } function createHeadersAndMethodForRequest(method: string = 'GET'): LooseObject { - return { - headers: { - 'Accept': 'application/json', - 'content-type' : 'application/json; charset=UTF-8', - 'X-CSRF-TOKEN': CSRF_TOKEN - }, - method: method, - }; + return { + headers: { + 'Accept': 'application/json', + 'content-type' : 'application/json; charset=UTF-8', + 'X-CSRF-TOKEN': CSRF_TOKEN + }, + method: method, + }; } function createAutocompleteRequest( - { address, types }: { address: string; types?: AutocompletePlaceTypeEnum } + { address, types }: { address: string; types?: AutocompletePlaceTypeEnum } ): AutocompletePlacesRequest { - const request: AutocompletePlacesRequest = { - 'address': address, - 'types': types, - }; - return request; + const request: AutocompletePlacesRequest = { + 'address': address, + 'types': types, + }; + return request; } function createLookupRequest( - { placeId }: { placeId: string } + { placeId }: { placeId: string } ): GetPlaceRequest { - const request: GetPlaceRequest = { - 'placeId': placeId, - }; - return request; + const request: GetPlaceRequest = { + 'placeId': placeId, + }; + return request; } const mockWindowHrefSet = vi.fn(); @@ -84,130 +84,130 @@ const mockWindowHrefSet = vi.fn(); const sdk = getTestSiteThemeSDK(); beforeEach(() => { - const documentMock = { - querySelector: () => { - return { - content: CSRF_TOKEN, - }; - }, - cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` - }; - - const windowMock = { - location: { - get href() { return CURRENT_URL; }, - set href(url) { - mockWindowHrefSet(url); - } - } - }; - - vi.stubGlobal('document', documentMock); - vi.stubGlobal('window', windowMock); - - global.fetch = vi.fn(); - vi.mocked(fetch).mockResolvedValue(okFetchResponse); + const documentMock = { + querySelector: () => { + return { + content: CSRF_TOKEN, + }; + }, + cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` + }; + + const windowMock = { + location: { + get href() { return CURRENT_URL; }, + set href(url) { + mockWindowHrefSet(url); + } + } + }; + + vi.stubGlobal('document', documentMock); + vi.stubGlobal('window', windowMock); + + global.fetch = vi.fn(); + vi.mocked(fetch).mockResolvedValue(okFetchResponse); }); describe('Places request', () => { - it('should make valid autocomplete', async () => { - const request: AutocompletePlacesRequest = createAutocompleteRequest({ address: '32 Reservoir' }); - const result = await sdk.places.autocompletePlaces(request); - - expect(fetch).toHaveBeenCalledWith( - `${PLACES_AUTOCOMPLETE_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest() - }) - ); - - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okPlacesResponse); - }); - - it('should make valid autocomplete with types specified', async () => { - const request: AutocompletePlacesRequest = createAutocompleteRequest({ address: '32 Reservoir', types: 'address' }); - const result = await sdk.places.autocompletePlaces(request); - - expect(fetch).toHaveBeenCalledWith( - `${PLACES_AUTOCOMPLETE_ADDRESS_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest() - }) - ); - - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okPlacesResponse); - }); - - it('should make valid autocomplete with cdnDomain', async () => { - const cdnDomain = 'cdn3.editmysite.com'; - const sdk = getTestSiteThemeSDK({ - cdnDomain - }); - const request: AutocompletePlacesRequest = createAutocompleteRequest({ address: '32 Reservoir' }); - const result = await sdk.places.autocompletePlaces(request); - - expect(fetch).toHaveBeenCalledWith( - `${PLACES_AUTOCOMPLETE_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest() - }) - ); - - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okPlacesResponse); - }); - - it('should make valid place lookup', async () => { - const request: GetPlaceRequest = createLookupRequest({ placeId: 'G:ChIJd0vnT0vCwokRNB851YLnt4s' }); - const result = await sdk.places.getPlace(request); - - expect(fetch).toHaveBeenCalledWith( - `${PLACES_LOOKUP_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest() - }) - ); - - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okPlacesResponse); - }); - - it('should make valid place lookup with cdnDomain', async () => { - const cdnDomain = 'cdn3.editmysite.com'; - const sdk = getTestSiteThemeSDK({ - cdnDomain - }); - const request: GetPlaceRequest = createLookupRequest({ placeId: 'G:ChIJd0vnT0vCwokRNB851YLnt4s' }); - const result = await sdk.places.getPlace(request); - - expect(fetch).toHaveBeenCalledWith( - `${PLACES_LOOKUP_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest() - }) - ); - - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okPlacesResponse); - }); - - it('should return empty object on empty array', async () => { - const getPlaceResponse = { - data: [] - }; - const fetchResponse = createFetchResponse(getPlaceResponse, true, 200); - vi.mocked(fetch).mockResolvedValue(fetchResponse); - - const request: GetPlaceRequest = createLookupRequest({ placeId: 'G:ChIJd0vnT0vCwokRNB851YLnt4s' }); - const result = await sdk.places.getPlace(request); - - expect(result.data).toStrictEqual({}); - }); + it('should make valid autocomplete', async () => { + const request: AutocompletePlacesRequest = createAutocompleteRequest({ address: '32 Reservoir' }); + const result = await sdk.places.autocompletePlaces(request); + + expect(fetch).toHaveBeenCalledWith( + `${PLACES_AUTOCOMPLETE_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest() + }) + ); + + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okPlacesResponse); + }); + + it('should make valid autocomplete with types specified', async () => { + const request: AutocompletePlacesRequest = createAutocompleteRequest({ address: '32 Reservoir', types: 'address' }); + const result = await sdk.places.autocompletePlaces(request); + + expect(fetch).toHaveBeenCalledWith( + `${PLACES_AUTOCOMPLETE_ADDRESS_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest() + }) + ); + + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okPlacesResponse); + }); + + it('should make valid autocomplete with cdnDomain', async () => { + const cdnDomain = 'cdn3.editmysite.com'; + const sdk = getTestSiteThemeSDK({ + cdnDomain + }); + const request: AutocompletePlacesRequest = createAutocompleteRequest({ address: '32 Reservoir' }); + const result = await sdk.places.autocompletePlaces(request); + + expect(fetch).toHaveBeenCalledWith( + `${PLACES_AUTOCOMPLETE_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest() + }) + ); + + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okPlacesResponse); + }); + + it('should make valid place lookup', async () => { + const request: GetPlaceRequest = createLookupRequest({ placeId: 'G:ChIJd0vnT0vCwokRNB851YLnt4s' }); + const result = await sdk.places.getPlace(request); + + expect(fetch).toHaveBeenCalledWith( + `${PLACES_LOOKUP_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest() + }) + ); + + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okPlacesResponse); + }); + + it('should make valid place lookup with cdnDomain', async () => { + const cdnDomain = 'cdn3.editmysite.com'; + const sdk = getTestSiteThemeSDK({ + cdnDomain + }); + const request: GetPlaceRequest = createLookupRequest({ placeId: 'G:ChIJd0vnT0vCwokRNB851YLnt4s' }); + const result = await sdk.places.getPlace(request); + + expect(fetch).toHaveBeenCalledWith( + `${PLACES_LOOKUP_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest() + }) + ); + + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okPlacesResponse); + }); + + it('should return empty object on empty array', async () => { + const getPlaceResponse = { + data: [] + }; + const fetchResponse = createFetchResponse(getPlaceResponse, true, 200); + vi.mocked(fetch).mockResolvedValue(fetchResponse); + + const request: GetPlaceRequest = createLookupRequest({ placeId: 'G:ChIJd0vnT0vCwokRNB851YLnt4s' }); + const result = await sdk.places.getPlace(request); + + expect(result.data).toStrictEqual({}); + }); }); diff --git a/test/resources.test.ts b/test/resources.test.ts index 8229ab6..c1670c0 100644 --- a/test/resources.test.ts +++ b/test/resources.test.ts @@ -2,8 +2,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { describe, expect, it, vi, beforeEach } from 'vitest'; import { - ItemResourceInput, - ResourcesRequest + ItemResourceInput, + ResourcesRequest } from '../src/types/api/resources'; import { getTestSiteThemeSDK } from './helpers'; import type LooseObject from '../src/types/looseobject'; @@ -15,65 +15,65 @@ const CURRENT_URL = 'currentUrl'; const RESOURCES_API_URL = '/s/api/v1/resources'; const okResourcesResponse = { - data: {}, - errors: [] + data: {}, + errors: [] }; const okFetchResponse = createFetchResponse(okResourcesResponse, true, 200); function createFetchResponse(data: LooseObject, ok: boolean, status: number, redirectUrl = ''): Response { - return { - ok, - headers: { - append: vi.fn(), - delete: vi.fn(), - get: vi.fn(), - has: vi.fn(), - set: vi.fn(), - forEach: vi.fn() - }, - redirected: !!redirectUrl, - status: status, - statusText: STATUS_TEXT, - type: 'default', - url: redirectUrl, - clone: vi.fn(), - body: null, - bodyUsed: false, - arrayBuffer: vi.fn(), - blob: vi.fn(), - formData: vi.fn(), - text: vi.fn(), - json: () => new Promise((resolve) => resolve(data)) - }; + return { + ok, + headers: { + append: vi.fn(), + delete: vi.fn(), + get: vi.fn(), + has: vi.fn(), + set: vi.fn(), + forEach: vi.fn() + }, + redirected: !!redirectUrl, + status: status, + statusText: STATUS_TEXT, + type: 'default', + url: redirectUrl, + clone: vi.fn(), + body: null, + bodyUsed: false, + arrayBuffer: vi.fn(), + blob: vi.fn(), + formData: vi.fn(), + text: vi.fn(), + json: () => new Promise((resolve) => resolve(data)) + }; } function createHeadersAndMethodForRequest(method: string = 'POST'): LooseObject { - return { - headers: { - 'Accept': 'application/json', - 'content-type' : 'application/json; charset=UTF-8', - 'X-CSRF-TOKEN': CSRF_TOKEN - }, - method: method, - }; + return { + headers: { + 'Accept': 'application/json', + 'content-type' : 'application/json; charset=UTF-8', + 'X-CSRF-TOKEN': CSRF_TOKEN + }, + method: method, + }; } function createItemResourcesRequest( - { itemId }: { itemId: string } + { itemId }: { itemId: string } ): ResourcesRequest { - const itemInput: ItemResourceInput = { - type: 'item', - filters: { - id: itemId, - square_online_id: true - } - }; - - const request: ResourcesRequest = { - 'item': itemInput - }; - - return request; + const itemInput: ItemResourceInput = { + type: 'item', + filters: { + id: itemId, + square_online_id: true + } + }; + + const request: ResourcesRequest = { + 'item': itemInput + }; + + return request; } const mockWindowHrefSet = vi.fn(); @@ -81,46 +81,46 @@ const mockWindowHrefSet = vi.fn(); const sdk = getTestSiteThemeSDK(); beforeEach(() => { - const documentMock = { - querySelector: () => { - return { - content: CSRF_TOKEN, - }; - }, - cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` - }; - - const windowMock = { - location: { - get href() { return CURRENT_URL; }, - set href(url) { - mockWindowHrefSet(url); - } - } - }; - - vi.stubGlobal('document', documentMock); - vi.stubGlobal('window', windowMock); - - global.fetch = vi.fn(); - vi.mocked(fetch).mockResolvedValue(okFetchResponse); + const documentMock = { + querySelector: () => { + return { + content: CSRF_TOKEN, + }; + }, + cookie: `test_cookie=test; com_cart_id=${CART_TOKEN};` + }; + + const windowMock = { + location: { + get href() { return CURRENT_URL; }, + set href(url) { + mockWindowHrefSet(url); + } + } + }; + + vi.stubGlobal('document', documentMock); + vi.stubGlobal('window', windowMock); + + global.fetch = vi.fn(); + vi.mocked(fetch).mockResolvedValue(okFetchResponse); }); describe('Resources request', () => { - it('should make valid fetch', async () => { - const request: ResourcesRequest = createItemResourcesRequest({ itemId: '1' }); - const result = await sdk.resources.getResources(request); - - expect(fetch).toHaveBeenCalledWith( - `${RESOURCES_API_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify({ input: request }) - }) - ); - - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okResourcesResponse); - }); + it('should make valid fetch', async () => { + const request: ResourcesRequest = createItemResourcesRequest({ itemId: '1' }); + const result = await sdk.resources.getResources(request); + + expect(fetch).toHaveBeenCalledWith( + `${RESOURCES_API_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify({ input: request }) + }) + ); + + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okResourcesResponse); + }); }); diff --git a/test/template.test.ts b/test/template.test.ts index 1747956..5a2f5ea 100644 --- a/test/template.test.ts +++ b/test/template.test.ts @@ -2,8 +2,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { describe, expect, it, vi, beforeEach } from 'vitest'; import { - TemplateRequest, - TemplateError + TemplateRequest, + TemplateError } from '../src/types/api/template'; import { getTestSiteThemeSDK } from './helpers'; import type LooseObject from '../src/types/looseobject'; @@ -21,52 +21,52 @@ const errorTemplateResponse: string = const errorFetchResponse = createFetchResponse(errorTemplateResponse, false, 500); function createFetchResponse(data: string, ok: boolean, status: number, redirectUrl = ''): Response { - return { - ok, - headers: { - append: vi.fn(), - delete: vi.fn(), - get: vi.fn(), - has: vi.fn(), - set: vi.fn(), - forEach: vi.fn() - }, - redirected: !!redirectUrl, - status: status, - statusText: STATUS_TEXT, - type: 'default', - url: redirectUrl, - clone: vi.fn(), - body: null, - bodyUsed: false, - arrayBuffer: vi.fn(), - blob: vi.fn(), - formData: vi.fn(), - text: () => new Promise((resolve) => resolve(data)) , - json: vi.fn() - }; + return { + ok, + headers: { + append: vi.fn(), + delete: vi.fn(), + get: vi.fn(), + has: vi.fn(), + set: vi.fn(), + forEach: vi.fn() + }, + redirected: !!redirectUrl, + status: status, + statusText: STATUS_TEXT, + type: 'default', + url: redirectUrl, + clone: vi.fn(), + body: null, + bodyUsed: false, + arrayBuffer: vi.fn(), + blob: vi.fn(), + formData: vi.fn(), + text: () => new Promise((resolve) => resolve(data)) , + json: vi.fn() + }; } function createHeadersAndMethodForRequest(method: string = 'POST'): LooseObject { - return { - headers: { - 'Accept': 'application/json', - 'content-type' : 'application/json; charset=UTF-8', - 'X-CSRF-TOKEN': CSRF_TOKEN - }, - method: method, - }; + return { + headers: { + 'Accept': 'application/json', + 'content-type' : 'application/json; charset=UTF-8', + 'X-CSRF-TOKEN': CSRF_TOKEN + }, + method: method, + }; } function createTemplateRequest( - { template, props }: { template: string; props: object } + { template, props }: { template: string; props: object } ): TemplateRequest { - const request: TemplateRequest = { - 'template': template, - 'props': props - }; + const request: TemplateRequest = { + 'template': template, + 'props': props + }; - return request; + return request; } const mockWindowHrefSet = vi.fn(); @@ -74,61 +74,61 @@ const mockWindowHrefSet = vi.fn(); const sdk = getTestSiteThemeSDK(); beforeEach(() => { - const documentMock = { - querySelector: () => { - return { - content: CSRF_TOKEN, - }; - }, - cookie: 'test_cookie=test;' - }; - - const windowMock = { - location: { - get href() { return CURRENT_URL; }, - set href(url) { - mockWindowHrefSet(url); - } - } - }; - - vi.stubGlobal('document', documentMock); - vi.stubGlobal('window', windowMock); - - global.fetch = vi.fn(); + const documentMock = { + querySelector: () => { + return { + content: CSRF_TOKEN, + }; + }, + cookie: 'test_cookie=test;' + }; + + const windowMock = { + location: { + get href() { return CURRENT_URL; }, + set href(url) { + mockWindowHrefSet(url); + } + } + }; + + vi.stubGlobal('document', documentMock); + vi.stubGlobal('window', windowMock); + + global.fetch = vi.fn(); }); describe('Template request', () => { - it('should make valid fetch', async () => { - vi.mocked(fetch).mockResolvedValue(okFetchResponse); - - const request: TemplateRequest = createTemplateRequest({template: 'templates/pages/whatever', props: {}}); - const result = await sdk.template.getTemplate(request); - - expect(fetch).toHaveBeenCalledWith( - `${TEMPLATE_API_URL}`, - expect.objectContaining({ - ...createHeadersAndMethodForRequest(), - body: JSON.stringify(request) - }) - ); - - expect(fetch).toHaveBeenCalledOnce(); - - expect(result).toStrictEqual(okTemplateResponse); - }); - - it('should throw error on invalid fetch', async () => { - vi.mocked(fetch).mockResolvedValue(errorFetchResponse); - - const request: TemplateRequest = createTemplateRequest({template: 'templates/pages/whatever', props: {}}); - - try { - await sdk.template.getTemplate(request); - } catch (e) { - const templateError = (e as TemplateError); - expect(templateError.message).toStrictEqual('Unable to render template'); - expect(templateError.template).toStrictEqual(errorTemplateResponse); - } - }); + it('should make valid fetch', async () => { + vi.mocked(fetch).mockResolvedValue(okFetchResponse); + + const request: TemplateRequest = createTemplateRequest({template: 'templates/pages/whatever', props: {}}); + const result = await sdk.template.getTemplate(request); + + expect(fetch).toHaveBeenCalledWith( + `${TEMPLATE_API_URL}`, + expect.objectContaining({ + ...createHeadersAndMethodForRequest(), + body: JSON.stringify(request) + }) + ); + + expect(fetch).toHaveBeenCalledOnce(); + + expect(result).toStrictEqual(okTemplateResponse); + }); + + it('should throw error on invalid fetch', async () => { + vi.mocked(fetch).mockResolvedValue(errorFetchResponse); + + const request: TemplateRequest = createTemplateRequest({template: 'templates/pages/whatever', props: {}}); + + try { + await sdk.template.getTemplate(request); + } catch (e) { + const templateError = (e as TemplateError); + expect(templateError.message).toStrictEqual('Unable to render template'); + expect(templateError.template).toStrictEqual(errorTemplateResponse); + } + }); }); diff --git a/vite.config.js b/vite.config.js index da7bb54..424b3f9 100644 --- a/vite.config.js +++ b/vite.config.js @@ -4,28 +4,28 @@ import dts from 'vite-plugin-dts'; // https://vitejs.dev/config/ export default defineConfig({ - build: { - lib: { - // eslint-disable-next-line no-undef - entry: resolve(__dirname, 'src/index.ts'), - name: 'SiteThemeSDK', - fileName: 'index' - }, - outDir: 'lib' - }, - plugins: [ - dts(), - ], - test: { - chaiConfig: { - truncateThreshold: 0 - }, - coverage: { - provider: 'v8', - lines: 100, - functions: 100, - branches: 100, - statements: 100 - } - } + build: { + lib: { + // eslint-disable-next-line no-undef + entry: resolve(__dirname, 'src/index.ts'), + name: 'SiteThemeSDK', + fileName: 'index' + }, + outDir: 'lib' + }, + plugins: [ + dts(), + ], + test: { + chaiConfig: { + truncateThreshold: 0 + }, + coverage: { + provider: 'v8', + lines: 100, + functions: 100, + branches: 100, + statements: 100 + } + } });