@zoxllc/shopify-checkout-extensions 0.0.1

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.
Files changed (41) hide show
  1. package/README.md +3 -0
  2. package/package.json +31 -0
  3. package/src/common/AndSelector.ts +35 -0
  4. package/src/common/BuyXGetY.ts +109 -0
  5. package/src/common/Campaign.ts +55 -0
  6. package/src/common/CampaignConfiguration.ts +10 -0
  7. package/src/common/CampaignFactory.ts +180 -0
  8. package/src/common/CartAmountQualifier.ts +64 -0
  9. package/src/common/CartHasItemQualifier.ts +64 -0
  10. package/src/common/CartQuantityQualifier.ts +73 -0
  11. package/src/common/CustomerEmailQualifier.ts +60 -0
  12. package/src/common/CustomerSubscriberQualifier.ts +32 -0
  13. package/src/common/CustomerTagQualifier.ts +56 -0
  14. package/src/common/DiscountCart.ts +130 -0
  15. package/src/common/DiscountInterface.ts +13 -0
  16. package/src/common/OrSelector.ts +35 -0
  17. package/src/common/PostCartAmountQualifier.ts +21 -0
  18. package/src/common/ProductHandleSelector.ts +29 -0
  19. package/src/common/ProductIdSelector.ts +28 -0
  20. package/src/common/ProductTagSelector.ts +54 -0
  21. package/src/common/ProductTypeSelector.ts +34 -0
  22. package/src/common/Qualifier.ts +67 -0
  23. package/src/common/SaleItemSelector.ts +35 -0
  24. package/src/common/Selector.ts +53 -0
  25. package/src/common/SubscriptionItemSelector.ts +21 -0
  26. package/src/generated/api.ts +2103 -0
  27. package/src/index.ts +39 -0
  28. package/src/lineItem/ConditionalDiscount.ts +102 -0
  29. package/src/lineItem/DiscountCodeList.ts +91 -0
  30. package/src/lineItem/FixedItemDiscount.ts +53 -0
  31. package/src/lineItem/PercentageDiscount.ts +46 -0
  32. package/tests/AndSelector.test.ts +27 -0
  33. package/tests/CartQuantityQualifier.test.ts +381 -0
  34. package/tests/CustomerSubscriberQualifier.test.ts +101 -0
  35. package/tests/DiscountCart.test.ts +115 -0
  36. package/tests/OrSelector.test.ts +27 -0
  37. package/tests/ProductTagSelector.test.ts +75 -0
  38. package/tests/Qualifier.test.ts +193 -0
  39. package/tests/SaleItemSelector.test.ts +113 -0
  40. package/tests/Selector.test.ts +83 -0
  41. package/tsconfig.json +25 -0
@@ -0,0 +1,193 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ NumericalComparisonType,
4
+ Qualifier,
5
+ } from '../src/common/Qualifier';
6
+ import type { DiscountCart } from '../src/common/DiscountCart';
7
+ import type { CartLine } from '../src/generated/api';
8
+ import type { Selector } from '../src/common/Selector';
9
+
10
+ export class MockTruthyQualifier extends Qualifier {
11
+ match(
12
+ subject: DiscountCart | CartLine,
13
+ selector?: Selector | undefined
14
+ ): boolean {
15
+ return true;
16
+ }
17
+ }
18
+
19
+ export class MockFalseyQualifier extends Qualifier {
20
+ match(
21
+ subject: DiscountCart | CartLine,
22
+ selector?: Selector | undefined
23
+ ): boolean {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ describe('Qualifier', () => {
29
+ const qualifier = new Qualifier();
30
+ it('Contains a root compareAmounts method', () => {
31
+ expect(qualifier.compareAmounts).toBeDefined();
32
+ });
33
+
34
+ it('Compares amounts properly for all cases', () => {
35
+ // EQUAL_TO
36
+ expect(
37
+ qualifier.compareAmounts(
38
+ 10,
39
+ NumericalComparisonType.EQUAL_TO,
40
+ 10
41
+ ),
42
+ 'Must be TRUE when 10 EQUAL_TO 10'
43
+ ).toEqual(true);
44
+ expect(
45
+ qualifier.compareAmounts(
46
+ 1,
47
+ NumericalComparisonType.EQUAL_TO,
48
+ 10
49
+ ),
50
+ 'Must be FALSE when 1 EQUAL_TO 10'
51
+ ).toEqual(false);
52
+ expect(
53
+ qualifier.compareAmounts(
54
+ 10,
55
+ NumericalComparisonType.EQUAL_TO,
56
+ 1
57
+ ),
58
+ 'Must be FALSE when 10 EQUAL_TO 1'
59
+ ).toEqual(false);
60
+
61
+ // GREATER_THAN
62
+ expect(
63
+ qualifier.compareAmounts(
64
+ 11,
65
+ NumericalComparisonType.GREATER_THAN,
66
+ 10
67
+ ),
68
+ 'Must be TRUE when 11 GREATER_THAN 10'
69
+ ).toEqual(true);
70
+ expect(
71
+ qualifier.compareAmounts(
72
+ 1,
73
+ NumericalComparisonType.GREATER_THAN,
74
+ 10
75
+ ),
76
+ 'Must be FALSE when 1 GREATER_THAN 10'
77
+ ).toEqual(false);
78
+ expect(
79
+ qualifier.compareAmounts(
80
+ 10,
81
+ NumericalComparisonType.GREATER_THAN,
82
+ 1
83
+ ),
84
+ 'Must be TRUE when 10 GREATER_THAN 1'
85
+ ).toEqual(true);
86
+
87
+ // GREATER_THAN_OR_EQUAL
88
+ expect(
89
+ qualifier.compareAmounts(
90
+ 11,
91
+ NumericalComparisonType.GREATER_THAN_OR_EQUAL,
92
+ 10
93
+ ),
94
+ 'Must be TRUE when 11 GREATER_THAN_OR_EQUAL 10'
95
+ ).toEqual(true);
96
+ expect(
97
+ qualifier.compareAmounts(
98
+ 10,
99
+ NumericalComparisonType.GREATER_THAN_OR_EQUAL,
100
+ 10
101
+ ),
102
+ 'Must be TRUE when 10 GREATER_THAN_OR_EQUAL 10'
103
+ ).toEqual(true);
104
+ expect(
105
+ qualifier.compareAmounts(
106
+ 1,
107
+ NumericalComparisonType.GREATER_THAN_OR_EQUAL,
108
+ 10
109
+ ),
110
+ 'Must be FALSE when 1 GREATER_THAN_OR_EQUAL 10'
111
+ ).toEqual(false);
112
+ expect(
113
+ qualifier.compareAmounts(
114
+ 10,
115
+ NumericalComparisonType.GREATER_THAN_OR_EQUAL,
116
+ 1
117
+ ),
118
+ 'Must be TRUE when 10 GREATER_THAN_OR_EQUAL 1'
119
+ ).toEqual(true);
120
+
121
+ // LESS_THAN
122
+ expect(
123
+ qualifier.compareAmounts(
124
+ 11,
125
+ NumericalComparisonType.LESS_THAN,
126
+ 10
127
+ ),
128
+ 'Must be FALSE when 11 LESS_THAN 10'
129
+ ).toEqual(false);
130
+ expect(
131
+ qualifier.compareAmounts(
132
+ 1,
133
+ NumericalComparisonType.LESS_THAN,
134
+ 10
135
+ ),
136
+ 'Must be TRUE when 1 LESS_THAN 10'
137
+ ).toEqual(true);
138
+ expect(
139
+ qualifier.compareAmounts(
140
+ 10,
141
+ NumericalComparisonType.LESS_THAN,
142
+ 1
143
+ ),
144
+ 'Must be FALSE when 10 LESS_THAN 1'
145
+ ).toEqual(false);
146
+ expect(
147
+ qualifier.compareAmounts(
148
+ 1,
149
+ NumericalComparisonType.LESS_THAN,
150
+ 1
151
+ ),
152
+ 'Must be FALSE when 1 LESS_THAN 1'
153
+ ).toEqual(false);
154
+
155
+ // LESS_THAN_OR_EQUAL
156
+ expect(
157
+ qualifier.compareAmounts(
158
+ 11,
159
+ NumericalComparisonType.LESS_THAN_OR_EQUAL,
160
+ 10
161
+ ),
162
+ 'Must be FALSE when 11 LESS_THAN_OR_EQUAL 10'
163
+ ).toEqual(false);
164
+ expect(
165
+ qualifier.compareAmounts(
166
+ 10,
167
+ NumericalComparisonType.LESS_THAN_OR_EQUAL,
168
+ 10
169
+ ),
170
+ 'Must be TRUE when 10 LESS_THAN_OR_EQUAL 10'
171
+ ).toEqual(true);
172
+ expect(
173
+ qualifier.compareAmounts(
174
+ 1,
175
+ NumericalComparisonType.LESS_THAN_OR_EQUAL,
176
+ 10
177
+ ),
178
+ 'Must be TRUE when 1 LESS_THAN_OR_EQUAL 10'
179
+ ).toEqual(true);
180
+ expect(
181
+ qualifier.compareAmounts(
182
+ 10,
183
+ NumericalComparisonType.LESS_THAN_OR_EQUAL,
184
+ 1
185
+ ),
186
+ 'Must be FALSE when 10 LESS_THAN_OR_EQUAL 1'
187
+ ).toEqual(false);
188
+ });
189
+
190
+ it('Will always return false when calling match()', () => {
191
+ expect(qualifier.match({} as DiscountCart)).toBeFalsy();
192
+ });
193
+ });
@@ -0,0 +1,113 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ CartLine,
4
+ CurrencyCode,
5
+ WeightUnit,
6
+ } from '../src/generated/api';
7
+ import _ from 'lodash';
8
+ import {
9
+ SaleItemSelector,
10
+ SaleItemSelectorMatchType,
11
+ } from '../src/common/SaleItemSelector';
12
+
13
+ describe('SaleItemSelector', () => {
14
+ it('returns true when item is on sale and we pass :IS', () => {
15
+ const isSelector = new SaleItemSelector(
16
+ SaleItemSelectorMatchType.IS
17
+ );
18
+ const isResult = isSelector.match(lineItemOnSale);
19
+ expect(isResult).toEqual(true);
20
+ });
21
+
22
+ it('returns false when item is on sale and we pass :NOT', () => {
23
+ const notSelector = new SaleItemSelector(
24
+ SaleItemSelectorMatchType.NOT
25
+ );
26
+ const notResult = notSelector.match(lineItemOnSale);
27
+ expect(notResult).toEqual(false);
28
+ });
29
+
30
+ it('returns false when item is NOT on sale and we pass :IS', () => {
31
+ const isSelector = new SaleItemSelector(
32
+ SaleItemSelectorMatchType.IS
33
+ );
34
+ const isResult = isSelector.match(lineItemNotOnSale);
35
+ expect(isResult).toEqual(false);
36
+ });
37
+
38
+ it('returns true when :NOT matchtype passed and item is NOT on sale', () => {
39
+ const notSelector = new SaleItemSelector(
40
+ SaleItemSelectorMatchType.NOT
41
+ );
42
+ const notResult = notSelector.match(lineItemNotOnSale);
43
+ expect(notResult).toEqual(true);
44
+ });
45
+
46
+ const lineItemOnSale: CartLine = {
47
+ id: 'gid://shopify/CartLine/111111',
48
+ cost: {
49
+ amountPerQuantity: {
50
+ amount: '8.0',
51
+ currencyCode: CurrencyCode.Usd,
52
+ },
53
+ subtotalAmount: {
54
+ amount: '64.0',
55
+ currencyCode: CurrencyCode.Usd,
56
+ },
57
+ totalAmount: {
58
+ amount: '64.0',
59
+ currencyCode: CurrencyCode.Usd,
60
+ },
61
+ compareAtAmountPerQuantity: {
62
+ amount: '10.0',
63
+ currencyCode: CurrencyCode.Usd,
64
+ },
65
+ },
66
+ quantity: 8,
67
+ merchandise: {
68
+ __typename: 'ProductVariant',
69
+ id: 'gid://shopify/ProductVariant/43900890579178',
70
+ product: {
71
+ title: 'Wristband Product',
72
+ handle: 'wristband-product',
73
+ id: 'gid://shopify/Product/8069766840554',
74
+ productType: 'Wristband - Single',
75
+ inAnyCollection: false,
76
+ inCollections: [],
77
+ hasAnyTag: false,
78
+ isGiftCard: false,
79
+ hasTags: [
80
+ {
81
+ tag: 'meta-exclude-gift',
82
+ hasTag: false,
83
+ },
84
+ {
85
+ tag: 'feel-good-mystery-3-pack',
86
+ hasTag: true,
87
+ },
88
+ ],
89
+ },
90
+ requiresShipping: false,
91
+ weightUnit: WeightUnit.Pounds,
92
+ },
93
+ };
94
+
95
+ const lineItemNotOnSale = _.cloneDeep(lineItemOnSale);
96
+ _.merge(lineItemNotOnSale, {
97
+ cost: {
98
+ amountPerQuantity: {
99
+ amount: '8.0',
100
+ currencyCode: CurrencyCode.Usd,
101
+ },
102
+ subtotalAmount: {
103
+ amount: '64.0',
104
+ currencyCode: CurrencyCode.Usd,
105
+ },
106
+ totalAmount: {
107
+ amount: '64.0',
108
+ currencyCode: CurrencyCode.Usd,
109
+ },
110
+ compareAtAmountPerQuantity: null,
111
+ },
112
+ } as CartLine);
113
+ });
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import type { CartLine } from '../src/generated/api';
3
+ import { Selector } from '../src/common/Selector';
4
+ import { StringComparisonType } from '../src/common/Qualifier';
5
+
6
+ export enum MatchType {
7
+ 'ALL' = ':all',
8
+ 'IS_ONE' = ':is_one',
9
+ 'NOT_ONE' = ':not_one',
10
+ }
11
+
12
+ describe('Selector', () => {
13
+ it('always returns true when calling match method', () => {
14
+ const selector = new Selector();
15
+ expect(selector.match({} as CartLine)).toBeTruthy();
16
+ });
17
+
18
+ it('is true when part of the string is present', () => {
19
+ const selector = new Selector();
20
+ const result = selector.partialMatch(
21
+ StringComparisonType.CONTAINS,
22
+ ['tag1', 'tag3'],
23
+ ['ag', 'a']
24
+ );
25
+
26
+ expect(result).toEqual(true);
27
+ });
28
+
29
+ it('is false when part of the string is not present', () => {
30
+ const selector = new Selector();
31
+ const result = selector.partialMatch(
32
+ StringComparisonType.CONTAINS,
33
+ ['tag1', 'tag3'],
34
+ ['sandwich', 'marshmallow']
35
+ );
36
+
37
+ expect(result).toEqual(false);
38
+ });
39
+
40
+ it('is true when it starts with a string', () => {
41
+ const selector = new Selector();
42
+ const result = selector.partialMatch(
43
+ StringComparisonType.START_WITH,
44
+ ['tag1', 'tag3'],
45
+ ['ta', 'tag']
46
+ );
47
+
48
+ expect(result).toEqual(true);
49
+ });
50
+
51
+ it('is false when it does NOT start with that string', () => {
52
+ const selector = new Selector();
53
+ const result = selector.partialMatch(
54
+ StringComparisonType.START_WITH,
55
+ ['tag1', 'tag3'],
56
+ ['ag1', 'ag3']
57
+ );
58
+
59
+ expect(result).toEqual(false);
60
+ });
61
+
62
+ it('is true when it ends with a string', () => {
63
+ const selector = new Selector();
64
+ const result = selector.partialMatch(
65
+ StringComparisonType.END_WITH,
66
+ ['tag1', 'tag3'],
67
+ ['ag3', 'coconuts']
68
+ );
69
+
70
+ expect(result).toEqual(true);
71
+ });
72
+
73
+ it('is false when it does NOT end with that string', () => {
74
+ const selector = new Selector();
75
+ const result = selector.partialMatch(
76
+ StringComparisonType.END_WITH,
77
+ ['tag1', 'tag3'],
78
+ ['ta', 'tag']
79
+ );
80
+
81
+ expect(result).toEqual(false);
82
+ });
83
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "include": ["**/*.ts", "**/*.tsx"],
3
+ "compilerOptions": {
4
+ "lib": ["DOM", "DOM.Iterable", "ES2019"],
5
+ "strict": true,
6
+ "skipLibCheck": true,
7
+ "isolatedModules": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "removeComments": false,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "noEmit": true,
12
+ "allowJs": true,
13
+ "jsx": "react-jsx",
14
+ "module": "Node16",
15
+ "moduleResolution": "Node16",
16
+ "target": "ES2019",
17
+ "baseUrl": ".",
18
+ "paths": {
19
+ "~/*": ["./src/*"]
20
+ },
21
+ "types": [
22
+ "node", "@shopify/app-bridge-types"
23
+ ]
24
+ }
25
+ }