Skip to content

Commit f098607

Browse files
committed
fix(tests): use more accurate totalValue
Enhances the tests to ensure the totalValue is more accurate. Establishes that totalValue is indeed the total value of the products in the event.
1 parent 2aff369 commit f098607

File tree

3 files changed

+178
-7
lines changed

3 files changed

+178
-7
lines changed

packages/hydrogen-react/src/analytics-schema-custom-storefront-customer-tracking.test.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
ShopifyPageViewPayload,
1616
} from './analytics-types.js';
1717
import {version} from '../package.json';
18+
import {getProductsValue, getProductValue} from './analytics-utils.js';
1819

1920
describe(`analytics schema - custom storefront customer tracking`, () => {
2021
describe('page view', () => {
@@ -141,7 +142,7 @@ describe(`analytics schema - custom storefront customer tracking`, () => {
141142
const pageViewPayload = {
142143
...BASE_PAYLOAD,
143144
pageType: 'product',
144-
totalValue: 100,
145+
totalValue: getProductsValue(BASE_PAYLOAD.products),
145146
};
146147
const events = pageView(pageViewPayload);
147148

@@ -169,7 +170,7 @@ describe(`analytics schema - custom storefront customer tracking`, () => {
169170
...BASE_PAYLOAD,
170171
pageType: 'product',
171172
products: [productPayload],
172-
totalValue: 100,
173+
totalValue: getProductValue(productPayload),
173174
};
174175
const events = pageView(pageViewPayload);
175176

@@ -184,7 +185,6 @@ describe(`analytics schema - custom storefront customer tracking`, () => {
184185
expect(events[1]).toEqual(
185186
getExpectedPayload(pageViewPayload, {
186187
event_name: 'product_page_rendered',
187-
total_value: pageViewPayload.totalValue,
188188
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
189189
products: expect.anything(),
190190
canonical_url: pageViewPayload.url,
@@ -217,7 +217,7 @@ describe(`analytics schema - custom storefront customer tracking`, () => {
217217
...BASE_PAYLOAD,
218218
pageType: 'product',
219219
products: [productPayload],
220-
totalValue: 100,
220+
totalValue: getProductValue(productPayload),
221221
};
222222
const events = pageView(pageViewPayload);
223223

@@ -293,7 +293,7 @@ describe(`analytics schema - custom storefront customer tracking`, () => {
293293
...BASE_PAYLOAD,
294294
cartId: 'gid://shopify/Cart/abc123',
295295
products: [productPayload],
296-
totalValue: 100,
296+
totalValue: getProductValue(productPayload),
297297
};
298298
const events = addToCart(addToCartPayload);
299299

@@ -303,7 +303,6 @@ describe(`analytics schema - custom storefront customer tracking`, () => {
303303
getExpectedPayload(addToCartPayload, {
304304
event_name: 'product_added_to_cart',
305305
cart_token: 'abc123',
306-
total_value: addToCartPayload.totalValue,
307306
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
308307
products: expect.anything(),
309308
}),
@@ -322,6 +321,45 @@ describe(`analytics schema - custom storefront customer tracking`, () => {
322321
price: parseFloat(productPayload.price),
323322
});
324323
});
324+
325+
it(`with base product payload quantity 1`, () => {
326+
const productPayload = {
327+
...BASE_PRODUCT_PAYLOAD,
328+
quantity: 1,
329+
};
330+
const addToCartPayload = {
331+
...BASE_PAYLOAD,
332+
cartId: 'gid://shopify/Cart/abc123',
333+
products: [productPayload],
334+
totalValue: getProductValue(productPayload),
335+
};
336+
const events = addToCart(addToCartPayload);
337+
338+
expectType<ShopifyMonorailPayload[]>(events);
339+
expect(events.length).toBe(1);
340+
expect(events[0]).toEqual(
341+
getExpectedPayload(addToCartPayload, {
342+
event_name: 'product_added_to_cart',
343+
cart_token: 'abc123',
344+
total_value: addToCartPayload.totalValue,
345+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
346+
products: expect.anything(),
347+
}),
348+
);
349+
const productEventPayload = events[0].payload;
350+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
351+
const product = JSON.parse(
352+
(productEventPayload.products && productEventPayload.products[0]) ||
353+
'{}',
354+
);
355+
expect(product).toEqual({
356+
...getForwardedProductPayload(productPayload),
357+
variant: '',
358+
quantity: 1,
359+
product_id: 1,
360+
price: parseFloat(productPayload.price),
361+
});
362+
});
325363
});
326364
});
327365

packages/hydrogen-react/src/analytics-utils.test.ts

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
import {describe, it, expect} from 'vitest';
2-
import {parseGid, addDataIf, schemaWrapper} from './analytics-utils.js';
2+
import {
3+
parseGid,
4+
addDataIf,
5+
schemaWrapper,
6+
getProductsValue,
7+
randomNatural,
8+
getProductValue,
9+
} from './analytics-utils.js';
10+
import {ShopifyAnalyticsProduct} from './analytics-types.js';
11+
import {expectType} from 'ts-expect';
12+
import {
13+
BASE_PAYLOAD,
14+
BASE_PRODUCT_PAYLOAD,
15+
} from './analytics-schema.test.helpers.js';
316

417
describe('analytic-utils', () => {
518
describe('parseGid', () => {
@@ -151,4 +164,91 @@ describe('analytic-utils', () => {
151164
});
152165
});
153166
});
167+
168+
describe(`getProductValue`, () => {
169+
it(`gets single quantity product value`, () => {
170+
const productPayload = {
171+
...BASE_PRODUCT_PAYLOAD,
172+
quantity: 1,
173+
};
174+
const calculatedValue = getProductValue(productPayload);
175+
176+
expectType<ShopifyAnalyticsProduct>(productPayload);
177+
expect(calculatedValue).toEqual(parseFloat(productPayload.price));
178+
});
179+
180+
it(`gets arbitrary quantity product value`, () => {
181+
const productPayload = {
182+
...BASE_PRODUCT_PAYLOAD,
183+
quantity: randomNatural(),
184+
};
185+
const calculatedValue = getProductValue(productPayload);
186+
187+
expectType<ShopifyAnalyticsProduct>(productPayload);
188+
expect(calculatedValue).toBeCloseTo(
189+
parseFloat(productPayload.price) * productPayload.quantity,
190+
);
191+
});
192+
});
193+
194+
describe(`getProductsValue`, () => {
195+
it(`gets singleton products value`, () => {
196+
const productPayload = {
197+
...BASE_PRODUCT_PAYLOAD,
198+
quantity: randomNatural(),
199+
};
200+
const productsPayload = [productPayload];
201+
const addToCartPayload = {
202+
...BASE_PAYLOAD,
203+
cartId: 'gid://shopify/Cart/abc123',
204+
products: productsPayload,
205+
totalValue: getProductsValue(productsPayload),
206+
};
207+
208+
expectType<ShopifyAnalyticsProduct>(productPayload);
209+
expect(addToCartPayload.totalValue).toBeCloseTo(
210+
parseFloat(productPayload.price) * productPayload.quantity,
211+
);
212+
});
213+
214+
it(`gets tuple products value`, () => {
215+
const productPayload = {
216+
...BASE_PRODUCT_PAYLOAD,
217+
quantity: randomNatural(),
218+
};
219+
const productsPayload = [productPayload, productPayload];
220+
const addToCartPayload = {
221+
...BASE_PAYLOAD,
222+
cartId: 'gid://shopify/Cart/abc123',
223+
products: productsPayload,
224+
totalValue: getProductsValue(productsPayload),
225+
};
226+
227+
expectType<ShopifyAnalyticsProduct>(productPayload);
228+
expect(addToCartPayload.totalValue).toBeCloseTo(
229+
parseFloat(productPayload.price) * productPayload.quantity * 2,
230+
);
231+
});
232+
233+
it(`gets N products value`, () => {
234+
const productPayload = {
235+
...BASE_PRODUCT_PAYLOAD,
236+
quantity: randomNatural(),
237+
};
238+
const productsPayload = new Array(randomNatural()).fill(productPayload);
239+
const addToCartPayload = {
240+
...BASE_PAYLOAD,
241+
cartId: 'gid://shopify/Cart/abc123',
242+
products: productsPayload,
243+
totalValue: getProductsValue(productsPayload),
244+
};
245+
246+
expectType<ShopifyAnalyticsProduct>(productPayload);
247+
expect(addToCartPayload.totalValue).toBeCloseTo(
248+
parseFloat(productPayload.price) *
249+
productPayload.quantity *
250+
productsPayload.length,
251+
);
252+
});
253+
});
154254
});

packages/hydrogen-react/src/analytics-utils.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import type {
22
ShopifyMonorailPayload,
33
ShopifyMonorailEvent,
44
ShopifyGid,
5+
ShopifyAnalyticsProduct,
56
} from './analytics-types.js';
7+
import {faker} from '@faker-js/faker';
68

79
/**
810
* Builds a Shopify Monorail event from a Shopify Monorail payload and a schema ID.
@@ -106,3 +108,34 @@ export function errorIfServer(fnName: string): boolean {
106108
}
107109
return false;
108110
}
111+
112+
/**
113+
* Get a random number [1,1000]
114+
* @returns A random number
115+
*/
116+
export function randomNatural() {
117+
return faker.number.int({min: 1, max: 1000});
118+
}
119+
120+
/**
121+
* Calculate product price * quantity
122+
* @param product - The product
123+
* @returns A number
124+
*/
125+
export const getProductValue = (product: ShopifyAnalyticsProduct): number =>
126+
parseFloat(product.price) * (product.quantity || 0);
127+
128+
/**
129+
* Reduce all products and get their total value
130+
* @param products - The products
131+
* @returns A number
132+
*/
133+
export function getProductsValue(
134+
products?: ShopifyAnalyticsProduct[],
135+
): number | undefined {
136+
return products?.reduce(
137+
(previousValue, currentProduct) =>
138+
previousValue + getProductValue(currentProduct),
139+
0,
140+
);
141+
}

0 commit comments

Comments
 (0)