diff --git a/jest.config.js b/jest.config.js index 9eab5882a7..08436dfdb2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -315,7 +315,14 @@ const jestConfig = { ), configureProject('pwa-theme-venia', 'Venia Theme', () => ({ testEnvironment: 'node' - })) + })), + configureProject( + 'extensions/experience-platform-connector', + 'Experience platform connector', + () => ({ + testEnvironment: 'node' + }) + ) ], // Include files with zero tests in overall coverage analysis by specifying // coverage paths manually. diff --git a/packages/extensions/experience-platform-connector/intercept.js b/packages/extensions/experience-platform-connector/intercept.js new file mode 100644 index 0000000000..70a3c328a1 --- /dev/null +++ b/packages/extensions/experience-platform-connector/intercept.js @@ -0,0 +1,24 @@ +module.exports = targets => { + const { talons } = targets.of('@magento/peregrine'); + const { specialFeatures } = targets.of('@magento/pwa-buildpack'); + + specialFeatures.tap(flags => { + /** + * Wee need to activate esModules, cssModules and GQL Queries to allow build pack to load our extension + * {@link https://magento.github.io/pwa-studio/pwa-buildpack/reference/configure-webpack/#special-flags}. + */ + flags[targets.name] = { + esModules: true + }; + }); + + talons.tap(({ App, Header, SearchBar }) => { + App.useApp.wrapWith('@magento/experience-platform-connector'); + Header.useAccountMenu.wrapWith( + '@magento/experience-platform-connector/src/wrappers/wrapUseAccountMenu' + ); + SearchBar.useAutocomplete.wrapWith( + '@magento/experience-platform-connector/src/wrappers/wrapUseAutocomplete' + ); + }); +}; diff --git a/packages/extensions/experience-platform-connector/package.json b/packages/extensions/experience-platform-connector/package.json new file mode 100644 index 0000000000..d0b593b5aa --- /dev/null +++ b/packages/extensions/experience-platform-connector/package.json @@ -0,0 +1,30 @@ +{ + "name": "@magento/experience-platform-connector", + "version": "0.0.1", + "publishConfig": { + "access": "public" + }, + "description": "Sends storefront events to the Adobe Experience Platform", + "main": "./src/main.js", + "scripts": { + "clean": " " + }, + "repository": "github:magento/pwa-studio", + "license": "(OSL-3.0 OR AFL-3.0)", + "dependencies": { + "@adobe/magento-storefront-event-collector": "~1.1.13", + "@adobe/magento-storefront-events-sdk": "~1.1.13" + }, + "devDependencies": {}, + "peerDependencies": { + "@apollo/client": "~3.6.6", + "@magento/peregrine": "~12.3.0", + "@magento/pwa-buildpack": "~11.2.0", + "react": "~17.0.1" + }, + "pwa-studio": { + "targets": { + "intercept": "./intercept" + } + } +} diff --git a/packages/extensions/experience-platform-connector/src/__tests__/__snapshots__/utils.spec.js.snap b/packages/extensions/experience-platform-connector/src/__tests__/__snapshots__/utils.spec.js.snap new file mode 100644 index 0000000000..3af68127eb --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/__tests__/__snapshots__/utils.spec.js.snap @@ -0,0 +1,137 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getFormattedProducts() returns correctly formatted data 1`] = ` +Array [ + Object { + "configurableOptions": Array [ + Object { + "id": 157, + "optionLabel": "Fashion Color", + "valueId": "Y29uZmlndXJhYmxlLzE1Ny8zMQ==", + "valueLabel": "Peach", + }, + Object { + "id": 190, + "optionLabel": "Fashion Size", + "valueId": "Y29uZmlndXJhYmxlLzE5MC80Mw==", + "valueLabel": "L", + }, + ], + "formattedPrice": "", + "id": "MjQ2Ng==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 78, + }, + "row_total": Object { + "__typename": "Money", + "value": 78, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": "rowena-skirt", + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_2.jpg", + "name": "Rowena Skirt", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 78, + "minimalPrice": 78, + "regularPrice": 78, + }, + "productType": "ConfigurableProduct", + "sku": "VSK02", + }, + "quantity": 1, + }, + Object { + "configurableOptions": null, + "formattedPrice": "", + "id": "MjQ4Mg==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 68, + }, + "row_total": Object { + "__typename": "Money", + "value": 136, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": "silver-cirque-earrings", + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va17-si_main.jpg", + "name": "Silver Cirque Earrings", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 68, + "minimalPrice": 68, + "regularPrice": 68, + }, + "productType": "SimpleProduct", + "sku": "VA17-SI-NA", + }, + "quantity": 2, + }, + Object { + "configurableOptions": Array [ + Object { + "id": 157, + "optionLabel": "Fashion Color", + "valueId": "Y29uZmlndXJhYmxlLzE1Ny8zMQ==", + "valueLabel": "Peach", + }, + Object { + "id": 190, + "optionLabel": "Fashion Size", + "valueId": "Y29uZmlndXJhYmxlLzE5MC80NA==", + "valueLabel": "M", + }, + ], + "formattedPrice": "", + "id": "MjQ4Mw==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 48, + }, + "row_total": Object { + "__typename": "Money", + "value": 144, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": "antonia-infinity-scarf", + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_2.jpg", + "name": "Antonia Infinity Scarf", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 48, + "minimalPrice": 48, + "regularPrice": 48, + }, + "productType": "ConfigurableProduct", + "sku": "VA04", + }, + "quantity": 3, + }, +] +`; diff --git a/packages/extensions/experience-platform-connector/src/__tests__/utils.spec.js b/packages/extensions/experience-platform-connector/src/__tests__/utils.spec.js new file mode 100644 index 0000000000..e766ba4059 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/__tests__/utils.spec.js @@ -0,0 +1,52 @@ +import { getCartTotal, getCurrency, getFormattedProducts } from '../utils'; + +import mockEvent from '../handlers/__tests__/__mocks__/cartPageView'; + +describe('getCartTotal', () => { + it('returns the correct sum for a populated array', () => { + const total = getCartTotal(mockEvent.payload.products); + + expect(total).toEqual(358); + }); + + it('returns zero for an empty array', () => { + const total = getCartTotal([]); + + expect(total).toEqual(0); + }); + + it('returns zero for a null parameter', () => { + const total = getCartTotal(); + + expect(total).toEqual(0); + }); +}); + +describe('getCurrency()', () => { + it('returns the correct currency code from the first array entry', () => { + const currency = getCurrency(mockEvent.payload.products); + + expect(currency).toMatchInlineSnapshot(`"USD"`); + }); + + it('returns null if the array is empty or null', () => { + expect(getCurrency([])).toBeNull(); + expect(getCurrency()).toBeNull(); + }); +}); + +describe('getFormattedProducts()', () => { + it('returns correctly formatted data', () => { + const formattedData = getFormattedProducts(mockEvent.payload.products); + + expect(formattedData).toMatchSnapshot(); + }); + + it('returns an empty array when given an empty array', () => { + expect(getFormattedProducts([])).toEqual([]); + }); + + it('returns null when given a null value', () => { + expect(getFormattedProducts()).toBeNull(); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/config.js b/packages/extensions/experience-platform-connector/src/config.js new file mode 100644 index 0000000000..5f4a6b9781 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/config.js @@ -0,0 +1,31 @@ +import { default as addToCartHandler } from './handlers/addToCart'; +import { default as categoryPageViewHandler } from './handlers/categoryPageView'; +import { default as completeCheckoutHandler } from './handlers/completeCheckout'; +import { default as createAccountHandler } from './handlers/createAccount'; +import { default as editAccountHandler } from './handlers/editAccount'; +import { default as pageViewHandler } from './handlers/pageView'; +import { default as placeOrderHandler } from './handlers/placeOrder'; +import { default as productPageViewHandler } from './handlers/productPageView'; +import { default as searchRequestSentHandler } from './handlers/searchRequestSent'; +import { default as searchResponseReceivedHandler } from './handlers/searchResponseReceived'; +import { default as shoppingCartPageViewHandler } from './handlers/shoppingCartPageView'; +import { default as shoppingMiniCartViewHandler } from './handlers/shoppingMiniCartView'; +import { default as startCheckoutHandler } from './handlers/startCheckout'; +import { default as signInHandler } from './handlers/signIn'; + +export default [ + addToCartHandler, + categoryPageViewHandler, + completeCheckoutHandler, + createAccountHandler, + editAccountHandler, + pageViewHandler, + placeOrderHandler, + productPageViewHandler, + searchRequestSentHandler, + searchResponseReceivedHandler, + shoppingCartPageViewHandler, + shoppingMiniCartViewHandler, + startCheckoutHandler, + signInHandler +]; diff --git a/packages/extensions/experience-platform-connector/src/handleEvent.js b/packages/extensions/experience-platform-connector/src/handleEvent.js new file mode 100644 index 0000000000..ca17544a83 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handleEvent.js @@ -0,0 +1,9 @@ +import handlers from './config'; + +export default (sdk, event) => { + handlers.forEach(({ canHandle, handle }) => { + if (canHandle(event)) { + handle(sdk, event); + } + }); +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/cartAddItem.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/cartAddItem.js new file mode 100644 index 0000000000..2ed44f8086 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/cartAddItem.js @@ -0,0 +1,36 @@ +export const addConfigurableProductEvent = { + type: 'CART_ADD_ITEM', + payload: { + cartId: 'kAR5Gg6uPC6J5wGY0ebecyKfX905epmU', + sku: 'VSK03', + name: 'Johanna Skirt', + priceTotal: 78, + currencyCode: 'USD', + discountAmount: 0, + selectedOptions: [ + { + attribute: 'Fashion Color', + value: 'Peach' + }, + { + attribute: 'Fashion Size', + value: 'M' + } + ], + quantity: 1 + } +}; + +export const addSimpleProductEvent = { + type: 'CART_ADD_ITEM', + payload: { + cartId: 'kAR5Gg6uPC6J5wGY0ebecyKfX905epmU', + sku: 'VA15-SI-NA', + name: 'Silver Sol Earrings', + priceTotal: 48, + currencyCode: 'USD', + discountAmount: 0, + selectedOptions: [], + quantity: 1 + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/cartPageView.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/cartPageView.js new file mode 100644 index 0000000000..87783cffab --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/cartPageView.js @@ -0,0 +1,880 @@ +export default { + type: 'CART_PAGE_VIEW', + payload: { + cart_id: 'kAR5Gg6uPC6J5wGY0ebecyKfX905epmU', + products: [ + { + __typename: 'ConfigurableCartItem', + uid: 'MjQ2Ng==', + product: { + __typename: 'ConfigurableProduct', + uid: 'MTEzMQ==', + name: 'Rowena Skirt', + sku: 'VSK02', + url_key: 'rowena-skirt', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_2.jpg' + }, + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_2.jpg' + }, + stock_status: 'IN_STOCK', + variants: [ + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc4', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc0', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTcw', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNA==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY2', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-rn_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc3', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTcz', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY5', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNA==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY1', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-rn_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc5', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc1', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTcx', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNA==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY3', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-rn_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTgw', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc2', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTcy', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNA==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY4', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-rn_main_1.jpg' + } + } + } + ] + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 78 + }, + row_total: { + __typename: 'Money', + value: 78 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 1, + errors: null, + configurable_options: [ + { + __typename: 'SelectedConfigurableOption', + id: 157, + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzEvMTU3', + option_label: 'Fashion Color', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==', + value_label: 'Peach' + }, + { + __typename: 'SelectedConfigurableOption', + id: 190, + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzEvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80Mw==', + value_label: 'L' + } + ] + }, + { + __typename: 'SimpleCartItem', + uid: 'MjQ4Mg==', + product: { + __typename: 'SimpleProduct', + uid: 'Nw==', + name: 'Silver Cirque Earrings', + sku: 'VA17-SI-NA', + url_key: 'silver-cirque-earrings', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va17-si_main.jpg' + }, + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va17-si_main.jpg' + }, + stock_status: 'IN_STOCK' + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 68 + }, + row_total: { + __typename: 'Money', + value: 136 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 2, + errors: null + }, + { + __typename: 'ConfigurableCartItem', + uid: 'MjQ4Mw==', + product: { + __typename: 'ConfigurableProduct', + uid: 'MTE2Mw==', + name: 'Antonia Infinity Scarf', + sku: 'VA04', + url_key: 'antonia-infinity-scarf', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_2.jpg' + }, + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_2.jpg' + }, + stock_status: 'IN_STOCK', + variants: [ + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0Ng==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0Mg==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzOA==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ly_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzNA==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0NQ==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0MQ==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzNw==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ly_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzMw==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0Nw==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0Mw==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzOQ==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ly_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzNQ==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0OA==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0NA==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0MA==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ly_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzNg==', + small_image: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-pe_main_1.jpg' + } + } + } + ] + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 48 + }, + row_total: { + __typename: 'Money', + value: 144 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 3, + errors: null, + configurable_options: [ + { + __typename: 'SelectedConfigurableOption', + id: 157, + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExNjMvMTU3', + option_label: 'Fashion Color', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==', + value_label: 'Peach' + }, + { + __typename: 'SelectedConfigurableOption', + id: 190, + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExNjMvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80NA==', + value_label: 'M' + } + ] + } + ] + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/categoryPageView.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/categoryPageView.js new file mode 100644 index 0000000000..f84f57deeb --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/categoryPageView.js @@ -0,0 +1,9 @@ +export default { + type: 'CATEGORY_PAGE_VIEW', + payload: { + id: 'MTQ=', + name: 'Dresses', + url_key: 'venia-dresses', + url_path: 'venia-dresses' + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/checkoutPageView.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/checkoutPageView.js new file mode 100644 index 0000000000..00047daf05 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/checkoutPageView.js @@ -0,0 +1,138 @@ +export default { + type: 'CHECKOUT_PAGE_VIEW', + payload: { + cart_id: 'adaIGWl4UsoKOGIeX17FPrnMq3bXHVZA', + products: [ + { + __typename: 'ConfigurableCartItem', + uid: 'Mjk3Nw==', + product: { + __typename: 'ConfigurableProduct', + uid: 'MTEzNw==', + stock_status: 'IN_STOCK', + sku: 'VP08', + name: 'Bella Eyelet Capris', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/p/vp08-rn_main_2.jpg' + } + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 98 + }, + row_total: { + __typename: 'Money', + value: 294 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 3, + configurable_options: [ + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzcvMTU3', + option_label: 'Fashion Color', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE1Ny8zNQ==', + value_label: 'Mint' + }, + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzcvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80OQ==', + value_label: '6' + } + ] + }, + { + __typename: 'ConfigurableCartItem', + uid: 'Mjk3OQ==', + product: { + __typename: 'ConfigurableProduct', + uid: 'MTE1OA==', + stock_status: 'IN_STOCK', + sku: 'VA09', + name: 'Thin Leather Braided Belt', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va09-br_main_2.jpg' + } + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 38 + }, + row_total: { + __typename: 'Money', + value: 38 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 1, + configurable_options: [ + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExNTgvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80NA==', + value_label: 'M' + } + ] + }, + { + __typename: 'SimpleCartItem', + uid: 'Mjk4Nw==', + product: { + __typename: 'SimpleProduct', + uid: 'MTI=', + stock_status: 'IN_STOCK', + sku: 'VA22-SI-NA', + name: 'Silver Amor Bangle Set', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va22-si_main.jpg' + } + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 98 + }, + row_total: { + __typename: 'Money', + value: 98 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 1 + } + ] + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/completeCheckout.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/completeCheckout.js new file mode 100644 index 0000000000..dfba32d6ab --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/completeCheckout.js @@ -0,0 +1,156 @@ +export default { + type: 'ORDER_CONFIRMATION_PAGE_VIEW', + payload: { + amount: { + grand_total: { + value: 40 + } + }, + cart_id: '123', + order_number: '001', + payment: { + title: 'Visa' + }, + products: [ + { + __typename: 'ConfigurableCartItem', + uid: 'Mjk3Nw==', + product: { + __typename: 'ConfigurableProduct', + uid: 'MTEzNw==', + stock_status: 'IN_STOCK', + sku: 'VP08', + name: 'Bella Eyelet Capris', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/p/vp08-rn_main_2.jpg' + } + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 98 + }, + row_total: { + __typename: 'Money', + value: 294 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 3, + configurable_options: [ + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzcvMTU3', + option_label: 'Fashion Color', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE1Ny8zNQ==', + value_label: 'Mint' + }, + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzcvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80OQ==', + value_label: '6' + } + ] + }, + { + __typename: 'ConfigurableCartItem', + uid: 'Mjk3OQ==', + product: { + __typename: 'ConfigurableProduct', + uid: 'MTE1OA==', + stock_status: 'IN_STOCK', + sku: 'VA09', + name: 'Thin Leather Braided Belt', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va09-br_main_2.jpg' + } + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 38 + }, + row_total: { + __typename: 'Money', + value: 38 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 1, + configurable_options: [ + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExNTgvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80NA==', + value_label: 'M' + } + ] + }, + { + __typename: 'SimpleCartItem', + uid: 'Mjk4Nw==', + product: { + __typename: 'SimpleProduct', + uid: 'MTI=', + stock_status: 'IN_STOCK', + sku: 'VA22-SI-NA', + name: 'Silver Amor Bangle Set', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va22-si_main.jpg' + } + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 98 + }, + row_total: { + __typename: 'Money', + value: 98 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 1 + } + ], + shipping: [ + { + carrier_title: 'carrier', + method_title: 'method', + amount: { + value: 13 + } + } + ] + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/createAccount.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/createAccount.js new file mode 100644 index 0000000000..d0b24b7017 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/createAccount.js @@ -0,0 +1,9 @@ +export default { + type: 'USER_CREATE_ACCOUNT', + payload: { + email: 'Stephen.Strange@fake.email', + firstName: 'Stephen', + lastName: 'Strange', + isSubscribed: true + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/editAccount.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/editAccount.js new file mode 100644 index 0000000000..d5a5d13633 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/editAccount.js @@ -0,0 +1,9 @@ +export default { + type: 'USER_ACCOUNT_UPDATE', + payload: { + email: 'Stephen.Strange@fake.email', + firstName: 'Stephen', + lastName: 'Strange', + isSubscribed: true + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/miniCartView.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/miniCartView.js new file mode 100644 index 0000000000..7f7fac69dd --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/miniCartView.js @@ -0,0 +1,846 @@ +export default { + type: 'MINI_CART_VIEW', + payload: { + cartId: 'kAR5Gg6uPC6J5wGY0ebecyKfX905epmU', + products: [ + { + __typename: 'ConfigurableCartItem', + uid: 'MjQ2Ng==', + product: { + __typename: 'ConfigurableProduct', + uid: 'MTEzMQ==', + name: 'Rowena Skirt', + sku: 'VSK02', + url_key: 'rowena-skirt', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_2.jpg' + }, + stock_status: 'IN_STOCK', + variants: [ + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc4', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc0', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTcw', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNA==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY2', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-rn_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc3', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTcz', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY5', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNA==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY1', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-rn_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc5', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc1', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTcx', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNA==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY3', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-rn_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTgw', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTc2', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTcy', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNA==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'NTY4', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-rn_main_1.jpg' + } + } + } + ] + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 78 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 1, + configurable_options: [ + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzEvMTU3', + option_label: 'Fashion Color', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==', + value_label: 'Peach' + }, + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzEvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80Mw==', + value_label: 'L' + } + ] + }, + { + __typename: 'SimpleCartItem', + uid: 'MjQ4Mg==', + product: { + __typename: 'SimpleProduct', + uid: 'Nw==', + name: 'Silver Cirque Earrings', + sku: 'VA17-SI-NA', + url_key: 'silver-cirque-earrings', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va17-si_main.jpg' + }, + stock_status: 'IN_STOCK' + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 68 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 2 + }, + { + __typename: 'ConfigurableCartItem', + uid: 'MjQ4Mw==', + product: { + __typename: 'ConfigurableProduct', + uid: 'MTE2Mw==', + name: 'Antonia Infinity Scarf', + sku: 'VA04', + url_key: 'antonia-infinity-scarf', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_2.jpg' + }, + stock_status: 'IN_STOCK', + variants: [ + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0Ng==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0Mg==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzOA==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ly_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NQ==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzNA==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0NQ==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0MQ==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzNw==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ly_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Ng==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzMw==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0Nw==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0Mw==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzOQ==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ly_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80NA==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzNQ==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-pe_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0OA==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-kh_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMw==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0NA==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zNg==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTA0MA==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ly_main_1.jpg' + } + } + }, + { + __typename: 'ConfigurableVariant', + attributes: [ + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==' + }, + { + __typename: 'ConfigurableAttributeOption', + uid: 'Y29uZmlndXJhYmxlLzE5MC80Mw==' + } + ], + product: { + __typename: 'SimpleProduct', + uid: 'MTAzNg==', + thumbnail: { + __typename: 'ProductImage', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-pe_main_1.jpg' + } + } + } + ] + }, + prices: { + __typename: 'CartItemPrices', + price: { + __typename: 'Money', + currency: 'USD', + value: 48 + }, + total_item_discount: { + __typename: 'Money', + value: 0 + } + }, + quantity: 3, + configurable_options: [ + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExNjMvMTU3', + option_label: 'Fashion Color', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE1Ny8zMQ==', + value_label: 'Peach' + }, + { + __typename: 'SelectedConfigurableOption', + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExNjMvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80NA==', + value_label: 'M' + } + ] + } + ] + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/orderConfirmationPageView.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/orderConfirmationPageView.js new file mode 100644 index 0000000000..6a2aa77357 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/orderConfirmationPageView.js @@ -0,0 +1,67 @@ +export default { + type: 'ORDER_CONFIRMATION_PAGE_VIEW', + payload: { + order_number: '000000267', + cart_id: 'I3XbPvRJhujwf2C2MWhevLgifr9xyXpL', + amount: { + grand_total: { + value: 84.53, + currency: 'USD', + __typename: 'Money' + }, + discounts: null, + __typename: 'CartPrices' + }, + shipping: [ + { + amount: { + value: 0, + currency: 'USD', + __typename: 'Money' + }, + carrier_title: 'Free Shipping', + method_title: 'Free', + __typename: 'SelectedShippingMethod' + } + ], + payment: { + purchase_order_number: null, + title: 'Credit Card', + __typename: 'SelectedPaymentMethod' + }, + products: [ + { + uid: 'MzA3NQ==', + product: { + uid: 'OQ==', + sku: 'VA19-GO-NA', + name: 'Gold Omni Bangle Set', + thumbnail: { + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va19-go_main.jpg', + __typename: 'ProductImage' + }, + __typename: 'SimpleProduct' + }, + prices: { + price: { + currency: 'USD', + value: 78, + __typename: 'Money' + }, + row_total: { + value: 78, + __typename: 'Money' + }, + total_item_discount: { + value: 0, + __typename: 'Money' + }, + __typename: 'CartItemPrices' + }, + quantity: 1, + __typename: 'SimpleCartItem' + } + ] + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/pageView.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/pageView.js new file mode 100644 index 0000000000..cb614b800b --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/pageView.js @@ -0,0 +1,7 @@ +export default { + type: 'CMS_PAGE_VIEW', + payload: { + title: 'Home Page - Venia', + url_key: 'venia-new-home' + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/placeOrderButtonClicked.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/placeOrderButtonClicked.js new file mode 100644 index 0000000000..92c4339140 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/placeOrderButtonClicked.js @@ -0,0 +1,161 @@ +export default { + type: 'CHECKOUT_PLACE_ORDER_BUTTON_CLICKED', + payload: { + cart_id: 'adaIGWl4UsoKOGIeX17FPrnMq3bXHVZA', + amount: { + grand_total: { + value: 466.01, + currency: 'USD', + __typename: 'Money' + }, + discounts: null, + __typename: 'CartPrices' + }, + shipping: [ + { + amount: { + value: 0, + currency: 'USD', + __typename: 'Money' + }, + carrier_title: 'Free Shipping', + method_title: 'Free', + __typename: 'SelectedShippingMethod' + } + ], + payment: { + purchase_order_number: null, + title: 'Credit Card', + __typename: 'SelectedPaymentMethod' + }, + products: [ + { + uid: 'Mjk3Nw==', + product: { + uid: 'MTEzNw==', + sku: 'VP08', + name: 'Bella Eyelet Capris', + thumbnail: { + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/p/vp08-rn_main_2.jpg', + __typename: 'ProductImage' + }, + __typename: 'ConfigurableProduct' + }, + prices: { + price: { + currency: 'USD', + value: 98, + __typename: 'Money' + }, + row_total: { + value: 294, + __typename: 'Money' + }, + total_item_discount: { + value: 0, + __typename: 'Money' + }, + __typename: 'CartItemPrices' + }, + quantity: 3, + configurable_options: [ + { + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzcvMTU3', + option_label: 'Fashion Color', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE1Ny8zNQ==', + value_label: 'Mint', + __typename: 'SelectedConfigurableOption' + }, + { + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExMzcvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80OQ==', + value_label: '6', + __typename: 'SelectedConfigurableOption' + } + ], + __typename: 'ConfigurableCartItem' + }, + { + uid: 'Mjk3OQ==', + product: { + uid: 'MTE1OA==', + sku: 'VA09', + name: 'Thin Leather Braided Belt', + thumbnail: { + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va09-br_main_2.jpg', + __typename: 'ProductImage' + }, + __typename: 'ConfigurableProduct' + }, + prices: { + price: { + currency: 'USD', + value: 38, + __typename: 'Money' + }, + row_total: { + value: 38, + __typename: 'Money' + }, + total_item_discount: { + value: 0, + __typename: 'Money' + }, + __typename: 'CartItemPrices' + }, + quantity: 1, + configurable_options: [ + { + configurable_product_option_uid: + 'Y29uZmlndXJhYmxlLzExNTgvMTkw', + option_label: 'Fashion Size', + configurable_product_option_value_uid: + 'Y29uZmlndXJhYmxlLzE5MC80NA==', + value_label: 'M', + __typename: 'SelectedConfigurableOption' + } + ], + __typename: 'ConfigurableCartItem' + }, + { + uid: 'Mjk4Nw==', + product: { + uid: 'MTI=', + sku: 'VA22-SI-NA', + name: 'Silver Amor Bangle Set', + thumbnail: { + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va22-si_main.jpg', + __typename: 'ProductImage' + }, + __typename: 'SimpleProduct' + }, + prices: { + price: { + currency: 'USD', + value: 98, + __typename: 'Money' + }, + row_total: { + value: 98, + __typename: 'Money' + }, + total_item_discount: { + value: 0, + __typename: 'Money' + }, + __typename: 'CartItemPrices' + }, + quantity: 1, + __typename: 'SimpleCartItem' + } + ] + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/productPageView.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/productPageView.js new file mode 100644 index 0000000000..ce50f460c5 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/productPageView.js @@ -0,0 +1,15 @@ +export default { + type: 'PRODUCT_PAGE_VIEW', + payload: { + id: '234d', + name: 'Selena Pants', + sku: '343g3434t', + currency_code: 'USD', + price_range: { + maximum_price: { + final_price: 40 + } + }, + url_key: 'selena-pants' + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/searchPageRequest.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/searchPageRequest.js new file mode 100644 index 0000000000..f360658a38 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/searchPageRequest.js @@ -0,0 +1,22 @@ +export default { + type: 'SEARCH_REQUEST', + payload: { + query: 'belt', + refinements: [ + { + attribute: 'category_id', + value: new Set(['Accessories,3']), + isRange: false + }, + { + attribute: 'fashion_size', + value: new Set(['M,44']), + isRange: false + } + ], + sort: { + attribute: 'relevance', + order: 'DESC' + } + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/searchRequestSent.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/searchRequestSent.js new file mode 100644 index 0000000000..8b626db46c --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/searchRequestSent.js @@ -0,0 +1,34 @@ +export const searchRequestEvent = { + type: 'SEARCH_REQUEST', + payload: { + query: 'selena', + refinements: [ + { + attribute: 'category_id', + value: new Set(['Bottoms,11']), + isRange: false + }, + { + attribute: 'fashion_color', + value: new Set(['Rain,34', 'Mint,25']), + isRange: false + } + ], + sort: { + attribute: 'relevance', + order: 'DESC' + }, + pageSize: 12, + currentPage: 1 + } +}; + +export const searchbarRequestEvent = { + type: 'SEARCHBAR_REQUEST', + payload: { + query: 'selena', + currentPage: 1, + pageSize: 3, + refinements: [] + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/searchResponseReceived.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/searchResponseReceived.js new file mode 100644 index 0000000000..6e865e443a --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__mocks__/searchResponseReceived.js @@ -0,0 +1,106 @@ +export const searchResponseEvent = { + type: 'SEARCH_RESPONSE', + payload: { + categories: [ + { + __typename: 'AggregationOption', + label: 'Bottoms', + value: '11' + }, + { + __typename: 'AggregationOption', + label: 'Pants & Shorts', + value: '12' + } + ], + facets: [], + page: 1, + perPage: 3, + products: [ + { + __typename: 'ConfigurableProduct', + id: 1144, + uid: 'MTE0NA==', + sku: 'VP01', + name: 'Selena Pants', + small_image: { + __typename: 'ProductImage', + url: + 'https://beacon-rjroszy-vzsrtettsztvg.us-4.magentosite.cloud/media/catalog/product/cache/37f3b100da589f62b6681aad6ae5936f/v/p/vp01-ll_main_2.jpg' + }, + url_key: 'selena-pants', + url_suffix: '.html', + price: { + __typename: 'ProductPrices', + regularPrice: { + __typename: 'Price', + amount: { + __typename: 'Money', + value: 108, + currency: 'USD' + } + } + }, + price_range: { + __typename: 'PriceRange', + maximum_price: { + __typename: 'ProductPrice', + final_price: { + __typename: 'Money', + currency: 'USD', + value: 108 + }, + discount: { + __typename: 'ProductDiscount', + amount_off: 0 + } + } + } + } + ], + searchRequestId: 'selena', + searchUnitId: 'search-bar', + suggestions: [ + { + __typename: 'ConfigurableProduct', + id: 1144, + uid: 'MTE0NA==', + sku: 'VP01', + name: 'Selena Pants', + small_image: { + __typename: 'ProductImage', + url: + 'https://beacon-rjroszy-vzsrtettsztvg.us-4.magentosite.cloud/media/catalog/product/cache/37f3b100da589f62b6681aad6ae5936f/v/p/vp01-ll_main_2.jpg' + }, + url_key: 'selena-pants', + url_suffix: '.html', + price: { + __typename: 'ProductPrices', + regularPrice: { + __typename: 'Price', + amount: { + __typename: 'Money', + value: 108, + currency: 'USD' + } + } + }, + price_range: { + __typename: 'PriceRange', + maximum_price: { + __typename: 'ProductPrice', + final_price: { + __typename: 'Money', + currency: 'USD', + value: 108 + }, + discount: { + __typename: 'ProductDiscount', + amount_off: 0 + } + } + } + } + ] + } +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/__snapshots__/shoppingMiniCartView.spec.js.snap b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__snapshots__/shoppingMiniCartView.spec.js.snap new file mode 100644 index 0000000000..a1e0eaaf71 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/__snapshots__/shoppingMiniCartView.spec.js.snap @@ -0,0 +1,137 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`handle() calls the correct sdk functions with the correct context value 1`] = ` +Object { + "giftMessageSelected": false, + "giftWrappingSelected": false, + "id": "kAR5Gg6uPC6J5wGY0ebecyKfX905epmU", + "items": Array [ + Object { + "configurableOptions": Array [ + Object { + "id": undefined, + "optionLabel": "Fashion Color", + "valueId": "Y29uZmlndXJhYmxlLzE1Ny8zMQ==", + "valueLabel": "Peach", + }, + Object { + "id": undefined, + "optionLabel": "Fashion Size", + "valueId": "Y29uZmlndXJhYmxlLzE5MC80Mw==", + "valueLabel": "L", + }, + ], + "formattedPrice": "", + "id": "MjQ2Ng==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 78, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": "rowena-skirt", + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_2.jpg", + "name": "Rowena Skirt", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 78, + "minimalPrice": 78, + "regularPrice": 78, + }, + "productType": "ConfigurableProduct", + "sku": "VSK02", + }, + "quantity": 1, + }, + Object { + "configurableOptions": null, + "formattedPrice": "", + "id": "MjQ4Mg==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 68, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": "silver-cirque-earrings", + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va17-si_main.jpg", + "name": "Silver Cirque Earrings", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 68, + "minimalPrice": 68, + "regularPrice": 68, + }, + "productType": "SimpleProduct", + "sku": "VA17-SI-NA", + }, + "quantity": 2, + }, + Object { + "configurableOptions": Array [ + Object { + "id": undefined, + "optionLabel": "Fashion Color", + "valueId": "Y29uZmlndXJhYmxlLzE1Ny8zMQ==", + "valueLabel": "Peach", + }, + Object { + "id": undefined, + "optionLabel": "Fashion Size", + "valueId": "Y29uZmlndXJhYmxlLzE5MC80NA==", + "valueLabel": "M", + }, + ], + "formattedPrice": "", + "id": "MjQ4Mw==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 48, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": "antonia-infinity-scarf", + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_2.jpg", + "name": "Antonia Infinity Scarf", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 48, + "minimalPrice": 48, + "regularPrice": 48, + }, + "productType": "ConfigurableProduct", + "sku": "VA04", + }, + "quantity": 3, + }, + ], + "possibleOnepageCheckout": false, + "prices": Object { + "subtotalExcludingTax": Object { + "currency": "USD", + "value": 358, + }, + }, +} +`; diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/addToCart.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/addToCart.spec.js new file mode 100644 index 0000000000..b6995c6bee --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/addToCart.spec.js @@ -0,0 +1,73 @@ +import handler from '../addToCart'; +import { + addSimpleProductEvent, + addConfigurableProductEvent +} from './__mocks__/cartAddItem'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(addSimpleProductEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setShoppingCart: jest.fn() + }, + publish: { + addToCart: jest.fn() + } + }; + + handler.handle(mockSdk, addConfigurableProductEvent); + + expect(mockSdk.context.setShoppingCart).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setShoppingCart.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "giftMessageSelected": false, + "giftWrappingSelected": false, + "id": "kAR5Gg6uPC6J5wGY0ebecyKfX905epmU", + "items": Array [ + Object { + "prices": Object { + "price": Object { + "currency": "USD", + "value": 78, + }, + }, + "product": Object { + "configurableOptions": Array [ + Object { + "optionLabel": undefined, + "valueLabel": undefined, + }, + ], + "name": "Johanna Skirt", + "sku": "VSK03", + }, + }, + ], + "possibleOnepageCheckout": false, + "prices": Object { + "subtotalExcludingTax": Object { + "currency": "USD", + "value": 78, + }, + }, + } + `); + + expect(mockSdk.publish.addToCart).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/categoryPageView.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/categoryPageView.spec.js new file mode 100644 index 0000000000..21a8cd54fe --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/categoryPageView.spec.js @@ -0,0 +1,55 @@ +import handler from '../categoryPageView'; + +import categoryPageViewEvent from './__mocks__/categoryPageView'; +import miniCartViewEvent from './__mocks__/miniCartView'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(categoryPageViewEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + expect(handler.canHandle(miniCartViewEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setCategory: jest.fn(), + setPage: jest.fn() + }, + publish: { + pageView: jest.fn() + } + }; + + handler.handle(mockSdk, categoryPageViewEvent); + + expect(mockSdk.context.setCategory).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setCategory.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "name": "Dresses", + "urlKey": "venia-dresses", + "urlPath": "venia-dresses", + } + `); + + expect(mockSdk.context.setPage).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setPage.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "eventType": "visibilityHidden", + "maxXOffset": 0, + "maxYOffset": 0, + "minXOffset": 0, + "minYOffset": 0, + "pageName": "Dresses", + "pageType": "Category", + } + `); + + expect(mockSdk.publish.pageView).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/completeCheckout.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/completeCheckout.spec.js new file mode 100644 index 0000000000..14a58649a8 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/completeCheckout.spec.js @@ -0,0 +1,70 @@ +import handler from '../completeCheckout'; +import completeCheckoutEvent from './__mocks__/completeCheckout'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(completeCheckoutEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setOrder: jest.fn(), + setPage: jest.fn() + }, + publish: { + placeOrder: jest.fn(), + pageView: jest.fn() + } + }; + + handler.handle(mockSdk, completeCheckoutEvent); + + expect(mockSdk.context.setOrder).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setOrder.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "grandTotal": 40, + "orderId": "001", + "orderType": "checkout", + "payments": Array [ + Object { + "paymentMethodCode": "Visa", + "paymentMethodName": "Visa", + "total": 40, + }, + ], + "shipping": Object { + "shippingAmount": 13, + "shippingMethod": "method", + }, + } + `); + + expect(mockSdk.context.setPage).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setPage.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "eventType": "visibilityHidden", + "maxXOffset": 0, + "maxYOffset": 0, + "minXOffset": 0, + "minYOffset": 0, + "pageName": "Order Confirmation", + "pageType": "Order Confirmation Page", + } + `); + + expect(mockSdk.publish.pageView).toHaveBeenCalledTimes(1); + expect(mockSdk.publish.placeOrder).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/createAccount.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/createAccount.spec.js new file mode 100644 index 0000000000..9c7ca0905f --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/createAccount.spec.js @@ -0,0 +1,43 @@ +import handler from '../createAccount'; +import createAccountEvent from './__mocks__/createAccount'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(createAccountEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setAccount: jest.fn() + }, + publish: { + createAccount: jest.fn() + } + }; + + handler.handle(mockSdk, createAccountEvent); + + expect(mockSdk.context.setAccount).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setAccount.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "emailAddress": "Stephen.Strange@fake.email", + "firstName": "Stephen", + "lastName": "Strange", + } + `); + + expect(mockSdk.publish.createAccount).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/editAccount.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/editAccount.spec.js new file mode 100644 index 0000000000..c68c7c0e1c --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/editAccount.spec.js @@ -0,0 +1,43 @@ +import handler from '../editAccount'; +import editAccountEvent from './__mocks__/editAccount'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(editAccountEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setAccount: jest.fn() + }, + publish: { + editAccount: jest.fn() + } + }; + + handler.handle(mockSdk, editAccountEvent); + + expect(mockSdk.context.setAccount).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setAccount.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "emailAddress": "Stephen.Strange@fake.email", + "firstName": "Stephen", + "lastName": "Strange", + } + `); + + expect(mockSdk.publish.editAccount).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/pageView.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/pageView.spec.js new file mode 100644 index 0000000000..ba5395c9b6 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/pageView.spec.js @@ -0,0 +1,46 @@ +import handler from '../pageView'; +import cmsPageViewEvent from './__mocks__/pageView'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(cmsPageViewEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setPage: jest.fn() + }, + publish: { + pageView: jest.fn() + } + }; + + handler.handle(mockSdk, cmsPageViewEvent); + + expect(mockSdk.context.setPage).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setPage.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "eventType": "visibilityHidden", + "maxXOffset": 0, + "maxYOffset": 0, + "minXOffset": 0, + "minYOffset": 0, + "pageName": "Home Page - Venia", + "pageType": "CMS", + } + `); + + expect(mockSdk.publish.pageView).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/placeOrder.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/placeOrder.spec.js new file mode 100644 index 0000000000..a40d266d4a --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/placeOrder.spec.js @@ -0,0 +1,55 @@ +import handler from '../placeOrder'; +import placeOrderEvent from './__mocks__/placeOrderButtonClicked'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(placeOrderEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setOrder: jest.fn() + }, + publish: { + placeOrder: jest.fn() + } + }; + + handler.handle(mockSdk, placeOrderEvent); + + expect(mockSdk.context.setOrder).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setOrder.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "grandTotal": 466.01, + "orderType": "checkout", + "payments": Array [ + Object { + "paymentMethodCode": "Credit Card", + "paymentMethodName": "Credit Card", + "total": 466.01, + }, + ], + "shipping": Object { + "shippingAmount": 0, + "shippingMethod": "Free", + }, + } + `); + + // Since we're sending this event after loading the order confirmation page, + // We don't need to send it here since we don't have an order number anyways. + expect(mockSdk.publish.placeOrder).toHaveBeenCalledTimes(0); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/productPageView.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/productPageView.spec.js new file mode 100644 index 0000000000..cf2cbcf2a5 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/productPageView.spec.js @@ -0,0 +1,64 @@ +import handler from '../productPageView'; +import productPageViewEvent from './__mocks__/productPageView'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(productPageViewEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setPage: jest.fn(), + setProduct: jest.fn() + }, + publish: { + pageView: jest.fn(), + productPageView: jest.fn() + } + }; + + handler.handle(mockSdk, productPageViewEvent); + + expect(mockSdk.context.setProduct).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setProduct.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "canonicalUrl": "selena-pants", + "name": "Selena Pants", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 40, + }, + "productId": "234d", + "sku": "343g3434t", + } + `); + + expect(mockSdk.context.setPage).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setPage.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "eventType": "visibilityHidden", + "maxXOffset": 0, + "maxYOffset": 0, + "minXOffset": 0, + "minYOffset": 0, + "pageName": "Selena Pants", + "pageType": "PDP", + } + `); + + expect(mockSdk.publish.productPageView).toHaveBeenCalledTimes(1); + expect(mockSdk.publish.pageView).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/searchRequestSent.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/searchRequestSent.spec.js new file mode 100644 index 0000000000..a23525c634 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/searchRequestSent.spec.js @@ -0,0 +1,119 @@ +import handler from '../searchRequestSent'; +import { + searchRequestEvent, + searchbarRequestEvent +} from './__mocks__/searchRequestSent'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(searchRequestEvent)).toBeTruthy(); + }); + + it('returns true for the correct event type', () => { + expect(handler.canHandle(searchbarRequestEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setSearchInput: jest.fn() + }, + publish: { + searchRequestSent: jest.fn() + } + }; + + handler.handle(mockSdk, searchRequestEvent); + + expect(mockSdk.context.setSearchInput).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setSearchInput.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "units": Array [ + Object { + "currentPage": 1, + "filter": Array [ + Object { + "attribute": "category_id", + "in": Array [ + "Bottoms,11", + ], + }, + Object { + "attribute": "fashion_color", + "in": Array [ + "Rain,34", + "Mint,25", + ], + }, + ], + "pageSize": 12, + "phrase": "selena", + "queryTypes": Array [ + "products", + ], + "searchUnitId": "productPage", + "sort": Array [ + Object { + "attribute": "relevance", + "direction": "DESC", + }, + ], + }, + ], + } + `); + + expect(mockSdk.publish.searchRequestSent).toHaveBeenCalledTimes(1); + }); + + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setSearchInput: jest.fn() + }, + publish: { + searchRequestSent: jest.fn() + } + }; + + handler.handle(mockSdk, searchbarRequestEvent); + + expect(mockSdk.context.setSearchInput).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setSearchInput.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "units": Array [ + Object { + "currentPage": 1, + "filter": Array [], + "pageSize": 3, + "phrase": "selena", + "queryTypes": Array [ + "products", + ], + "searchUnitId": "productPage", + "sort": Array [ + Object { + "attribute": undefined, + "direction": undefined, + }, + ], + }, + ], + } + `); + + expect(mockSdk.publish.searchRequestSent).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/searchResponseReceived.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/searchResponseReceived.spec.js new file mode 100644 index 0000000000..e65ec9bafc --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/searchResponseReceived.spec.js @@ -0,0 +1,143 @@ +import handler from '../searchResponseReceived'; +import { searchResponseEvent } from './__mocks__/searchResponseReceived'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(searchResponseEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setSearchResults: jest.fn() + }, + publish: { + searchResponseReceived: jest.fn() + } + }; + + handler.handle(mockSdk, searchResponseEvent); + + expect(mockSdk.context.setSearchResults).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setSearchResults.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "units": Array [ + Object { + "categories": Array [ + Object { + "__typename": "AggregationOption", + "label": "Bottoms", + "value": "11", + }, + Object { + "__typename": "AggregationOption", + "label": "Pants & Shorts", + "value": "12", + }, + ], + "facets": Array [], + "page": 1, + "perPage": 3, + "products": Array [ + Object { + "__typename": "ConfigurableProduct", + "id": 1144, + "name": "Selena Pants", + "price": Object { + "__typename": "ProductPrices", + "regularPrice": Object { + "__typename": "Price", + "amount": Object { + "__typename": "Money", + "currency": "USD", + "value": 108, + }, + }, + }, + "price_range": Object { + "__typename": "PriceRange", + "maximum_price": Object { + "__typename": "ProductPrice", + "discount": Object { + "__typename": "ProductDiscount", + "amount_off": 0, + }, + "final_price": Object { + "__typename": "Money", + "currency": "USD", + "value": 108, + }, + }, + }, + "sku": "VP01", + "small_image": Object { + "__typename": "ProductImage", + "url": "https://beacon-rjroszy-vzsrtettsztvg.us-4.magentosite.cloud/media/catalog/product/cache/37f3b100da589f62b6681aad6ae5936f/v/p/vp01-ll_main_2.jpg", + }, + "uid": "MTE0NA==", + "url_key": "selena-pants", + "url_suffix": ".html", + }, + ], + "searchRequestId": "selena", + "searchUnitId": "search-bar", + "suggestions": Array [ + Object { + "__typename": "ConfigurableProduct", + "id": 1144, + "name": "Selena Pants", + "price": Object { + "__typename": "ProductPrices", + "regularPrice": Object { + "__typename": "Price", + "amount": Object { + "__typename": "Money", + "currency": "USD", + "value": 108, + }, + }, + }, + "price_range": Object { + "__typename": "PriceRange", + "maximum_price": Object { + "__typename": "ProductPrice", + "discount": Object { + "__typename": "ProductDiscount", + "amount_off": 0, + }, + "final_price": Object { + "__typename": "Money", + "currency": "USD", + "value": 108, + }, + }, + }, + "sku": "VP01", + "small_image": Object { + "__typename": "ProductImage", + "url": "https://beacon-rjroszy-vzsrtettsztvg.us-4.magentosite.cloud/media/catalog/product/cache/37f3b100da589f62b6681aad6ae5936f/v/p/vp01-ll_main_2.jpg", + }, + "uid": "MTE0NA==", + "url_key": "selena-pants", + "url_suffix": ".html", + }, + ], + }, + ], + } + `); + + expect(mockSdk.publish.searchResponseReceived).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/shoppingCartPageView.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/shoppingCartPageView.spec.js new file mode 100644 index 0000000000..c3d345327b --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/shoppingCartPageView.spec.js @@ -0,0 +1,198 @@ +import handler from '../shoppingCartPageView'; + +import miniCartViewEvent from './__mocks__/miniCartView'; +import cartPageViewEvent from './__mocks__/cartPageView'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(cartPageViewEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + expect(handler.canHandle(miniCartViewEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setShoppingCart: jest.fn(), + setPage: jest.fn() + }, + publish: { + shoppingCartView: jest.fn(), + pageView: jest.fn() + } + }; + + handler.handle(mockSdk, cartPageViewEvent); + + expect(mockSdk.context.setShoppingCart).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setShoppingCart.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "giftMessageSelected": false, + "giftWrappingSelected": false, + "id": "kAR5Gg6uPC6J5wGY0ebecyKfX905epmU", + "items": Array [ + Object { + "configurableOptions": Array [ + Object { + "id": 157, + "optionLabel": "Fashion Color", + "valueId": "Y29uZmlndXJhYmxlLzE1Ny8zMQ==", + "valueLabel": "Peach", + }, + Object { + "id": 190, + "optionLabel": "Fashion Size", + "valueId": "Y29uZmlndXJhYmxlLzE5MC80Mw==", + "valueLabel": "L", + }, + ], + "formattedPrice": "", + "id": "MjQ2Ng==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 78, + }, + "row_total": Object { + "__typename": "Money", + "value": 78, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": "rowena-skirt", + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/s/vsk02-ll_main_2.jpg", + "name": "Rowena Skirt", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 78, + "minimalPrice": 78, + "regularPrice": 78, + }, + "productType": "ConfigurableProduct", + "sku": "VSK02", + }, + "quantity": 1, + }, + Object { + "configurableOptions": null, + "formattedPrice": "", + "id": "MjQ4Mg==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 68, + }, + "row_total": Object { + "__typename": "Money", + "value": 136, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": "silver-cirque-earrings", + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va17-si_main.jpg", + "name": "Silver Cirque Earrings", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 68, + "minimalPrice": 68, + "regularPrice": 68, + }, + "productType": "SimpleProduct", + "sku": "VA17-SI-NA", + }, + "quantity": 2, + }, + Object { + "configurableOptions": Array [ + Object { + "id": 157, + "optionLabel": "Fashion Color", + "valueId": "Y29uZmlndXJhYmxlLzE1Ny8zMQ==", + "valueLabel": "Peach", + }, + Object { + "id": 190, + "optionLabel": "Fashion Size", + "valueId": "Y29uZmlndXJhYmxlLzE5MC80NA==", + "valueLabel": "M", + }, + ], + "formattedPrice": "", + "id": "MjQ4Mw==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 48, + }, + "row_total": Object { + "__typename": "Money", + "value": 144, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": "antonia-infinity-scarf", + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va04-ll_main_2.jpg", + "name": "Antonia Infinity Scarf", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 48, + "minimalPrice": 48, + "regularPrice": 48, + }, + "productType": "ConfigurableProduct", + "sku": "VA04", + }, + "quantity": 3, + }, + ], + "possibleOnepageCheckout": false, + "prices": Object { + "subtotalExcludingTax": Object { + "currency": "USD", + "value": 358, + }, + }, + } + `); + + expect(mockSdk.publish.shoppingCartView).toHaveBeenCalledTimes(1); + + expect(mockSdk.context.setPage).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setPage.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "eventType": "visibilityHidden", + "maxXOffset": 0, + "maxYOffset": 0, + "minXOffset": 0, + "minYOffset": 0, + "pageName": "Cart", + "pageType": "Cart", + } + `); + + expect(mockSdk.publish.pageView).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/shoppingMiniCartView.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/shoppingMiniCartView.spec.js new file mode 100644 index 0000000000..b59a52490c --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/shoppingMiniCartView.spec.js @@ -0,0 +1,36 @@ +import handler from '../shoppingMiniCartView'; + +import miniCartViewEvent from './__mocks__/miniCartView'; +import cartPageViewEvent from './__mocks__/cartPageView'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(miniCartViewEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + expect(handler.canHandle(cartPageViewEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setShoppingCart: jest.fn() + }, + publish: { + shoppingCartView: jest.fn() + } + }; + + handler.handle(mockSdk, miniCartViewEvent); + + expect(mockSdk.context.setShoppingCart).toHaveBeenCalledTimes(1); + expect( + mockSdk.context.setShoppingCart.mock.calls[0][0] + ).toMatchSnapshot(); + + expect(mockSdk.publish.shoppingCartView).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/signIn.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/signIn.spec.js new file mode 100644 index 0000000000..3fd067652b --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/signIn.spec.js @@ -0,0 +1,73 @@ +import handler from '../signIn'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + const mockEvent = { + type: 'USER_SIGN_IN', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setAccount: jest.fn(), + setShopper: jest.fn() + }, + publish: { + signIn: jest.fn() + } + }; + + const mockEvent = { + type: 'USER_SIGN_IN', + payload: { + email: 'doctor.strange@fake.email', + firstname: 'Stephen', + is_subscribed: false, + lastname: 'Strange', + __typename: 'Customer' + } + }; + + handler.handle(mockSdk, mockEvent); + + expect(mockSdk.context.setShopper).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setShopper.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "shopperId": "logged-in", + } + `); + + expect(mockSdk.context.setAccount).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setAccount.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "emailAddress": "doctor.strange@fake.email", + "firstName": "Stephen", + "lastName": "Strange", + } + `); + + expect(mockSdk.publish.signIn).toHaveBeenCalledTimes(1); + expect(mockSdk.publish.signIn.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "personalEmail": Object { + "address": "doctor.strange@fake.email", + }, + } + `); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/__tests__/startCheckout.spec.js b/packages/extensions/experience-platform-connector/src/handlers/__tests__/startCheckout.spec.js new file mode 100644 index 0000000000..fa6138cb27 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/__tests__/startCheckout.spec.js @@ -0,0 +1,193 @@ +import handler from '../startCheckout'; +import startCheckoutEvent from './__mocks__/checkoutPageView'; + +describe('canHandle()', () => { + it('returns true for the correct event type', () => { + expect(handler.canHandle(startCheckoutEvent)).toBeTruthy(); + }); + + it('returns false for non supported event types', () => { + const mockEvent = { + type: 'USER_SIGN_OUT', + payload: {} + }; + expect(handler.canHandle(mockEvent)).toBeFalsy(); + }); +}); + +describe('handle()', () => { + it('calls the correct sdk functions with the correct context value', () => { + const mockSdk = { + context: { + setPage: jest.fn(), + setShoppingCart: jest.fn() + }, + publish: { + pageView: jest.fn(), + initiateCheckout: jest.fn() + } + }; + + handler.handle(mockSdk, startCheckoutEvent); + + expect(mockSdk.context.setPage).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setPage.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "eventType": "visibilityHidden", + "maxXOffset": 0, + "maxYOffset": 0, + "minXOffset": 0, + "minYOffset": 0, + "pageName": "Checkout", + "pageType": "Checkout", + } + `); + + expect(mockSdk.context.setShoppingCart).toHaveBeenCalledTimes(1); + expect(mockSdk.context.setShoppingCart.mock.calls[0][0]) + .toMatchInlineSnapshot(` + Object { + "giftMessageSelected": false, + "giftWrappingSelected": false, + "id": "adaIGWl4UsoKOGIeX17FPrnMq3bXHVZA", + "items": Array [ + Object { + "configurableOptions": Array [ + Object { + "id": undefined, + "optionLabel": "Fashion Color", + "valueId": "Y29uZmlndXJhYmxlLzE1Ny8zNQ==", + "valueLabel": "Mint", + }, + Object { + "id": undefined, + "optionLabel": "Fashion Size", + "valueId": "Y29uZmlndXJhYmxlLzE5MC80OQ==", + "valueLabel": "6", + }, + ], + "formattedPrice": "", + "id": "Mjk3Nw==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 98, + }, + "row_total": Object { + "__typename": "Money", + "value": 294, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": undefined, + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/p/vp08-rn_main_2.jpg", + "name": "Bella Eyelet Capris", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 98, + "minimalPrice": 98, + "regularPrice": 98, + }, + "productType": "ConfigurableProduct", + "sku": "VP08", + }, + "quantity": 3, + }, + Object { + "configurableOptions": Array [ + Object { + "id": undefined, + "optionLabel": "Fashion Size", + "valueId": "Y29uZmlndXJhYmxlLzE5MC80NA==", + "valueLabel": "M", + }, + ], + "formattedPrice": "", + "id": "Mjk3OQ==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 38, + }, + "row_total": Object { + "__typename": "Money", + "value": 38, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": undefined, + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va09-br_main_2.jpg", + "name": "Thin Leather Braided Belt", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 38, + "minimalPrice": 38, + "regularPrice": 38, + }, + "productType": "ConfigurableProduct", + "sku": "VA09", + }, + "quantity": 1, + }, + Object { + "configurableOptions": null, + "formattedPrice": "", + "id": "Mjk4Nw==", + "prices": Object { + "__typename": "CartItemPrices", + "price": Object { + "__typename": "Money", + "currency": "USD", + "value": 98, + }, + "row_total": Object { + "__typename": "Money", + "value": 98, + }, + "total_item_discount": Object { + "__typename": "Money", + "value": 0, + }, + }, + "product": Object { + "canonicalUrl": undefined, + "mainImageUrl": "https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/catalog/product/cache/609faca36a4bc16a754bc2f43c184970/v/a/va22-si_main.jpg", + "name": "Silver Amor Bangle Set", + "pricing": Object { + "currencyCode": "USD", + "maximalPrice": 98, + "minimalPrice": 98, + "regularPrice": 98, + }, + "productType": "SimpleProduct", + "sku": "VA22-SI-NA", + }, + "quantity": 1, + }, + ], + "possibleOnepageCheckout": false, + "prices": Object { + "subtotalExcludingTax": Object { + "currency": "USD", + "value": 430, + }, + }, + } + `); + + expect(mockSdk.publish.pageView).toHaveBeenCalledTimes(1); + expect(mockSdk.publish.initiateCheckout).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/extensions/experience-platform-connector/src/handlers/addToCart.js b/packages/extensions/experience-platform-connector/src/handlers/addToCart.js new file mode 100644 index 0000000000..23d9f1a62f --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/addToCart.js @@ -0,0 +1,60 @@ +const canHandle = event => event.type === 'CART_ADD_ITEM'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { + cartId, + currencyCode, + priceTotal, + quantity, + name, + sku, + selectedOptions + } = payload; + + const configurableOptions = selectedOptions + ? [ + { + optionLabel: selectedOptions.attribute, + valueLabel: selectedOptions.value + } + ] + : null; + + const cartItemContext = { + id: cartId, + prices: { + subtotalExcludingTax: { + value: priceTotal * quantity, + currency: currencyCode + } + }, + items: [ + { + product: { + name: name, + sku: sku, + configurableOptions: configurableOptions + }, + prices: { + price: { + value: priceTotal, + currency: currencyCode + } + } + } + ], + possibleOnepageCheckout: false, + giftMessageSelected: false, + giftWrappingSelected: false + }; + + sdk.context.setShoppingCart(cartItemContext); + sdk.publish.addToCart(); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/categoryPageView.js b/packages/extensions/experience-platform-connector/src/handlers/categoryPageView.js new file mode 100644 index 0000000000..3eb57ef80a --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/categoryPageView.js @@ -0,0 +1,34 @@ +const canHandle = event => event.type === 'CATEGORY_PAGE_VIEW'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { name, url_key, url_path } = payload; + + const categoryContext = { + name, + urlKey: url_key, + urlPath: url_path + }; + + sdk.context.setCategory(categoryContext); + + // Send out page view event + const pageContext = { + pageType: 'Category', + pageName: name, + eventType: 'visibilityHidden', + maxXOffset: 0, + maxYOffset: 0, + minXOffset: 0, + minYOffset: 0 + }; + + sdk.context.setPage(pageContext); + sdk.publish.pageView(); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/completeCheckout.js b/packages/extensions/experience-platform-connector/src/handlers/completeCheckout.js new file mode 100644 index 0000000000..a60738ab07 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/completeCheckout.js @@ -0,0 +1,49 @@ +const canHandle = event => event.type === 'ORDER_CONFIRMATION_PAGE_VIEW'; + +const handle = (sdk, event) => { + const { payload } = event; + + const grandTotal = payload.amount.grand_total.value; + + const { order_number, payment, shipping } = payload; + + const orderContext = { + orderId: order_number, + grandTotal: grandTotal, + orderType: 'checkout', + payments: [ + { + paymentMethodCode: payment.title, + paymentMethodName: payment.title, + total: grandTotal + } + ], + shipping: { + shippingMethod: shipping[0].method_title, + shippingAmount: shipping[0].amount.value + } + }; + + sdk.context.setOrder(orderContext); + + sdk.publish.placeOrder(); + + // Send out page view event + const pageContext = { + pageType: 'Order Confirmation Page', + pageName: 'Order Confirmation', + eventType: 'visibilityHidden', + maxXOffset: 0, + maxYOffset: 0, + minXOffset: 0, + minYOffset: 0 + }; + + sdk.context.setPage(pageContext); + sdk.publish.pageView(); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/createAccount.js b/packages/extensions/experience-platform-connector/src/handlers/createAccount.js new file mode 100644 index 0000000000..f2de5e624a --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/createAccount.js @@ -0,0 +1,25 @@ +const canHandle = event => event.type === 'USER_CREATE_ACCOUNT'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { firstName, lastName, email } = payload; + + const accountContext = { + firstName, + lastName, + emailAddress: email + }; + + sdk.context.setAccount(accountContext); + sdk.publish.createAccount({ + personalEmail: { + address: email + } + }); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/editAccount.js b/packages/extensions/experience-platform-connector/src/handlers/editAccount.js new file mode 100644 index 0000000000..87ffc90b1b --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/editAccount.js @@ -0,0 +1,25 @@ +const canHandle = event => event.type === 'USER_ACCOUNT_UPDATE'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { email, firstName, lastName } = payload; + + const accountContext = { + firstName, + lastName, + emailAddress: email + }; + + sdk.context.setAccount(accountContext); + sdk.publish.editAccount({ + personalEmail: { + address: email + } + }); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/pageView.js b/packages/extensions/experience-platform-connector/src/handlers/pageView.js new file mode 100644 index 0000000000..61dcbf53ce --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/pageView.js @@ -0,0 +1,26 @@ +const canHandle = event => event.type === 'CMS_PAGE_VIEW'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { title } = payload; + + const context = { + pageType: 'CMS', + pageName: title, + eventType: 'visibilityHidden', + maxXOffset: 0, + maxYOffset: 0, + minXOffset: 0, + minYOffset: 0 + }; + + sdk.context.setPage(context); + + sdk.publish.pageView(); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/placeOrder.js b/packages/extensions/experience-platform-connector/src/handlers/placeOrder.js new file mode 100644 index 0000000000..b22c1510c8 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/placeOrder.js @@ -0,0 +1,32 @@ +const canHandle = event => event.type === 'CHECKOUT_PLACE_ORDER_BUTTON_CLICKED'; + +const handle = (sdk, event) => { + const { payload } = event; + + const grandTotal = payload.amount.grand_total.value; + + const { payment, shipping } = payload; + + const orderContext = { + grandTotal: grandTotal, + orderType: 'checkout', + payments: [ + { + paymentMethodCode: payment.title, + paymentMethodName: payment.title, + total: grandTotal + } + ], + shipping: { + shippingMethod: shipping[0].method_title, + shippingAmount: shipping[0].amount.value + } + }; + + sdk.context.setOrder(orderContext); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/productPageView.js b/packages/extensions/experience-platform-connector/src/handlers/productPageView.js new file mode 100644 index 0000000000..531b910473 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/productPageView.js @@ -0,0 +1,41 @@ +const canHandle = event => event.type === 'PRODUCT_PAGE_VIEW'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { name, id, currency_code, price_range, sku, url_key } = payload; + + const pageContext = { + pageType: 'PDP', + pageName: name, + eventType: 'visibilityHidden', + maxXOffset: 0, + maxYOffset: 0, + minXOffset: 0, + minYOffset: 0 + }; + + sdk.context.setPage(pageContext); + + sdk.publish.pageView(); + + const productContext = { + productId: id, + name, + sku, + pricing: { + currencyCode: currency_code, + maximalPrice: price_range.maximum_price.final_price + }, + canonicalUrl: url_key + }; + + sdk.context.setProduct(productContext); + + sdk.publish.productPageView(); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/searchRequestSent.js b/packages/extensions/experience-platform-connector/src/handlers/searchRequestSent.js new file mode 100644 index 0000000000..2f199b71d3 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/searchRequestSent.js @@ -0,0 +1,38 @@ +const canHandle = event => + ['SEARCH_REQUEST', 'SEARCHBAR_REQUEST'].includes(event.type); + +const handle = (sdk, event) => { + const { payload } = event; + + const { query, pageSize, currentPage, refinements, sort } = payload; + + const filter = refinements.map(refinement => { + const { attribute, value } = refinement; + return { + attribute: attribute, + in: Array.from(value.values()) + }; + }); + + const requestContext = { + units: [ + { + searchUnitId: 'productPage', + queryTypes: ['products'], + phrase: query, + pageSize: pageSize, + currentPage: currentPage, + filter: filter, + sort: [{ attribute: sort?.attribute, direction: sort?.order }] + } + ] + }; + + sdk.context.setSearchInput(requestContext); + sdk.publish.searchRequestSent(); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/searchResponseReceived.js b/packages/extensions/experience-platform-connector/src/handlers/searchResponseReceived.js new file mode 100644 index 0000000000..27555202d3 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/searchResponseReceived.js @@ -0,0 +1,39 @@ +const canHandle = event => event.type === 'SEARCH_RESPONSE'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { + categories, + facets, + page, + perPage, + products, + searchRequestId, + searchUnitId, + suggestions + } = payload; + + const searchResultsContext = { + units: [ + { + categories, + facets, + page, + perPage, + products, + searchRequestId, + searchUnitId, + suggestions + } + ] + }; + + sdk.context.setSearchResults(searchResultsContext); + sdk.publish.searchResponseReceived(searchUnitId, searchResultsContext); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/shoppingCartPageView.js b/packages/extensions/experience-platform-connector/src/handlers/shoppingCartPageView.js new file mode 100644 index 0000000000..3b7ff32105 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/shoppingCartPageView.js @@ -0,0 +1,45 @@ +import { getCartTotal, getCurrency, getFormattedProducts } from '../utils'; + +const canHandle = event => event.type === 'CART_PAGE_VIEW'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { cart_id: id, products } = payload; + + const cartContext = { + id, + prices: { + subtotalExcludingTax: { + value: getCartTotal(products), + currency: getCurrency(products) + } + }, + items: getFormattedProducts(products), + possibleOnepageCheckout: false, + giftMessageSelected: false, + giftWrappingSelected: false + }; + + sdk.context.setShoppingCart(cartContext); + sdk.publish.shoppingCartView(); + + // Send out page view event + const pageContext = { + pageType: 'Cart', + pageName: 'Cart', + eventType: 'visibilityHidden', + maxXOffset: 0, + maxYOffset: 0, + minXOffset: 0, + minYOffset: 0 + }; + + sdk.context.setPage(pageContext); + sdk.publish.pageView(); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/shoppingMiniCartView.js b/packages/extensions/experience-platform-connector/src/handlers/shoppingMiniCartView.js new file mode 100644 index 0000000000..4a083d7e3b --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/shoppingMiniCartView.js @@ -0,0 +1,31 @@ +import { getCartTotal, getCurrency, getFormattedProducts } from '../utils'; + +const canHandle = event => event.type === 'MINI_CART_VIEW'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { cartId: id, products } = payload; + + const cartContext = { + id: id, + prices: { + subtotalExcludingTax: { + value: getCartTotal(products), + currency: getCurrency(products) + } + }, + items: getFormattedProducts(products), + possibleOnepageCheckout: false, + giftMessageSelected: false, + giftWrappingSelected: false + }; + + sdk.context.setShoppingCart(cartContext); + sdk.publish.shoppingCartView(); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/signIn.js b/packages/extensions/experience-platform-connector/src/handlers/signIn.js new file mode 100644 index 0000000000..9bc490705e --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/signIn.js @@ -0,0 +1,29 @@ +const canHandle = event => event.type === 'USER_SIGN_IN'; + +const handle = (sdk, event) => { + const { payload } = event; + + sdk.context.setShopper({ + shopperId: 'logged-in' + }); + + const { firstname, lastname, email } = payload; + + const accountContext = { + firstName: firstname, + lastName: lastname, + emailAddress: email + }; + + sdk.context.setAccount(accountContext); + sdk.publish.signIn({ + personalEmail: { + address: email + } + }); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/handlers/startCheckout.js b/packages/extensions/experience-platform-connector/src/handlers/startCheckout.js new file mode 100644 index 0000000000..65877d534d --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/handlers/startCheckout.js @@ -0,0 +1,45 @@ +import { getCartTotal, getCurrency, getFormattedProducts } from '../utils'; + +const canHandle = event => event.type === 'CHECKOUT_PAGE_VIEW'; + +const handle = (sdk, event) => { + const { payload } = event; + + const { cart_id, products } = payload; + + // Send out page view event + const pageContext = { + pageType: 'Checkout', + pageName: 'Checkout', + eventType: 'visibilityHidden', + maxXOffset: 0, + maxYOffset: 0, + minXOffset: 0, + minYOffset: 0 + }; + + sdk.context.setPage(pageContext); + sdk.publish.pageView(); + + const cartContext = { + id: cart_id, + prices: { + subtotalExcludingTax: { + value: getCartTotal(products), + currency: getCurrency(products) + } + }, + items: getFormattedProducts(products), + possibleOnepageCheckout: false, + giftMessageSelected: false, + giftWrappingSelected: false + }; + + sdk.context.setShoppingCart(cartContext); + sdk.publish.initiateCheckout(); +}; + +export default { + canHandle, + handle +}; diff --git a/packages/extensions/experience-platform-connector/src/hooks/useExtensionContext.js b/packages/extensions/experience-platform-connector/src/hooks/useExtensionContext.js new file mode 100644 index 0000000000..323cdeb30a --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/hooks/useExtensionContext.js @@ -0,0 +1,21 @@ +import { useLazyQuery } from '@apollo/client'; +import { GET_EXTENSION_CONTEXT } from '../queries/getExtensionContext.js'; +import { useEffect } from 'react'; + +const useExtensionContext = () => { + const [ + fetchExtensionContext, + { called, data, loading, error } + ] = useLazyQuery(GET_EXTENSION_CONTEXT); + useEffect(() => { + fetchExtensionContext(); + }, [fetchExtensionContext]); + + return { + ready: called && !loading, + data, + error + }; +}; + +export default useExtensionContext; diff --git a/packages/extensions/experience-platform-connector/src/main.js b/packages/extensions/experience-platform-connector/src/main.js new file mode 100644 index 0000000000..d2579fc6cb --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/main.js @@ -0,0 +1,116 @@ +import { useEventingContext } from '@magento/peregrine/lib/context/eventing'; +import { useUserContext } from '@magento/peregrine/lib/context/user'; +import { useEffect, useState } from 'react'; +import { default as handleEvent } from './handleEvent'; +import useExtensionContext from './hooks/useExtensionContext'; + +export default original => props => { + const [{ isSignedIn, currentUser }] = useUserContext(); + const [observable] = useEventingContext(); + + const [sdk, setSdk] = useState(); + + const { + data: storefrontData, + ready: storefrontDataReady, + errors + } = useExtensionContext(); + + useEffect(() => { + if (errors) { + console.error('Experience Platform Connector Error', errors); + return; + } + + if (storefrontDataReady && storefrontData) { + const { + dataServicesStorefrontInstanceContext: storefrontContext, + experienceConnectorContext: connectorContext + } = storefrontData; + + import('@adobe/magento-storefront-events-sdk').then(mse => { + if (!window.magentoStorefrontEvents) { + window.magentoStorefrontEvents = mse; + } + + const orgId = storefrontContext.ims_org_id; + const datastreamId = connectorContext.datastream_id; + + if (orgId && datastreamId) { + mse.context.setAEP({ + imsOrgId: orgId, + datastreamId: datastreamId + }); + + mse.context.setEventForwarding({ + aep: true + }); + + // Set storefront context + mse.context.setStorefrontInstance({ + environmentId: storefrontContext.environment_id, + environment: storefrontContext.environment, + storeUrl: storefrontContext.store_url, + websiteId: storefrontContext.website_id, + websiteCode: storefrontContext.website_code, + storeId: storefrontContext.store_id, + storeCode: storefrontContext.store_code, + storeViewId: storefrontContext.store_view_id, + storeViewCode: storefrontContext.store_view_code, + websiteName: storefrontContext.website_name, + storeName: storefrontContext.store_name, + storeViewName: storefrontContext.store_view_name, + baseCurrencyCode: storefrontContext.base_currency_code, + storeViewCurrencyCode: + storefrontContext.store_view_currency_code, + catalogExtensionVersion: + storefrontContext.catalog_extension_version + }); + + import('@adobe/magento-storefront-event-collector').then( + msec => { + msec; + setSdk(mse); + } + ); + } + }); + } + }, [storefrontDataReady, storefrontData, setSdk, errors]); + + useEffect(() => { + if (sdk) { + const sub = observable.subscribe(async event => { + handleEvent(sdk, event); + }); + + return () => { + sub.unsubscribe(); + }; + } + }, [sdk, observable]); + + // Sets shopper context on initial load (when shopper context is null) + useEffect(() => { + if (sdk && !sdk.context.getShopper()) { + if (isSignedIn) { + sdk.context.setShopper({ + shopperId: 'logged-in' + }); + + sdk.context.setAccount({ + firstName: currentUser.firstname, + lastName: currentUser.lastname, + emailAddress: currentUser.email, + accountType: currentUser.__typename + }); + } else { + sdk.context.setShopper({ + shopperId: 'guest' + }); + } + } + }, [sdk, isSignedIn, currentUser]); + + return original(props); +}; diff --git a/packages/extensions/experience-platform-connector/src/queries/getExtensionContext.js b/packages/extensions/experience-platform-connector/src/queries/getExtensionContext.js new file mode 100644 index 0000000000..d9b9996e51 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/queries/getExtensionContext.js @@ -0,0 +1,31 @@ +import { gql } from '@apollo/client'; + +export const GET_EXTENSION_CONTEXT = gql` + query experiencePlatformConnectorContext { + dataServicesStorefrontInstanceContext { + environment_id + environment + store_url + website_id + website_code + store_id + store_code + store_view_id + store_view_code + website_name + store_name + store_view_name + base_currency_code + store_view_currency_code + catalog_extension_version + ims_org_id + } + experienceConnectorContext { + datastream_id + } + } +`; + +export default { + getExtensionContext: GET_EXTENSION_CONTEXT +}; diff --git a/packages/extensions/experience-platform-connector/src/utils.js b/packages/extensions/experience-platform-connector/src/utils.js new file mode 100644 index 0000000000..852fa27503 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/utils.js @@ -0,0 +1,101 @@ +/** Cart focused utils **/ + +/** + * Returns the total sum from an array of cart products + * + * @param {Array} products + * @returns {Number} Sum of all product prices in the array + */ +export const getCartTotal = products => { + return products + ? products.reduce( + (previous, current) => + current.prices.price.value * current.quantity + previous, + 0 + ) + : 0; +}; + +/** + * Get the currency from the first product item in an array of cart products + * + * @param {Array} products + * @returns {String} Currency code from the first product item or null if array is empty + */ +export const getCurrency = products => + products && products.length > 0 ? products[0].prices.price.currency : null; + +/** + * Transforms an array of cart products into a format compatible with the + * Magento Storefront Event SDK + * + * @param {Array} products + * @returns {Array} Array of data compatible with the Magento Storefront Event SDK + */ +export const getFormattedProducts = products => { + return products + ? products.map(item => { + const { + uid, + product, + prices, + quantity, + configurable_options: options + } = item; + + const { + name, + sku, + __typename: type, + url_key: url, + small_image: image, + thumbnail + } = product; + + const formattedOptions = options + ? options.map(option => { + const { + id, + option_label, + value_label, + configurable_product_option_value_uid: valueId + } = option; + return { + id: id, + optionLabel: option_label, + valueId: valueId, + valueLabel: value_label + }; + }) + : null; + + const imageUrl = image + ? image.url + : thumbnail + ? thumbnail.url + : null; + + return { + formattedPrice: '', + id: uid, + prices: prices, + product: { + name: name, + sku: sku, + productType: type, + pricing: { + regularPrice: prices.price.value, + minimalPrice: prices.price.value, + maximalPrice: prices.price.value, + currencyCode: prices.price.currency + }, + canonicalUrl: url, + mainImageUrl: imageUrl + }, + + configurableOptions: formattedOptions, + quantity: quantity + }; + }) + : null; +}; diff --git a/packages/extensions/experience-platform-connector/src/wrappers/wrapUseAccountMenu.js b/packages/extensions/experience-platform-connector/src/wrappers/wrapUseAccountMenu.js new file mode 100644 index 0000000000..e42047cef9 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/wrappers/wrapUseAccountMenu.js @@ -0,0 +1,29 @@ +import { useCallback } from 'react'; + +// Wrapper for the useAccountMenu() talon +const wrapUseAccountMenu = useAccountMenu => { + return props => { + const talonProps = useAccountMenu(props); + + const { handleSignOut, ...restProps } = talonProps; + + const sdk = window.magentoStorefrontEvents; + + // Need to publish the sign out event before actually calling the original sign out + // callback to make sure data is sent before the page refreshes + const newHandleSignOut = useCallback(async () => { + if (sdk) { + sdk.publish.signOut(); + } + + handleSignOut(); + }, [sdk, handleSignOut]); + + return { + handleSignOut: newHandleSignOut, + ...restProps + }; + }; +}; + +export default wrapUseAccountMenu; diff --git a/packages/extensions/experience-platform-connector/src/wrappers/wrapUseAutocomplete.js b/packages/extensions/experience-platform-connector/src/wrappers/wrapUseAutocomplete.js new file mode 100644 index 0000000000..3a727318b4 --- /dev/null +++ b/packages/extensions/experience-platform-connector/src/wrappers/wrapUseAutocomplete.js @@ -0,0 +1,48 @@ +import { useEffect } from 'react'; +import { useEventingContext } from '@magento/peregrine/lib/context/eventing'; + +// Wrapper for the useAutocomplete() talon +const wrapUseAutocomplete = useAutocomplete => { + return props => { + const talonProps = useAutocomplete(props); + const [, { dispatch }] = useEventingContext(); + + const { + categories, + displayResult, + messageType, + value, + ...restProps + } = talonProps; + + useEffect(() => { + if ( + messageType === 'RESULT_SUMMARY' || + messageType === 'EMPTY_RESULT' + ) { + dispatch({ + type: 'SEARCH_RESPONSE', + payload: { + categories: categories || [], + facets: [], + page: 1, + perPage: 3, // Same value used in GQL query + products: displayResult || [], + searchRequestId: value, + searchUnitId: 'search-bar', + suggestions: displayResult || [] + } + }); + } + }); + + return { + displayResult, + messageType, + value, + ...restProps + }; + }; +}; + +export default wrapUseAutocomplete; diff --git a/packages/peregrine/lib/talons/SearchBar/__tests__/__snapshots__/useAutocomplete.spec.js.snap b/packages/peregrine/lib/talons/SearchBar/__tests__/__snapshots__/useAutocomplete.spec.js.snap new file mode 100644 index 0000000000..ccd285b857 --- /dev/null +++ b/packages/peregrine/lib/talons/SearchBar/__tests__/__snapshots__/useAutocomplete.spec.js.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`dispatches an event when valid and visible are true and there is text value 1`] = ` +Object { + "payload": Object { + "currentPage": 1, + "pageSize": 3, + "query": "MOCK_VALUE", + "refinements": Array [], + }, + "type": "SEARCHBAR_REQUEST", +} +`; diff --git a/packages/peregrine/lib/talons/SearchBar/__tests__/useAutocomplete.spec.js b/packages/peregrine/lib/talons/SearchBar/__tests__/useAutocomplete.spec.js index 54935658ba..f2cd920e6c 100644 --- a/packages/peregrine/lib/talons/SearchBar/__tests__/useAutocomplete.spec.js +++ b/packages/peregrine/lib/talons/SearchBar/__tests__/useAutocomplete.spec.js @@ -5,6 +5,7 @@ import useFieldState from '@magento/peregrine/lib/hooks/hook-wrappers/useInforme import { runQuery, useLazyQuery } from '@apollo/client'; import { useAutocomplete } from '../useAutocomplete'; import createTestInstance from '../../../util/createTestInstance'; +import { useEventingContext } from '../../../context/eventing'; jest.mock('informed', () => ({ ...jest.requireActual('informed') @@ -36,6 +37,10 @@ jest.mock('lodash.debounce', () => { return callback => args => callback(args); }); +jest.mock('../../../context/eventing', () => ({ + useEventingContext: jest.fn().mockReturnValue([{}, { dispatch: jest.fn() }]) +})); + const log = jest.fn(); const Component = props => { @@ -67,6 +72,24 @@ test('runs query when valid is true', () => { ); }); +test('dispatches an event when valid and visible are true and there is text value', () => { + const [, { dispatch }] = useEventingContext(); + + useFieldState.mockReturnValueOnce({ + value: 'MOCK_VALUE' + }); + + createTestInstance( +
+ ); + + expect(dispatch).toHaveBeenCalledTimes(1); + expect(dispatch.mock.calls[0][0]).toMatchSnapshot(); +}); + test('renders a hint message', () => { createTestInstance(