@tapcart/mobile-components 0.11.5 → 0.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../../components/ui/money.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAGjE,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,QAAA,MAAM,aAAa,gGAMjB,CAAA;AAEF,MAAM,WAAW,UACf,SAAQ,SAAS,EACf,YAAY,CAAC,OAAO,aAAa,CAAC;IACpC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,MAAM,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAC7B;AAED,iBAAS,KAAK,CAAC,EACb,KAAK,EACL,MAAM,EACN,QAAQ,EACR,aAAqB,EACrB,MAAM,EACN,GAAG,KAAK,EACT,EAAE,UAAU,2CAgDZ;AAED,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../../components/ui/money.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAGjE,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,QAAA,MAAM,aAAa,gGAMjB,CAAA;AAEF,MAAM,WAAW,UACf,SAAQ,SAAS,EACf,YAAY,CAAC,OAAO,aAAa,CAAC;IACpC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,MAAM,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAC7B;AAED,iBAAS,KAAK,CAAC,EACb,KAAK,EACL,MAAM,EACN,QAAQ,EACR,aAAqB,EACrB,MAAM,EACN,GAAG,KAAK,EACT,EAAE,UAAU,2CAiFZ;AAED,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA"}
@@ -25,27 +25,59 @@ function Money(_a) {
25
25
  var _b, _c;
26
26
  var { price, locale, currency, hideZeroCents = false, styles } = _a, props = __rest(_a, ["price", "locale", "currency", "hideZeroCents", "styles"]);
27
27
  const searchParams = useSearchParams();
28
- const countryFromParams = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("country");
29
- // Only use routeLocale if it's a valid BCP 47 language tag (e.g., "en-US", "fr-FR")
30
- const routeLocale = ((_c = (_b = usePathname()) === null || _b === void 0 ? void 0 : _b.split("/")) === null || _c === void 0 ? void 0 : _c.pop()) || "";
31
- // Valid locale format: 2-3 letter language code, hyphen, 2 letter country code
32
- const localeRegex = /^[a-z]{2,3}-[A-Z]{2}$/;
33
- const isValidLocale = routeLocale && localeRegex.test(routeLocale);
34
- let language = isValidLocale ? routeLocale : locale;
35
- // Fallback to a default locale if none provided or invalid
36
- if (!language || typeof language !== "string") {
37
- language = "en-US";
38
- }
39
- // If locale is just a language code (no hyphen), combine with country to create proper BCP 47 locale
40
- // e.g., lang=ES + country=MX -> es-MX (not es-ES which would default to euros)
41
- if (language && !localeRegex.test(language) && countryFromParams) {
42
- language = `${language.toLowerCase()}-${countryFromParams.toUpperCase()}`;
43
- }
44
- // Fix for when we want to format in USD in english
45
- // but language only contains two letter region
46
- if (currency === "USD" && !language.includes("-")) {
47
- language = `en-${language}`;
48
- }
28
+ const countryFromParams = (searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("country")) || undefined;
29
+ // Derive a candidate locale from the route or prop, then normalize to a valid BCP 47 tag
30
+ const routeCandidate = ((_c = (_b = usePathname()) === null || _b === void 0 ? void 0 : _b.split("/")) === null || _c === void 0 ? void 0 : _c.pop()) || "";
31
+ const normalizeLocale = (baseLocale, countryParam) => {
32
+ const DEFAULT_LOCALE = "en-US";
33
+ const raw = (baseLocale || "").replace(/_/g, "-").trim();
34
+ let languageCode = "";
35
+ let regionCode = "";
36
+ if (raw.includes("-")) {
37
+ const [langPart, regionPart] = raw.split("-");
38
+ if (langPart)
39
+ languageCode = langPart.toLowerCase();
40
+ if (regionPart)
41
+ regionCode = regionPart.toUpperCase();
42
+ }
43
+ else if (raw.length === 2) {
44
+ if (/^[a-z]{2}$/.test(raw)) {
45
+ // language only (e.g., "en")
46
+ languageCode = raw.toLowerCase();
47
+ }
48
+ else if (/^[A-Z]{2}$/.test(raw)) {
49
+ // region only (e.g., "US")
50
+ regionCode = raw.toUpperCase();
51
+ }
52
+ else {
53
+ languageCode = raw.toLowerCase();
54
+ }
55
+ }
56
+ else if (raw) {
57
+ // fallback: treat as language subtag
58
+ languageCode = raw.toLowerCase();
59
+ }
60
+ // If a country param is present, prefer it as region
61
+ if (countryParam) {
62
+ regionCode = countryParam.toUpperCase();
63
+ }
64
+ // Ensure we always have a sensible language
65
+ if (!languageCode) {
66
+ languageCode = "en";
67
+ }
68
+ const candidate = regionCode
69
+ ? `${languageCode}-${regionCode}`
70
+ : languageCode;
71
+ try {
72
+ // Validate and canonicalize via NumberFormat
73
+ const formatter = new Intl.NumberFormat(candidate);
74
+ return formatter.resolvedOptions().locale;
75
+ }
76
+ catch (_a) {
77
+ return DEFAULT_LOCALE;
78
+ }
79
+ };
80
+ const language = normalizeLocale(routeCandidate || locale, countryFromParams);
49
81
  const formatter = React.useMemo(() => new Intl.NumberFormat(language, {
50
82
  style: "currency",
51
83
  currency: currency,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=money.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.test.d.ts","sourceRoot":"","sources":["../../../components/ui/money.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { Money } from "./money";
4
+ jest.mock("next/navigation", () => ({
5
+ usePathname: jest.fn(),
6
+ useSearchParams: jest.fn(),
7
+ }));
8
+ const mockedUsePathname = require("next/navigation").usePathname;
9
+ const mockedUseSearchParams = require("next/navigation")
10
+ .useSearchParams;
11
+ function makeSearchParams(params) {
12
+ return {
13
+ get: (key) => params[key],
14
+ };
15
+ }
16
+ describe("Money", () => {
17
+ beforeEach(() => {
18
+ mockedUsePathname.mockReset();
19
+ mockedUseSearchParams.mockReset();
20
+ });
21
+ it("formats USD with default en-US when no locale provided", () => {
22
+ mockedUsePathname.mockReturnValue("/");
23
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({}));
24
+ render(_jsx(Money, { price: 1234.56, currency: "USD", locale: "" }));
25
+ expect(screen.getByText(/\$1,234.56/)).toBeTruthy();
26
+ });
27
+ it("removes trailing cents when hideZeroCents is true", () => {
28
+ mockedUsePathname.mockReturnValue("/");
29
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({}));
30
+ render(_jsx(Money, { price: 100, currency: "USD", locale: "en-US", hideZeroCents: true }));
31
+ expect(screen.getByText("$100")).toBeTruthy();
32
+ });
33
+ it("uses route locale when valid (e.g., fr-FR)", () => {
34
+ mockedUsePathname.mockReturnValue("/shop/fr-FR");
35
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({}));
36
+ render(_jsx(Money, { price: 1234.56, currency: "EUR", locale: "en-US" }));
37
+ expect(screen.getByText(/1\s?234,56\s?€/)).toBeTruthy();
38
+ });
39
+ it("normalizes underscore and applies country param for region", () => {
40
+ mockedUsePathname.mockReturnValue("/shop/es_es");
41
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({ country: "mx" }));
42
+ render(_jsx(Money, { price: 50, currency: "MXN", locale: "es" }));
43
+ expect(screen.getByText(/\$\s?50(\.00)?/)).toBeTruthy();
44
+ });
45
+ it("avoids malformed tags like en-us-US and falls back safely", () => {
46
+ mockedUsePathname.mockReturnValue("/en-us-US");
47
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({}));
48
+ render(_jsx(Money, { price: 10, currency: "USD", locale: "en-us-US" }));
49
+ expect(screen.getByText(/\$10(\.00)?/)).toBeTruthy();
50
+ });
51
+ it("handles region-only input via country param", () => {
52
+ mockedUsePathname.mockReturnValue("/US");
53
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({ country: "US" }));
54
+ render(_jsx(Money, { price: 20, currency: "USD", locale: "" }));
55
+ expect(screen.getByText(/\$20(\.00)?/)).toBeTruthy();
56
+ });
57
+ });
@@ -0,0 +1,2 @@
1
+ export declare const isVersion20: (version: string | null | undefined) => boolean;
2
+ //# sourceMappingURL=isVersion20.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isVersion20.util.d.ts","sourceRoot":"","sources":["../../lib/isVersion20.util.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,YAAa,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,OAIhE,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { SemVer } from "semver";
2
+ export const isVersion20 = (version) => {
3
+ if (!version)
4
+ return false;
5
+ const currentVersion = new SemVer(version || "0.0.0");
6
+ return currentVersion.compare("13.20.0") >= 0;
7
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=utils.wishlist.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.wishlist.test.d.ts","sourceRoot":"","sources":["../../lib/utils.wishlist.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
1
+ import { findWishlistEntry, getEnabledWishlistIntegration, supportsMultipleWishlists, } from "./utils";
2
+ describe("getEnabledWishlistIntegration", () => {
3
+ it("returns the first enabled wishlist integration", () => {
4
+ const integrations = [
5
+ { name: "tapcart-search", enabled: true },
6
+ { name: "tapcart-wishlist", enabled: true, multiwishlist: true },
7
+ { name: "swym", enabled: true },
8
+ ];
9
+ const result = getEnabledWishlistIntegration(integrations);
10
+ expect(result).toEqual({
11
+ name: "tapcart-wishlist",
12
+ enabled: true,
13
+ multiwishlist: true,
14
+ });
15
+ });
16
+ it("returns null when no wishlist integration is enabled", () => {
17
+ const integrations = [
18
+ { name: "tapcart-search", enabled: true },
19
+ { name: "tapcart-wishlist", enabled: false },
20
+ ];
21
+ expect(getEnabledWishlistIntegration(integrations)).toBeNull();
22
+ });
23
+ it("returns null when integrations is not an array", () => {
24
+ expect(getEnabledWishlistIntegration(undefined)).toBeNull();
25
+ expect(getEnabledWishlistIntegration(null)).toBeNull();
26
+ });
27
+ });
28
+ describe("supportsMultipleWishlists", () => {
29
+ it("respects explicit multiwishlist flag", () => {
30
+ expect(supportsMultipleWishlists({
31
+ name: "tapcart-wishlist",
32
+ enabled: true,
33
+ multiwishlist: false,
34
+ })).toBe(false);
35
+ expect(supportsMultipleWishlists({
36
+ name: "tapcart-wishlist",
37
+ enabled: true,
38
+ multiwishlist: true,
39
+ })).toBe(true);
40
+ });
41
+ it("defaults to true for known integrations when flag is omitted", () => {
42
+ expect(supportsMultipleWishlists({
43
+ name: "tapcart-wishlist-v2",
44
+ enabled: true,
45
+ })).toBe(true);
46
+ });
47
+ it("returns false for unknown integrations", () => {
48
+ expect(supportsMultipleWishlists({
49
+ name: "not-a-wishlist",
50
+ enabled: true,
51
+ })).toBe(false);
52
+ });
53
+ });
54
+ describe("findWishlistEntry", () => {
55
+ const wishlists = [
56
+ {
57
+ id: "wl-1",
58
+ items: [
59
+ {
60
+ id: "item-1",
61
+ productId: "gid://shopify/Product/123",
62
+ variantId: "gid://shopify/ProductVariant/456",
63
+ },
64
+ ],
65
+ },
66
+ {
67
+ _id: "wl-2",
68
+ items: [
69
+ {
70
+ id: "item-2",
71
+ productId: "789",
72
+ variantId: "654",
73
+ },
74
+ ],
75
+ },
76
+ ];
77
+ it("finds an entry when product and variant match across gid representations", () => {
78
+ const match = findWishlistEntry(wishlists, "123", "456");
79
+ expect(match).toEqual({
80
+ wishlistId: "wl-1",
81
+ item: {
82
+ id: "item-1",
83
+ productId: "gid://shopify/Product/123",
84
+ variantId: "gid://shopify/ProductVariant/456",
85
+ },
86
+ });
87
+ });
88
+ it("falls back to product-only lookup when variant is omitted", () => {
89
+ const match = findWishlistEntry(wishlists, "789");
90
+ expect(match).toEqual({
91
+ wishlistId: "wl-2",
92
+ item: {
93
+ id: "item-2",
94
+ productId: "789",
95
+ variantId: "654",
96
+ },
97
+ });
98
+ });
99
+ it("returns null when no matching entry is found", () => {
100
+ const match = findWishlistEntry(wishlists, "999", "888");
101
+ expect(match).toBeNull();
102
+ });
103
+ it("handles empty or invalid inputs gracefully", () => {
104
+ expect(findWishlistEntry(undefined, "123")).toBeNull();
105
+ expect(findWishlistEntry([], "123")).toBeNull();
106
+ expect(findWishlistEntry(wishlists, undefined)).toBeNull();
107
+ });
108
+ });
package/dist/styles.css CHANGED
@@ -778,6 +778,9 @@ video {
778
778
  .top-\[50\%\] {
779
779
  top: 50%;
780
780
  }
781
+ .z-0 {
782
+ z-index: 0;
783
+ }
781
784
  .z-10 {
782
785
  z-index: 10;
783
786
  }
@@ -886,6 +889,9 @@ video {
886
889
  .ml-2 {
887
890
  margin-left: 0.5rem;
888
891
  }
892
+ .ml-4 {
893
+ margin-left: 1rem;
894
+ }
889
895
  .ml-auto {
890
896
  margin-left: auto;
891
897
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=addItemToWishlist.wishlistId.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addItemToWishlist.wishlistId.test.d.ts","sourceRoot":"","sources":["../../../tests/addItemToWishlist/addItemToWishlist.wishlistId.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { addItemToWishlist } from "../../lib/utils";
11
+ describe("addItemToWishlist with explicit wishlistId", () => {
12
+ let mockTapcart;
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ mockTapcart = {
16
+ action: jest.fn().mockResolvedValue(true),
17
+ variables: {
18
+ customer: { id: "customer-123" },
19
+ wishlists: [{ id: "wishlist-1" }],
20
+ },
21
+ };
22
+ });
23
+ it("should use the provided wishlistId without opening the drawer", () => __awaiter(void 0, void 0, void 0, function* () {
24
+ const integrations = [
25
+ {
26
+ name: "tapcart-wishlist",
27
+ enabled: true,
28
+ multiwishlist: false,
29
+ },
30
+ ];
31
+ const product = {
32
+ id: "gid://shopify/Product/123",
33
+ variants: [{ id: "gid://shopify/ProductVariant/456" }],
34
+ };
35
+ const result = yield addItemToWishlist({
36
+ Tapcart: mockTapcart,
37
+ integrations,
38
+ product,
39
+ selectedVariantId: "gid://shopify/ProductVariant/456",
40
+ wishlistId: "gid://shopify/Wishlist/789",
41
+ });
42
+ expect(result).toBe(true);
43
+ expect(mockTapcart.action).toHaveBeenCalledTimes(1);
44
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/add", {
45
+ productId: "123",
46
+ variantId: "456",
47
+ wishlistId: "789",
48
+ });
49
+ }));
50
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=removeItemFromWishlist.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"removeItemFromWishlist.test.d.ts","sourceRoot":"","sources":["../../../tests/removeItemFromWishlist/removeItemFromWishlist.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,268 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { removeItemFromWishlist } from "../../lib/utils";
11
+ describe("removeItemFromWishlist", () => {
12
+ let mockTapcart;
13
+ let consoleErrorSpy;
14
+ let consoleLogSpy;
15
+ beforeEach(() => {
16
+ // Reset mocks before each test
17
+ jest.clearAllMocks();
18
+ // Spy on console methods
19
+ consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
20
+ consoleLogSpy = jest.spyOn(console, "log").mockImplementation();
21
+ // Default mock Tapcart instance
22
+ mockTapcart = {
23
+ action: jest.fn(),
24
+ variables: {
25
+ customer: { id: "customer-123" },
26
+ wishlists: [
27
+ {
28
+ id: "wishlist-1",
29
+ items: [
30
+ {
31
+ productId: "gid://shopify/Product/123",
32
+ variantId: "gid://shopify/ProductVariant/456",
33
+ },
34
+ ],
35
+ },
36
+ ],
37
+ },
38
+ };
39
+ });
40
+ afterEach(() => {
41
+ consoleErrorSpy.mockRestore();
42
+ consoleLogSpy.mockRestore();
43
+ });
44
+ describe("validation and error handling", () => {
45
+ it("should return false when Tapcart instance is missing", () => __awaiter(void 0, void 0, void 0, function* () {
46
+ const result = yield removeItemFromWishlist({
47
+ Tapcart: null,
48
+ productId: "gid://shopify/Product/123",
49
+ selectedVariantId: "gid://shopify/ProductVariant/456",
50
+ });
51
+ expect(result).toBe(false);
52
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Tapcart instance is missing");
53
+ }));
54
+ it("should handle errors gracefully and return false", () => __awaiter(void 0, void 0, void 0, function* () {
55
+ // Mock Tapcart.action to throw an error
56
+ mockTapcart.action = jest.fn().mockImplementation(() => {
57
+ throw new Error("Test error");
58
+ });
59
+ const result = yield removeItemFromWishlist({
60
+ Tapcart: mockTapcart,
61
+ productId: "gid://shopify/Product/123",
62
+ selectedVariantId: "gid://shopify/ProductVariant/456",
63
+ });
64
+ expect(result).toBe(false);
65
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Failed to remove from wishlist", expect.any(Error));
66
+ }));
67
+ });
68
+ describe("variant ID handling", () => {
69
+ it("should return false when variant ID is missing from both wishlist entry and selectedVariantId", () => __awaiter(void 0, void 0, void 0, function* () {
70
+ // Mock Tapcart with no variant ID in wishlist entry
71
+ mockTapcart.variables.wishlists[0].items[0].variantId = null;
72
+ const result = yield removeItemFromWishlist({
73
+ Tapcart: mockTapcart,
74
+ productId: "gid://shopify/Product/123",
75
+ selectedVariantId: null,
76
+ });
77
+ expect(result).toBe(false);
78
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Missing variant id for wishlist removal");
79
+ expect(mockTapcart.action).not.toHaveBeenCalled();
80
+ }));
81
+ it("should use variant ID from wishlist entry when available", () => __awaiter(void 0, void 0, void 0, function* () {
82
+ // Mock Tapcart.action to return a success result
83
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
84
+ const result = yield removeItemFromWishlist({
85
+ Tapcart: mockTapcart,
86
+ productId: "gid://shopify/Product/123",
87
+ selectedVariantId: null,
88
+ });
89
+ expect(result).toBe(true);
90
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
91
+ productId: "gid://shopify/Product/123",
92
+ variantId: "456",
93
+ wishlistId: "wishlist-1",
94
+ });
95
+ }));
96
+ it("should use selectedVariantId when wishlist entry has no variant ID", () => __awaiter(void 0, void 0, void 0, function* () {
97
+ // Mock Tapcart with no variant ID in wishlist entry
98
+ mockTapcart.variables.wishlists[0].items[0].variantId = null;
99
+ // Mock Tapcart.action to return a success result
100
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
101
+ const result = yield removeItemFromWishlist({
102
+ Tapcart: mockTapcart,
103
+ productId: "gid://shopify/Product/123",
104
+ selectedVariantId: "gid://shopify/ProductVariant/789",
105
+ });
106
+ expect(result).toBe(true);
107
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
108
+ productId: "gid://shopify/Product/123",
109
+ variantId: "789",
110
+ wishlistId: "wishlist-1",
111
+ });
112
+ }));
113
+ it("should prioritize wishlist entry variant ID over selectedVariantId", () => __awaiter(void 0, void 0, void 0, function* () {
114
+ // Mock Tapcart.action to return a success result
115
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
116
+ const result = yield removeItemFromWishlist({
117
+ Tapcart: mockTapcart,
118
+ productId: "gid://shopify/Product/123",
119
+ selectedVariantId: "gid://shopify/ProductVariant/789",
120
+ });
121
+ expect(result).toBe(true);
122
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
123
+ productId: "gid://shopify/Product/123",
124
+ variantId: "456",
125
+ wishlistId: "wishlist-1",
126
+ });
127
+ }));
128
+ });
129
+ describe("wishlist ID handling", () => {
130
+ it("should return false when wishlist ID is missing", () => __awaiter(void 0, void 0, void 0, function* () {
131
+ // Mock Tapcart with no wishlist ID
132
+ mockTapcart.variables.wishlists = [];
133
+ const result = yield removeItemFromWishlist({
134
+ Tapcart: mockTapcart,
135
+ productId: "gid://shopify/Product/123",
136
+ selectedVariantId: "gid://shopify/ProductVariant/456",
137
+ });
138
+ expect(result).toBe(false);
139
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Missing wishlist id for wishlist removal");
140
+ expect(mockTapcart.action).not.toHaveBeenCalled();
141
+ }));
142
+ it("should use wishlist ID from wishlist entry when available", () => __awaiter(void 0, void 0, void 0, function* () {
143
+ // Mock Tapcart.action to return a success result
144
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
145
+ const result = yield removeItemFromWishlist({
146
+ Tapcart: mockTapcart,
147
+ productId: "gid://shopify/Product/123",
148
+ selectedVariantId: "gid://shopify/ProductVariant/456",
149
+ });
150
+ expect(result).toBe(true);
151
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
152
+ productId: "gid://shopify/Product/123",
153
+ variantId: "456",
154
+ wishlistId: "wishlist-1", // From wishlist entry
155
+ });
156
+ }));
157
+ });
158
+ describe("GID handling", () => {
159
+ it("should extract IDs from GID format", () => __awaiter(void 0, void 0, void 0, function* () {
160
+ // Mock Tapcart with GID format
161
+ mockTapcart.variables.wishlists[0].items[0].productId = "gid://shopify/Product/123?param=value";
162
+ mockTapcart.variables.wishlists[0].items[0].variantId = "gid://shopify/ProductVariant/456?param=value";
163
+ // Mock Tapcart.action to return a success result
164
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
165
+ const result = yield removeItemFromWishlist({
166
+ Tapcart: mockTapcart,
167
+ productId: "gid://shopify/Product/123?param=value",
168
+ selectedVariantId: "gid://shopify/ProductVariant/789?param=value",
169
+ });
170
+ expect(result).toBe(true);
171
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
172
+ productId: "gid://shopify/Product/123?param=value",
173
+ variantId: "456",
174
+ wishlistId: "wishlist-1",
175
+ });
176
+ }));
177
+ it("should handle plain IDs without GID format", () => __awaiter(void 0, void 0, void 0, function* () {
178
+ // Mock Tapcart with plain IDs
179
+ mockTapcart.variables.wishlists[0].items[0].productId = "123";
180
+ mockTapcart.variables.wishlists[0].items[0].variantId = "456";
181
+ // Mock Tapcart.action to return a success result
182
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
183
+ const result = yield removeItemFromWishlist({
184
+ Tapcart: mockTapcart,
185
+ productId: "123",
186
+ selectedVariantId: "789",
187
+ });
188
+ expect(result).toBe(true);
189
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
190
+ productId: "123",
191
+ variantId: "456",
192
+ wishlistId: "wishlist-1",
193
+ });
194
+ }));
195
+ it("should use the provided wishlistId when supplied", () => __awaiter(void 0, void 0, void 0, function* () {
196
+ mockTapcart.variables.wishlists = [];
197
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
198
+ const result = yield removeItemFromWishlist({
199
+ Tapcart: mockTapcart,
200
+ productId: "gid://shopify/Product/123",
201
+ selectedVariantId: "gid://shopify/ProductVariant/456",
202
+ wishlistId: "gid://shopify/Wishlist/789",
203
+ });
204
+ expect(result).toBe(true);
205
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
206
+ productId: "gid://shopify/Product/123",
207
+ variantId: "456",
208
+ wishlistId: "789",
209
+ });
210
+ }));
211
+ });
212
+ describe("edge cases", () => {
213
+ it("should handle undefined wishlists array", () => __awaiter(void 0, void 0, void 0, function* () {
214
+ mockTapcart.variables.wishlists = undefined;
215
+ const result = yield removeItemFromWishlist({
216
+ Tapcart: mockTapcart,
217
+ productId: "gid://shopify/Product/123",
218
+ selectedVariantId: "gid://shopify/ProductVariant/456",
219
+ });
220
+ expect(result).toBe(false);
221
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Missing wishlist id for wishlist removal");
222
+ expect(mockTapcart.action).not.toHaveBeenCalled();
223
+ }));
224
+ it("should handle empty wishlists array", () => __awaiter(void 0, void 0, void 0, function* () {
225
+ mockTapcart.variables.wishlists = [];
226
+ const result = yield removeItemFromWishlist({
227
+ Tapcart: mockTapcart,
228
+ productId: "gid://shopify/Product/123",
229
+ selectedVariantId: "gid://shopify/ProductVariant/456",
230
+ });
231
+ expect(result).toBe(false);
232
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Missing wishlist id for wishlist removal");
233
+ expect(mockTapcart.action).not.toHaveBeenCalled();
234
+ }));
235
+ it("should handle wishlist entry with undefined variantId", () => __awaiter(void 0, void 0, void 0, function* () {
236
+ mockTapcart.variables.wishlists[0].items[0].variantId = undefined;
237
+ // Mock Tapcart.action to return a success result
238
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
239
+ const result = yield removeItemFromWishlist({
240
+ Tapcart: mockTapcart,
241
+ productId: "gid://shopify/Product/123",
242
+ selectedVariantId: "gid://shopify/ProductVariant/789",
243
+ });
244
+ expect(result).toBe(true);
245
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
246
+ productId: "gid://shopify/Product/123",
247
+ variantId: "789",
248
+ wishlistId: "wishlist-1",
249
+ });
250
+ }));
251
+ it("should handle wishlist entry with null variantId", () => __awaiter(void 0, void 0, void 0, function* () {
252
+ mockTapcart.variables.wishlists[0].items[0].variantId = null;
253
+ // Mock Tapcart.action to return a success result
254
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
255
+ const result = yield removeItemFromWishlist({
256
+ Tapcart: mockTapcart,
257
+ productId: "gid://shopify/Product/123",
258
+ selectedVariantId: "gid://shopify/ProductVariant/789",
259
+ });
260
+ expect(result).toBe(true);
261
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
262
+ productId: "gid://shopify/Product/123",
263
+ variantId: "789",
264
+ wishlistId: "wishlist-1",
265
+ });
266
+ }));
267
+ });
268
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=removeItemFromWishlist.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"removeItemFromWishlist.test.d.ts","sourceRoot":"","sources":["../../../tests/removeItemFromWishlists/removeItemFromWishlist.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,268 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { removeItemFromWishlist } from "../../lib/utils";
11
+ describe("removeItemFromWishlist", () => {
12
+ let mockTapcart;
13
+ let consoleErrorSpy;
14
+ let consoleLogSpy;
15
+ beforeEach(() => {
16
+ // Reset mocks before each test
17
+ jest.clearAllMocks();
18
+ // Spy on console methods
19
+ consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
20
+ consoleLogSpy = jest.spyOn(console, "log").mockImplementation();
21
+ // Default mock Tapcart instance
22
+ mockTapcart = {
23
+ action: jest.fn(),
24
+ variables: {
25
+ customer: { id: "customer-123" },
26
+ wishlists: [
27
+ {
28
+ id: "wishlist-1",
29
+ items: [
30
+ {
31
+ productId: "gid://shopify/Product/123",
32
+ variantId: "gid://shopify/ProductVariant/456",
33
+ },
34
+ ],
35
+ },
36
+ ],
37
+ },
38
+ };
39
+ });
40
+ afterEach(() => {
41
+ consoleErrorSpy.mockRestore();
42
+ consoleLogSpy.mockRestore();
43
+ });
44
+ describe("validation and error handling", () => {
45
+ it("should return false when Tapcart instance is missing", () => __awaiter(void 0, void 0, void 0, function* () {
46
+ const result = yield removeItemFromWishlist({
47
+ Tapcart: null,
48
+ productId: "gid://shopify/Product/123",
49
+ selectedVariantId: "gid://shopify/ProductVariant/456",
50
+ });
51
+ expect(result).toBe(false);
52
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Tapcart instance is missing");
53
+ }));
54
+ it("should handle errors gracefully and return false", () => __awaiter(void 0, void 0, void 0, function* () {
55
+ // Mock Tapcart.action to throw an error
56
+ mockTapcart.action = jest.fn().mockImplementation(() => {
57
+ throw new Error("Test error");
58
+ });
59
+ const result = yield removeItemFromWishlist({
60
+ Tapcart: mockTapcart,
61
+ productId: "gid://shopify/Product/123",
62
+ selectedVariantId: "gid://shopify/ProductVariant/456",
63
+ });
64
+ expect(result).toBe(false);
65
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Failed to remove from wishlist", expect.any(Error));
66
+ }));
67
+ });
68
+ describe("variant ID handling", () => {
69
+ it("should return false when variant ID is missing from both wishlist entry and selectedVariantId", () => __awaiter(void 0, void 0, void 0, function* () {
70
+ // Mock Tapcart with no variant ID in wishlist entry
71
+ mockTapcart.variables.wishlists[0].items[0].variantId = null;
72
+ const result = yield removeItemFromWishlist({
73
+ Tapcart: mockTapcart,
74
+ productId: "gid://shopify/Product/123",
75
+ selectedVariantId: null,
76
+ });
77
+ expect(result).toBe(false);
78
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Missing variant id for wishlist removal");
79
+ expect(mockTapcart.action).not.toHaveBeenCalled();
80
+ }));
81
+ it("should use variant ID from wishlist entry when available", () => __awaiter(void 0, void 0, void 0, function* () {
82
+ // Mock Tapcart.action to return a success result
83
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
84
+ const result = yield removeItemFromWishlist({
85
+ Tapcart: mockTapcart,
86
+ productId: "gid://shopify/Product/123",
87
+ selectedVariantId: null,
88
+ });
89
+ expect(result).toBe(true);
90
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
91
+ productId: "gid://shopify/Product/123",
92
+ variantId: "456",
93
+ wishlistId: "wishlist-1",
94
+ });
95
+ }));
96
+ it("should use selectedVariantId when wishlist entry has no variant ID", () => __awaiter(void 0, void 0, void 0, function* () {
97
+ // Mock Tapcart with no variant ID in wishlist entry
98
+ mockTapcart.variables.wishlists[0].items[0].variantId = null;
99
+ // Mock Tapcart.action to return a success result
100
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
101
+ const result = yield removeItemFromWishlist({
102
+ Tapcart: mockTapcart,
103
+ productId: "gid://shopify/Product/123",
104
+ selectedVariantId: "gid://shopify/ProductVariant/789",
105
+ });
106
+ expect(result).toBe(true);
107
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
108
+ productId: "gid://shopify/Product/123",
109
+ variantId: "789",
110
+ wishlistId: "wishlist-1",
111
+ });
112
+ }));
113
+ it("should prioritize wishlist entry variant ID over selectedVariantId", () => __awaiter(void 0, void 0, void 0, function* () {
114
+ // Mock Tapcart.action to return a success result
115
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
116
+ const result = yield removeItemFromWishlist({
117
+ Tapcart: mockTapcart,
118
+ productId: "gid://shopify/Product/123",
119
+ selectedVariantId: "gid://shopify/ProductVariant/789",
120
+ });
121
+ expect(result).toBe(true);
122
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
123
+ productId: "gid://shopify/Product/123",
124
+ variantId: "456",
125
+ wishlistId: "wishlist-1",
126
+ });
127
+ }));
128
+ });
129
+ describe("wishlist ID handling", () => {
130
+ it("should return false when wishlist ID is missing", () => __awaiter(void 0, void 0, void 0, function* () {
131
+ // Mock Tapcart with no wishlist ID
132
+ mockTapcart.variables.wishlists = [];
133
+ const result = yield removeItemFromWishlist({
134
+ Tapcart: mockTapcart,
135
+ productId: "gid://shopify/Product/123",
136
+ selectedVariantId: "gid://shopify/ProductVariant/456",
137
+ });
138
+ expect(result).toBe(false);
139
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Missing wishlist id for wishlist removal");
140
+ expect(mockTapcart.action).not.toHaveBeenCalled();
141
+ }));
142
+ it("should use wishlist ID from wishlist entry when available", () => __awaiter(void 0, void 0, void 0, function* () {
143
+ // Mock Tapcart.action to return a success result
144
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
145
+ const result = yield removeItemFromWishlist({
146
+ Tapcart: mockTapcart,
147
+ productId: "gid://shopify/Product/123",
148
+ selectedVariantId: "gid://shopify/ProductVariant/456",
149
+ });
150
+ expect(result).toBe(true);
151
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
152
+ productId: "gid://shopify/Product/123",
153
+ variantId: "456",
154
+ wishlistId: "wishlist-1", // From wishlist entry
155
+ });
156
+ }));
157
+ });
158
+ describe("GID handling", () => {
159
+ it("should extract IDs from GID format", () => __awaiter(void 0, void 0, void 0, function* () {
160
+ // Mock Tapcart with GID format
161
+ mockTapcart.variables.wishlists[0].items[0].productId = "gid://shopify/Product/123?param=value";
162
+ mockTapcart.variables.wishlists[0].items[0].variantId = "gid://shopify/ProductVariant/456?param=value";
163
+ // Mock Tapcart.action to return a success result
164
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
165
+ const result = yield removeItemFromWishlist({
166
+ Tapcart: mockTapcart,
167
+ productId: "gid://shopify/Product/123?param=value",
168
+ selectedVariantId: "gid://shopify/ProductVariant/789?param=value",
169
+ });
170
+ expect(result).toBe(true);
171
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
172
+ productId: "gid://shopify/Product/123?param=value",
173
+ variantId: "456",
174
+ wishlistId: "wishlist-1",
175
+ });
176
+ }));
177
+ it("should handle plain IDs without GID format", () => __awaiter(void 0, void 0, void 0, function* () {
178
+ // Mock Tapcart with plain IDs
179
+ mockTapcart.variables.wishlists[0].items[0].productId = "123";
180
+ mockTapcart.variables.wishlists[0].items[0].variantId = "456";
181
+ // Mock Tapcart.action to return a success result
182
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
183
+ const result = yield removeItemFromWishlist({
184
+ Tapcart: mockTapcart,
185
+ productId: "123",
186
+ selectedVariantId: "789",
187
+ });
188
+ expect(result).toBe(true);
189
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
190
+ productId: "123",
191
+ variantId: "456",
192
+ wishlistId: "wishlist-1",
193
+ });
194
+ }));
195
+ it("should use the provided wishlistId when supplied", () => __awaiter(void 0, void 0, void 0, function* () {
196
+ mockTapcart.variables.wishlists = [];
197
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
198
+ const result = yield removeItemFromWishlist({
199
+ Tapcart: mockTapcart,
200
+ productId: "gid://shopify/Product/123",
201
+ selectedVariantId: "gid://shopify/ProductVariant/456",
202
+ wishlistId: "gid://shopify/Wishlist/789",
203
+ });
204
+ expect(result).toBe(true);
205
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
206
+ productId: "gid://shopify/Product/123",
207
+ variantId: "456",
208
+ wishlistId: "789",
209
+ });
210
+ }));
211
+ });
212
+ describe("edge cases", () => {
213
+ it("should handle undefined wishlists array", () => __awaiter(void 0, void 0, void 0, function* () {
214
+ mockTapcart.variables.wishlists = undefined;
215
+ const result = yield removeItemFromWishlist({
216
+ Tapcart: mockTapcart,
217
+ productId: "gid://shopify/Product/123",
218
+ selectedVariantId: "gid://shopify/ProductVariant/456",
219
+ });
220
+ expect(result).toBe(false);
221
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Missing wishlist id for wishlist removal");
222
+ expect(mockTapcart.action).not.toHaveBeenCalled();
223
+ }));
224
+ it("should handle empty wishlists array", () => __awaiter(void 0, void 0, void 0, function* () {
225
+ mockTapcart.variables.wishlists = [];
226
+ const result = yield removeItemFromWishlist({
227
+ Tapcart: mockTapcart,
228
+ productId: "gid://shopify/Product/123",
229
+ selectedVariantId: "gid://shopify/ProductVariant/456",
230
+ });
231
+ expect(result).toBe(false);
232
+ expect(consoleErrorSpy).toHaveBeenCalledWith("Missing wishlist id for wishlist removal");
233
+ expect(mockTapcart.action).not.toHaveBeenCalled();
234
+ }));
235
+ it("should handle wishlist entry with undefined variantId", () => __awaiter(void 0, void 0, void 0, function* () {
236
+ mockTapcart.variables.wishlists[0].items[0].variantId = undefined;
237
+ // Mock Tapcart.action to return a success result
238
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
239
+ const result = yield removeItemFromWishlist({
240
+ Tapcart: mockTapcart,
241
+ productId: "gid://shopify/Product/123",
242
+ selectedVariantId: "gid://shopify/ProductVariant/789",
243
+ });
244
+ expect(result).toBe(true);
245
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
246
+ productId: "gid://shopify/Product/123",
247
+ variantId: "789",
248
+ wishlistId: "wishlist-1",
249
+ });
250
+ }));
251
+ it("should handle wishlist entry with null variantId", () => __awaiter(void 0, void 0, void 0, function* () {
252
+ mockTapcart.variables.wishlists[0].items[0].variantId = null;
253
+ // Mock Tapcart.action to return a success result
254
+ mockTapcart.action = jest.fn().mockResolvedValue(true);
255
+ const result = yield removeItemFromWishlist({
256
+ Tapcart: mockTapcart,
257
+ productId: "gid://shopify/Product/123",
258
+ selectedVariantId: "gid://shopify/ProductVariant/789",
259
+ });
260
+ expect(result).toBe(true);
261
+ expect(mockTapcart.action).toHaveBeenCalledWith("wishlist/item/remove", {
262
+ productId: "gid://shopify/Product/123",
263
+ variantId: "789",
264
+ wishlistId: "wishlist-1",
265
+ });
266
+ }));
267
+ });
268
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapcart/mobile-components",
3
- "version": "0.11.5",
3
+ "version": "0.11.6",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "style": "dist/styles.css",