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(
diff --git a/packages/peregrine/lib/talons/SearchBar/useAutocomplete.js b/packages/peregrine/lib/talons/SearchBar/useAutocomplete.js index f27e953f8e..99b712d3a1 100644 --- a/packages/peregrine/lib/talons/SearchBar/useAutocomplete.js +++ b/packages/peregrine/lib/talons/SearchBar/useAutocomplete.js @@ -2,6 +2,7 @@ import { useEffect, useMemo } from 'react'; import useFieldState from '@magento/peregrine/lib/hooks/hook-wrappers/useInformedFieldStateWrapper'; import { useLazyQuery } from '@apollo/client'; import debounce from 'lodash.debounce'; +import { useEventingContext } from '../../context/eventing'; /** * @typedef { import("graphql").DocumentNode } DocumentNode @@ -21,6 +22,8 @@ export const useAutocomplete = props => { visible } = props; + const [, { dispatch }] = useEventingContext(); + // Prepare to run the queries. const [runSearch, productResult] = useLazyQuery(getAutocompleteResults, { fetchPolicy: 'cache-and-network', @@ -44,12 +47,25 @@ export const useAutocomplete = props => { useEffect(() => { if (valid && visible) { debouncedRunQuery(value); + + if (value) { + dispatch({ + type: 'SEARCHBAR_REQUEST', + payload: { + query: value, + currentPage: 1, // Same value used in GQL query + pageSize: 3, // Same value used in GQL query + refinements: [] + } + }); + } } - }, [debouncedRunQuery, valid, value, visible]); + }, [debouncedRunQuery, valid, value, visible, dispatch]); const { data, error, loading } = productResult; // Handle results. + const categories = data && data.products?.aggregations[1]?.options; const products = data && data.products; const filters = data && data.products.aggregations; const hasResult = products && products.items; @@ -73,6 +89,7 @@ export const useAutocomplete = props => { } return { + categories, displayResult, filters, messageType, diff --git a/packages/peregrine/lib/talons/SearchPage/__tests__/__snapshots__/useSearchPage.spec.js.snap b/packages/peregrine/lib/talons/SearchPage/__tests__/__snapshots__/useSearchPage.spec.js.snap index 4cd8175ef6..5070948f12 100644 --- a/packages/peregrine/lib/talons/SearchPage/__tests__/__snapshots__/useSearchPage.spec.js.snap +++ b/packages/peregrine/lib/talons/SearchPage/__tests__/__snapshots__/useSearchPage.spec.js.snap @@ -40,6 +40,8 @@ Object { exports[`searchCategory should dispatch event when search request takes effect 1`] = ` Object { "payload": Object { + "currentPage": 3, + "pageSize": 12, "query": "Search Query Value", "refinements": Array [ Object { diff --git a/packages/peregrine/lib/talons/SearchPage/useSearchPage.js b/packages/peregrine/lib/talons/SearchPage/useSearchPage.js index 38255135f5..2fc583dafa 100644 --- a/packages/peregrine/lib/talons/SearchPage/useSearchPage.js +++ b/packages/peregrine/lib/talons/SearchPage/useSearchPage.js @@ -199,7 +199,9 @@ export const useSearchPage = (props = {}) => { sort: { attribute: sortAttribute, order: sortDirection - } + }, + pageSize: Number(pageSize), + currentPage: Number(currentPage) } }); searched.current = true; diff --git a/packages/venia-concept/package.json b/packages/venia-concept/package.json index ef8d49eae2..2aa727486b 100644 --- a/packages/venia-concept/package.json +++ b/packages/venia-concept/package.json @@ -52,6 +52,7 @@ "@babel/runtime": "~7.15.3", "@magento/babel-preset-peregrine": "~1.2.1", "@magento/eslint-config": "~1.5.0", + "@magento/experience-platform-connector": "0.0.1", "@magento/pagebuilder": "~7.3.0", "@magento/peregrine": "~12.4.0", "@magento/pwa-theme-venia": "~1.3.0", @@ -147,4 +148,4 @@ "intercept": "./local-intercept.js" } } -} +} \ No newline at end of file diff --git a/packages/venia-ui/lib/components/SearchBar/__tests__/autocomplete.spec.js b/packages/venia-ui/lib/components/SearchBar/__tests__/autocomplete.spec.js index 3db5a1f08b..945960c432 100644 --- a/packages/venia-ui/lib/components/SearchBar/__tests__/autocomplete.spec.js +++ b/packages/venia-ui/lib/components/SearchBar/__tests__/autocomplete.spec.js @@ -11,6 +11,10 @@ import { IntlProvider } from 'react-intl'; jest.mock('../../../classify'); jest.mock('../suggestions', () => () => null); +jest.mock('@magento/peregrine/lib/context/eventing', () => ({ + useEventingContext: jest.fn().mockReturnValue([{}, { dispatch: jest.fn() }]) +})); + const cache = new InMemoryCache({ typePolicies }); diff --git a/yarn.lock b/yarn.lock index 7e96e1801f..a7fddc5974 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,13 +2,25 @@ # yarn lockfile v1 -"@adobe/adobe-client-data-layer@~1.1.3": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@adobe/adobe-client-data-layer/-/adobe-client-data-layer-1.1.5.tgz#1bbf9b8038d52652aea8f6eb1a01721f94b14766" - integrity sha512-5z4MaibmcSrKIfRJx5Q0V8dBE7yrTFfyocznpSZ37LGHyjb93Bs35vKafueW/IT/6UUREXy0rEbmDsW+VNw8Gg== +"@adobe/adobe-client-data-layer@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@adobe/adobe-client-data-layer/-/adobe-client-data-layer-2.0.2.tgz#48f72acbee0ddbcb4edd4b37143a45a16fd69e0c" + integrity sha512-Ah0MoI+RF84G3QCjV8SF0kvPjGCkKhU/tWEe2TpTq/5nuxQy89215A6oOm4WuLv1tIV2kQ4PJooL+0LgcjUb9w== dependencies: lodash "^4.17.15" +"@adobe/alloy@^2.9.0": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@adobe/alloy/-/alloy-2.10.1.tgz#55ccc534d2c4fcf7249b0fa080f83c587eff951c" + integrity sha512-0LGE3Ek0LGpsEDSUWl2lQfyhfc+smfAq4sv8qKEpYfzvY+PQnz5zVhc/KlYMegs6tQY6zGnd5xArG2BIOkNiyg== + dependencies: + "@adobe/reactor-cookie" "^1.0.0" + "@adobe/reactor-load-script" "^1.1.1" + "@adobe/reactor-object-assign" "^1.0.0" + "@adobe/reactor-query-string" "^1.0.0" + css.escape "^1.5.1" + uuid "^3.3.2" + "@adobe/apollo-link-mutation-queue@~1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@adobe/apollo-link-mutation-queue/-/apollo-link-mutation-queue-1.0.2.tgz#0c589ffb970b9917ba52c38812740c613c0a40da" @@ -16,6 +28,57 @@ dependencies: apollo-link "~1.2.13" +"@adobe/magento-storefront-event-collector@~1.1.13": + version "1.1.13" + resolved "https://registry.yarnpkg.com/@adobe/magento-storefront-event-collector/-/magento-storefront-event-collector-1.1.13.tgz#8382c4d9bb3675315fdd4dd7a9a04a658804c399" + integrity sha512-vYVRNoWGMTwOsQI2ZDPemf/9vDitJq3jzGE1GwnQ8gyLvk/tWfcK1OOPqnaWCTBtsmFQNX/dga35UxztJCo++w== + dependencies: + "@adobe/adobe-client-data-layer" "^2.0.2" + "@adobe/alloy" "^2.9.0" + "@snowplow/browser-plugin-link-click-tracking" "^3.0.2" + "@snowplow/browser-plugin-performance-timing" "^3.0.1" + "@snowplow/browser-tracker" "^3.0.1" + +"@adobe/magento-storefront-events-sdk@~1.1.13": + version "1.1.13" + resolved "https://registry.yarnpkg.com/@adobe/magento-storefront-events-sdk/-/magento-storefront-events-sdk-1.1.13.tgz#31c8777216f5563f3213ce9886ca7614639f9768" + integrity sha512-Y5SpXBz5RtV8Gi6IzZYWze1zSRJ4KRaIf8/aGpdPhakzmQQKZMtu69CIw94ZlXvljrRWLCRf+bkIoyM8KCMRmw== + +"@adobe/reactor-cookie@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@adobe/reactor-cookie/-/reactor-cookie-1.1.0.tgz#9c13201fc4dd41b7600aa911e587539b7b9e4216" + integrity sha512-JlC0In45XJOrGuTnfLxj5Hmz9VFJO0hKJHnfc6Qtsrqw2vpKKHmfB8qMExtUw2HJ8iVQd3t+/q3h9wjfvUnVMA== + dependencies: + js-cookie "2.2.1" + +"@adobe/reactor-load-script@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@adobe/reactor-load-script/-/reactor-load-script-1.1.1.tgz#41b6e6df98ad0de5faa75d4f672c90e2623aa2cd" + integrity sha512-zshG46a+KpTPrSO2C5YJCZa1XEel2DNZijtsitHHsGMChQkSx6lfVH3JH2vuqUxCSq8bKC2eyBKcoDZkEZImgg== + dependencies: + "@adobe/reactor-promise" "*" + +"@adobe/reactor-object-assign@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@adobe/reactor-object-assign/-/reactor-object-assign-1.0.0.tgz#9bc02dcbff2a28ad6f55a0e5fd80f37c93f7624a" + integrity sha512-WI3uplIKeFqqGYyU0IRbXwq+7Nk40EF40s7UmDNL5hdVkiDhEtXmWmbuGPjapKQfe3BoxMlH+pk/rxTva/DaUw== + dependencies: + object-assign "4.1.1" + +"@adobe/reactor-promise@*": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@adobe/reactor-promise/-/reactor-promise-1.2.0.tgz#7ef1ef2850400a5ac20b1d7346402a359df9e471" + integrity sha512-0haTPOfJRKvTuG5Sbja3xAFPdv8aJUz+CiU2ocV5F88az0g6fOwt4+NLlLKzJcbh7qcg6MjDMZopbNc0xqUUVg== + dependencies: + promise-polyfill "8.1.3" + +"@adobe/reactor-query-string@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@adobe/reactor-query-string/-/reactor-query-string-1.0.0.tgz#e7f6f9bd89d0a84dac5a6c502255f2b20a5687c1" + integrity sha512-Y6Cb9azVk+zIs9wEoOTMa01pzMqxFDPhRCfvZeW3YTAeR/b3kZOjD8loTpDt7bDj0vCZPRB2mhnVGOMCnsvBFQ== + dependencies: + querystring "0.2.0" + "@apideck/better-ajv-errors@^0.2.4": version "0.2.5" resolved "https://registry.yarnpkg.com/@apideck/better-ajv-errors/-/better-ajv-errors-0.2.5.tgz#b9c0092b7f7f23c356a0a31600334f7b8958458b" @@ -43,6 +106,24 @@ tslib "^2.3.0" zen-observable-ts "^1.2.0" +"@apollo/client@~3.6.6": + version "3.6.6" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.6.6.tgz#3fb1f5b11da9a64b51b5a86b62450bee034e7f41" + integrity sha512-AzNLN043wy0bDTTR9wzKYSu+I1IT2Ox3+vWckxgIt88Jfw5jHBvumf3lXE1JlgvbFCTiKS/Sa66AadQXWMVBRQ== + dependencies: + "@graphql-typed-document-node/core" "^3.1.1" + "@wry/context" "^0.6.0" + "@wry/equality" "^0.5.0" + "@wry/trie" "^0.3.0" + graphql-tag "^2.12.6" + hoist-non-react-statics "^3.3.2" + optimism "^0.16.1" + prop-types "^15.7.2" + symbol-observable "^4.0.0" + ts-invariant "^0.10.3" + tslib "^2.3.0" + zen-observable-ts "^1.2.5" + "@ardatan/aggregate-error@0.0.6": version "0.0.6" resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" @@ -2583,7 +2664,7 @@ "@graphql-tools/utils" "^8.5.1" tslib "~2.3.0" -"@graphql-tools/prisma-loader@6.3.0", "@graphql-tools/prisma-loader@^6.0.0": +"@graphql-tools/prisma-loader@^6.0.0": version "6.3.0" resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-6.3.0.tgz#c907e17751ff2b26e7c2bc75d0913ebf03f970da" integrity sha512-9V3W/kzsFBmUQqOsd96V4a4k7Didz66yh/IK89B1/rrvy9rYj+ULjEqR73x9BYZ+ww9FV8yP8LasWAJwWaqqJQ== @@ -2745,6 +2826,11 @@ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.0.tgz#0eee6373e11418bfe0b5638f654df7a4ca6a3950" integrity sha512-wYn6r8zVZyQJ6rQaALBEln5B1pzxb9shV5Ef97kTvn6yVGrqyXVnDqnU24MXnFubR+rZjBY9NWuxX3FB2sTsjg== +"@graphql-typed-document-node/core@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052" + integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg== + "@hapi/bourne@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-2.0.0.tgz#5bb2193eb685c0007540ca61d166d4e1edaf918d" @@ -3901,6 +3987,51 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@snowplow/browser-plugin-link-click-tracking@^3.0.2": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@snowplow/browser-plugin-link-click-tracking/-/browser-plugin-link-click-tracking-3.4.0.tgz#c9d81465ae1b16f1aca735485a78bd64be9ec81e" + integrity sha512-J5e9M41PxawYUuVCuxffBzbTRB73EKMDzNE917EQAVR9fJwjDwS6lPTCXbi3muX8nOprSfzu7hPCJeuxoXMETQ== + dependencies: + "@snowplow/browser-tracker-core" "3.4.0" + "@snowplow/tracker-core" "3.4.0" + tslib "^2.3.1" + +"@snowplow/browser-plugin-performance-timing@^3.0.1": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@snowplow/browser-plugin-performance-timing/-/browser-plugin-performance-timing-3.4.0.tgz#bf0ca1c2970ca0d8a92d2536cab85dda194fd317" + integrity sha512-U6CV8EFMgXUC98IuEJUZynJFKHi8CQUjdm6H4notmMF6KG6OM7qGEBKC9tuCJDGuVA7UgLucGhu3onxBXeRYGg== + dependencies: + "@snowplow/browser-tracker-core" "3.4.0" + "@snowplow/tracker-core" "3.4.0" + tslib "^2.3.1" + +"@snowplow/browser-tracker-core@3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@snowplow/browser-tracker-core/-/browser-tracker-core-3.4.0.tgz#ff037367325445cb0f5373fdefd6dabc7434192c" + integrity sha512-Eav/+UnfnytnuGVJRh8wjIdAN2Qx/IZxAtDtNj0cu+WQactAnSJBeUuaWOrcasXbvrMchlF+GyvA/PiOXLeDTQ== + dependencies: + "@snowplow/tracker-core" "3.4.0" + sha1 "^1.1.1" + tslib "^2.3.1" + uuid "^3.4.0" + +"@snowplow/browser-tracker@^3.0.1": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@snowplow/browser-tracker/-/browser-tracker-3.4.0.tgz#8d1b5df945d0d8872b97c470b00baa0aae0a6e7d" + integrity sha512-kOzTFAc4cwr/CJLA+lpwR0eDc0vmcIIjlX6veoRXiELb7tR1JFdCjaQirSmQ1mLjjxTjqrkBPb+2TNhxtCzw0g== + dependencies: + "@snowplow/browser-tracker-core" "3.4.0" + "@snowplow/tracker-core" "3.4.0" + tslib "^2.3.1" + +"@snowplow/tracker-core@3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@snowplow/tracker-core/-/tracker-core-3.4.0.tgz#688ddcbf7ebe894112d337532ee1473c703eb920" + integrity sha512-HINAMZdvcQwIOZVVM8UtNZB2X33m7+gAIzDj3YQxvzphDUo7+GPCmmoVZmP9C2m6HovtBbKDX7slruvWFh+kcw== + dependencies: + tslib "^2.3.1" + uuid "^3.4.0" + "@storybook/addons@6.3.7": version "6.3.7" resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.3.7.tgz#7c6b8d11b65f67b1884f6140437fe996dc39537a" @@ -6709,12 +6840,7 @@ camelspace@~1.0.0: dependencies: change-case "^3.1.0" -caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001251, caniuse-lite@^1.0.30001252, caniuse-lite@^1.0.30001274, caniuse-lite@^1.0.30001280: - version "1.0.30001335" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz" - integrity sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w== - -caniuse-lite@~1.0.30001335: +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001251, caniuse-lite@^1.0.30001252, caniuse-lite@^1.0.30001274, caniuse-lite@^1.0.30001280, caniuse-lite@~1.0.30001335: version "1.0.30001335" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz#899254a0b70579e5a957c32dced79f0727c61f2a" integrity sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w== @@ -6858,6 +6984,11 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +"charenc@>= 0.0.1": + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + check-types@^8.0.3: version "8.0.3" resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" @@ -7628,6 +7759,11 @@ cross-undici-fetch@^0.1.4: undici "^4.9.3" web-streams-polyfill "^3.2.0" +"crypt@>= 0.0.1": + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -7711,6 +7847,11 @@ css-what@^5.0.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -10444,6 +10585,13 @@ graphql-tag@^2.12.3, graphql-tag@~2.12.4, graphql-tag@~2.12.5: dependencies: tslib "^2.1.0" +graphql-tag@^2.12.6: + version "2.12.6" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" + integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== + dependencies: + tslib "^2.1.0" + graphql-type-json@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/graphql-type-json/-/graphql-type-json-0.3.2.tgz#f53a851dbfe07bd1c8157d24150064baab41e115" @@ -10960,7 +11108,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@^2.2.1, https-proxy-agent@^5.0.0, https-proxy-agent@~2.2.3: +https-proxy-agent@^2.2.1: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== @@ -10968,6 +11116,14 @@ https-proxy-agent@^2.2.1, https-proxy-agent@^5.0.0, https-proxy-agent@~2.2.3: agent-base "^4.3.0" debug "^3.1.0" +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -12346,6 +12502,11 @@ joycon@^2.2.5: resolved "https://registry.yarnpkg.com/joycon/-/joycon-2.2.5.tgz#8d4cf4cbb2544d7b7583c216fcdfec19f6be1615" integrity sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ== +js-cookie@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" @@ -14080,7 +14241,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -15353,6 +15514,11 @@ promise-polyfill@8.1.0: resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.1.0.tgz#30059da54d1358ce905ac581f287e184aedf995d" integrity sha512-OzSf6gcCUQ01byV4BgwyUCswlaQQ6gzXc23aLQWhicvfX9kfsUiUhgt3CCQej8jDnl8/PhGF31JdHX2/MzF3WA== +promise-polyfill@8.1.3: + version "8.1.3" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.1.3.tgz#8c99b3cf53f3a91c68226ffde7bde81d7f904116" + integrity sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g== + promise-polyfill@^7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-7.1.2.tgz#ab05301d8c28536301622d69227632269a70ca3b" @@ -16846,6 +17012,14 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" +sha1@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848" + integrity sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg= + dependencies: + charenc ">= 0.0.1" + crypt ">= 0.0.1" + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -18316,6 +18490,13 @@ ts-essentials@^2.0.3: resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-2.0.12.tgz#c9303f3d74f75fa7528c3d49b80e089ab09d8745" integrity sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w== +ts-invariant@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" + integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ== + dependencies: + tslib "^2.1.0" + ts-invariant@^0.4.0: version "0.4.4" resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86" @@ -18362,6 +18543,11 @@ tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.3.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" @@ -18811,7 +18997,7 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@^3.0.1, uuid@^3.3.2: +uuid@^3.0.1, uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -18978,10 +19164,8 @@ watchpack@^1.7.4: resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== dependencies: - chokidar "^3.4.1" graceful-fs "^4.1.2" neo-async "^2.5.0" - watchpack-chokidar2 "^2.0.1" optionalDependencies: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.1" @@ -19880,6 +20064,13 @@ zen-observable-ts@^1.2.0: dependencies: zen-observable "0.8.15" +zen-observable-ts@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" + integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg== + dependencies: + zen-observable "0.8.15" + zen-observable@0.8.15, zen-observable@^0.8.0: version "0.8.15" resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"