@rebilly/instruments 1.0.2-beta.9 → 3.0.0-beta.0

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 (255) hide show
  1. package/.babelrc +13 -4
  2. package/.eslintrc.js +3 -0
  3. package/.prettierrc.js +11 -0
  4. package/README.md +15 -314
  5. package/dist/events/base-event.js +6 -9
  6. package/dist/events/events.spec.js +4 -4
  7. package/dist/events/index.js +2 -1
  8. package/dist/functions/destroy.js +12 -14
  9. package/dist/functions/destroy.spec.js +3 -3
  10. package/dist/functions/mount/fetch-data.js +187 -0
  11. package/dist/functions/mount/fetch-data.spec.js +189 -0
  12. package/dist/functions/mount/index.js +164 -251
  13. package/dist/functions/mount/mount.spec.js +25 -124
  14. package/dist/functions/mount/setup-element.js +40 -0
  15. package/dist/functions/mount/setup-framepay-theme.js +95 -0
  16. package/dist/functions/mount/setup-framepay.js +46 -0
  17. package/dist/functions/mount/setup-i18n.js +33 -0
  18. package/dist/functions/mount/setup-options.js +99 -0
  19. package/dist/functions/mount/setup-options.spec.js +66 -0
  20. package/dist/functions/mount/setup-storefront.js +34 -0
  21. package/dist/functions/mount/setup-styles.js +43 -0
  22. package/dist/functions/on.js +13 -4
  23. package/dist/functions/on.spec.js +19 -5
  24. package/dist/functions/purchase.js +139 -22
  25. package/dist/functions/purchase.spec.js +23 -19
  26. package/dist/functions/setup.js +85 -0
  27. package/dist/functions/setup.spec.js +87 -0
  28. package/dist/functions/show.js +31 -14
  29. package/dist/functions/show.spec.js +47 -18
  30. package/dist/functions/update.js +53 -27
  31. package/dist/functions/update.spec.js +40 -21
  32. package/dist/i18n/en.json +5 -2
  33. package/dist/i18n/es.json +4 -1
  34. package/dist/index.js +67 -56
  35. package/dist/index.spec.js +7 -27
  36. package/dist/loader/index.js +4 -3
  37. package/dist/storefront/index.js +33 -0
  38. package/dist/storefront/invoices.js +27 -0
  39. package/dist/storefront/models/base-model.js +18 -0
  40. package/dist/storefront/models/invoice-model.js +14 -0
  41. package/dist/storefront/models/plan-model.js +4 -35
  42. package/dist/storefront/models/product-model.js +4 -23
  43. package/dist/storefront/models/summary-model.js +12 -25
  44. package/dist/storefront/models/transaction-model.js +31 -0
  45. package/dist/storefront/payment-instruments.js +47 -0
  46. package/dist/storefront/payment-instruments.spec.js +55 -0
  47. package/dist/storefront/plans.js +15 -24
  48. package/dist/storefront/plans.spec.js +17 -44
  49. package/dist/storefront/products.js +16 -20
  50. package/dist/storefront/products.spec.js +25 -49
  51. package/dist/storefront/purchase.js +28 -16
  52. package/dist/storefront/purchase.spec.js +4 -22
  53. package/dist/storefront/ready-to-pay.js +26 -22
  54. package/dist/storefront/ready-to-pay.spec.js +25 -54
  55. package/dist/storefront/storefront.spec.js +1 -1
  56. package/dist/storefront/summary.js +27 -24
  57. package/dist/storefront/summary.spec.js +44 -86
  58. package/dist/storefront/transactions.js +27 -0
  59. package/dist/style/base/__snapshots__/theme.spec.js.snap +188 -45
  60. package/dist/style/base/default-theme.js +699 -0
  61. package/dist/style/base/index.js +48 -16
  62. package/dist/style/base/theme.js +15 -48
  63. package/dist/style/base/theme.spec.js +4 -15
  64. package/dist/style/components/address.js +3 -3
  65. package/dist/style/components/button.js +32 -22
  66. package/dist/style/components/divider.js +9 -9
  67. package/dist/style/components/forms/checkbox.js +12 -9
  68. package/dist/style/components/forms/field.js +18 -6
  69. package/dist/style/components/forms/form.js +2 -2
  70. package/dist/style/components/forms/input.js +54 -13
  71. package/dist/style/components/forms/label.js +44 -18
  72. package/dist/style/components/forms/select.js +54 -22
  73. package/dist/style/components/forms/validation.js +53 -6
  74. package/dist/style/components/icons.js +4 -4
  75. package/dist/style/components/loader.js +5 -3
  76. package/dist/style/components/methods.js +52 -48
  77. package/dist/style/components/overlay.js +5 -5
  78. package/dist/style/helpers/index.js +46 -46
  79. package/dist/style/index.js +3 -1
  80. package/dist/style/payment-instruments/payment-card.js +4 -4
  81. package/dist/style/utils/border.js +47 -0
  82. package/dist/style/utils/color-values.js +37 -3
  83. package/dist/style/utils/remove-empty-null.js +20 -0
  84. package/dist/style/vendor/framepay.js +11 -8
  85. package/dist/style/vendor/postmate.js +2 -2
  86. package/dist/style/views/confirmation.js +13 -13
  87. package/dist/style/views/method-selector.js +3 -3
  88. package/dist/style/views/modal.js +8 -6
  89. package/dist/style/views/result.js +4 -4
  90. package/dist/style/views/summary.js +26 -22
  91. package/dist/utils/format-currency.js +4 -2
  92. package/dist/utils/has-valid-css-selector.js +1 -1
  93. package/dist/utils/process-property-as-dom-element.js +0 -2
  94. package/dist/views/__snapshots__/summary.spec.js.snap +72 -118
  95. package/dist/views/common/iframe/base-iframe.js +10 -2
  96. package/dist/views/common/iframe/modal-iframe.js +50 -4
  97. package/dist/views/confirmation.js +44 -20
  98. package/dist/views/method-selector/express-methods/apple-pay.js +92 -0
  99. package/dist/views/method-selector/express-methods/google-pay.js +31 -0
  100. package/dist/views/method-selector/express-methods/paypal.js +19 -0
  101. package/dist/views/method-selector/generate-digital-wallet.js +68 -0
  102. package/dist/views/method-selector/generate-digital-wallet.spec.js +135 -0
  103. package/dist/views/method-selector/get-payment-methods.js +27 -7
  104. package/dist/views/method-selector/get-payment-methods.spec.js +25 -26
  105. package/dist/views/method-selector/index.js +55 -86
  106. package/dist/views/method-selector/method-selector.spec.js +80 -69
  107. package/dist/views/method-selector/mount-express-methods.js +38 -62
  108. package/dist/views/method-selector/mount-methods.js +18 -18
  109. package/dist/views/modal.js +18 -12
  110. package/dist/views/result.js +13 -16
  111. package/dist/views/summary.js +176 -114
  112. package/dist/views/summary.spec.js +72 -76
  113. package/package.json +7 -4
  114. package/src/events/base-event.js +15 -17
  115. package/src/events/events.spec.js +6 -4
  116. package/src/events/index.js +6 -3
  117. package/src/functions/destroy.js +12 -13
  118. package/src/functions/destroy.spec.js +30 -31
  119. package/src/functions/mount/fetch-data.js +152 -0
  120. package/src/functions/mount/fetch-data.spec.js +238 -0
  121. package/src/functions/mount/index.js +131 -244
  122. package/src/functions/mount/mount.spec.js +35 -141
  123. package/src/functions/mount/setup-element.js +26 -0
  124. package/src/functions/mount/setup-framepay-theme.js +82 -0
  125. package/src/functions/mount/setup-framepay.js +41 -0
  126. package/src/functions/mount/setup-i18n.js +19 -0
  127. package/src/functions/mount/setup-options.js +103 -0
  128. package/src/functions/mount/setup-options.spec.js +60 -0
  129. package/src/functions/mount/setup-storefront.js +24 -0
  130. package/src/functions/mount/setup-styles.js +30 -0
  131. package/src/functions/on.js +13 -8
  132. package/src/functions/on.spec.js +30 -17
  133. package/src/functions/purchase.js +101 -19
  134. package/src/functions/purchase.spec.js +18 -18
  135. package/src/functions/setup.js +48 -0
  136. package/src/functions/setup.spec.js +98 -0
  137. package/src/functions/show.js +20 -10
  138. package/src/functions/show.spec.js +43 -22
  139. package/src/functions/update.js +50 -27
  140. package/src/functions/update.spec.js +57 -22
  141. package/src/i18n/en.json +5 -2
  142. package/src/i18n/es.json +4 -1
  143. package/src/i18n/i18n.spec.js +6 -4
  144. package/src/i18n/index.js +14 -11
  145. package/src/index.js +41 -52
  146. package/src/index.spec.js +8 -37
  147. package/src/loader/index.js +51 -47
  148. package/src/loader/loader.spec.js +26 -19
  149. package/src/storefront/index.js +37 -7
  150. package/src/storefront/invoices.js +11 -0
  151. package/src/storefront/models/base-model.js +10 -0
  152. package/src/storefront/models/invoice-model.js +3 -0
  153. package/src/storefront/models/plan-model.js +3 -35
  154. package/src/storefront/models/product-model.js +3 -23
  155. package/src/storefront/models/ready-to-pay-model.js +3 -3
  156. package/src/storefront/models/summary-model.js +15 -29
  157. package/src/storefront/models/transaction-model.js +19 -0
  158. package/src/storefront/payment-instruments.js +30 -0
  159. package/src/storefront/payment-instruments.spec.js +69 -0
  160. package/src/storefront/plans.js +16 -23
  161. package/src/storefront/plans.spec.js +25 -54
  162. package/src/storefront/products.js +18 -22
  163. package/src/storefront/products.spec.js +23 -54
  164. package/src/storefront/purchase.js +14 -14
  165. package/src/storefront/purchase.spec.js +17 -29
  166. package/src/storefront/ready-to-pay.js +26 -23
  167. package/src/storefront/ready-to-pay.spec.js +41 -71
  168. package/src/storefront/storefront.spec.js +1 -1
  169. package/src/storefront/summary.js +26 -22
  170. package/src/storefront/summary.spec.js +60 -109
  171. package/src/storefront/transactions.js +11 -0
  172. package/src/style/base/__snapshots__/theme.spec.js.snap +188 -45
  173. package/src/style/base/default-theme.js +674 -0
  174. package/src/style/base/index.js +48 -16
  175. package/src/style/base/theme.js +20 -48
  176. package/src/style/base/theme.spec.js +5 -15
  177. package/src/style/browserslist.js +1 -3
  178. package/src/style/components/address.js +3 -3
  179. package/src/style/components/button.js +32 -22
  180. package/src/style/components/divider.js +9 -9
  181. package/src/style/components/forms/checkbox.js +11 -9
  182. package/src/style/components/forms/field.js +18 -6
  183. package/src/style/components/forms/form.js +2 -2
  184. package/src/style/components/forms/input.js +54 -13
  185. package/src/style/components/forms/label.js +44 -18
  186. package/src/style/components/forms/select.js +54 -22
  187. package/src/style/components/forms/validation.js +53 -6
  188. package/src/style/components/icons.js +4 -4
  189. package/src/style/components/index.js +1 -1
  190. package/src/style/components/loader.js +4 -3
  191. package/src/style/components/methods.js +52 -48
  192. package/src/style/components/overlay.js +5 -5
  193. package/src/style/helpers/index.js +46 -46
  194. package/src/style/index.js +4 -2
  195. package/src/style/payment-instruments/payment-card.js +4 -4
  196. package/src/style/utils/border.js +34 -0
  197. package/src/style/utils/color-values.js +30 -4
  198. package/src/style/utils/remove-empty-null.js +10 -0
  199. package/src/style/vendor/framepay.js +12 -9
  200. package/src/style/vendor/postmate.js +3 -3
  201. package/src/style/views/confirmation.js +13 -13
  202. package/src/style/views/index.js +1 -1
  203. package/src/style/views/method-selector.js +3 -3
  204. package/src/style/views/modal.js +9 -7
  205. package/src/style/views/result.js +4 -4
  206. package/src/style/views/summary.js +26 -22
  207. package/src/utils/add-dom-element.js +12 -13
  208. package/src/utils/format-currency.js +6 -2
  209. package/src/utils/has-valid-css-selector.js +2 -2
  210. package/src/utils/is-dom-element.js +1 -1
  211. package/src/utils/process-property-as-dom-element.js +27 -24
  212. package/src/utils/sleep.js +1 -1
  213. package/src/views/__snapshots__/summary.spec.js.snap +72 -118
  214. package/src/views/common/iframe/base-iframe.js +12 -4
  215. package/src/views/common/iframe/event-listeners.js +6 -6
  216. package/src/views/common/iframe/index.js +1 -1
  217. package/src/views/common/iframe/method-iframe.js +3 -6
  218. package/src/views/common/iframe/modal-iframe.js +48 -6
  219. package/src/views/common/iframe/view-iframe.js +3 -5
  220. package/src/views/common/render-utilities.js +3 -3
  221. package/src/views/confirmation.js +34 -25
  222. package/src/views/method-selector/express-methods/apple-pay.js +78 -0
  223. package/src/views/method-selector/express-methods/google-pay.js +24 -0
  224. package/src/views/method-selector/express-methods/paypal.js +7 -0
  225. package/src/views/method-selector/generate-digital-wallet.js +51 -0
  226. package/src/views/method-selector/generate-digital-wallet.spec.js +135 -0
  227. package/src/views/method-selector/get-method-data.js +7 -4
  228. package/src/views/method-selector/get-payment-methods.js +38 -31
  229. package/src/views/method-selector/get-payment-methods.spec.js +26 -33
  230. package/src/views/method-selector/index.js +70 -99
  231. package/src/views/method-selector/method-selector.spec.js +88 -78
  232. package/src/views/method-selector/mount-express-methods.js +36 -60
  233. package/src/views/method-selector/mount-methods.js +32 -21
  234. package/src/views/modal.js +36 -22
  235. package/src/views/result.js +12 -15
  236. package/src/views/summary.js +175 -101
  237. package/src/views/summary.spec.js +99 -74
  238. package/tests/async-utilities.js +22 -0
  239. package/tests/mocks/rebilly-instruments-mock.js +89 -77
  240. package/tests/mocks/storefront-api-mock.js +8 -0
  241. package/tests/mocks/storefront-mock.js +17 -0
  242. package/dist/events/purchase-completed.js +0 -24
  243. package/dist/functions/initialize.js +0 -82
  244. package/dist/functions/initialize.spec.js +0 -34
  245. package/dist/functions/mount/fetch-summary-data.js +0 -31
  246. package/dist/functions/mount/fetch-summary-data.spec.js +0 -45
  247. package/dist/views/method-selector/process-digital-wallet-options.js +0 -35
  248. package/dist/views/method-selector/process-digital-wallet-options.spec.js +0 -80
  249. package/src/events/purchase-completed.js +0 -11
  250. package/src/functions/initialize.js +0 -74
  251. package/src/functions/initialize.spec.js +0 -38
  252. package/src/functions/mount/fetch-summary-data.js +0 -26
  253. package/src/functions/mount/fetch-summary-data.spec.js +0 -46
  254. package/src/views/method-selector/process-digital-wallet-options.js +0 -16
  255. package/src/views/method-selector/process-digital-wallet-options.spec.js +0 -94
@@ -0,0 +1,48 @@
1
+ import { setupPaymentInstrument } from '../storefront/payment-instruments';
2
+ import Events from '../events';
3
+ import { mountModal } from '../views/modal';
4
+ import { DataInstance } from './mount/fetch-data';
5
+
6
+ export async function setup({ state, payload }) {
7
+ try {
8
+ const {instrument, transaction} = await setupPaymentInstrument({
9
+ state,
10
+ data: {
11
+ token: payload._raw.id,
12
+ websiteId: state.options?.websiteId,
13
+ ...payload
14
+ }
15
+ });
16
+
17
+ state.data = new DataInstance({state, instrument, transaction});
18
+
19
+ if (transaction.approvalUrl) {
20
+ const { paymentMethodsUrl } = state.options._computed;
21
+ mountModal({
22
+ state,
23
+ name: 'rebilly-instruments-approval-url',
24
+ url: `${paymentMethodsUrl}/approval-url`,
25
+ model: {
26
+ setup: {transaction}
27
+ },
28
+ close: ({transaction: updatedTransaction = transaction}) => {
29
+ Events.setupCompleted.dispatch({
30
+ instrument,
31
+ transaction: updatedTransaction
32
+ });
33
+ }
34
+ });
35
+ } else {
36
+ Events.setupCompleted.dispatch({instrument, transaction});
37
+ }
38
+
39
+ return {instrument, transaction};
40
+ } catch (error) {
41
+ // TODO: Display error to customer
42
+ console.error(error);
43
+ if(error.status === 422) {
44
+ error.details.forEach(e => console.error(e));
45
+ }
46
+ return error;
47
+ }
48
+ }
@@ -0,0 +1,98 @@
1
+ import { RenderMockRebillyInstruments } from 'tests/mocks/rebilly-instruments-mock';
2
+ import { ok, post } from 'msw-when-then';
3
+ import { when } from 'tests/msw/server';
4
+ import { storefrontURL } from 'tests/mocks/storefront-api-mock';
5
+ import Events from '../events';
6
+ import { avoidUnhandledPromises } from 'tests/async-utilities';
7
+
8
+ describe('RebillyInstruments setup', () => {
9
+ it('should be able to setup a payment instrument', async () => {
10
+ const options = {
11
+ websiteId: 'test-website-id',
12
+ money: {
13
+ amount: 30,
14
+ currency: 'USD'
15
+ },
16
+ transactionType: 'setup'
17
+ };
18
+
19
+ const token = {
20
+ id: 'test-token-id'
21
+ };
22
+ const billingAddress = {
23
+ billing: 'address'
24
+ };
25
+ const deliveryAddress = {
26
+ delivery: 'address'
27
+ };
28
+
29
+ const paymentInstrumentFields = {
30
+ id: 'payment-instrument-id'
31
+ };
32
+
33
+ const paymentInstrumentSetupFields = {
34
+ id: 'payment-instrument-id',
35
+ approvalUrl: null,
36
+ };
37
+
38
+ when(post(`${storefrontURL}/payment-instruments`)).thenReturn(ok(paymentInstrumentFields));
39
+ when(post(`${storefrontURL}/payment-instruments/*/setup`)).thenReturn(ok(paymentInstrumentSetupFields));
40
+ const rebillyInstruments = await RenderMockRebillyInstruments(options);
41
+
42
+ const spyCreatePaymentInstrument = jest.spyOn(rebillyInstruments.state.storefront.paymentInstruments, 'create');
43
+ const spySetupPaymentInstrument = jest.spyOn(rebillyInstruments.state.storefront.paymentInstruments, 'setup');
44
+ const spyDispatchSetupCompleted = jest.spyOn(Events.setupCompleted, 'dispatch');
45
+
46
+ const setupCompletedListener = jest.fn();
47
+
48
+ rebillyInstruments.on('setup-completed', setupCompletedListener);
49
+
50
+ const setupPayload = {
51
+ billingAddress,
52
+ deliveryAddress,
53
+ ...options.money,
54
+ _raw: token
55
+ };
56
+
57
+ rebillyInstruments.mock.data({
58
+ previewPurchase: {
59
+ currency: 'USD',
60
+ total: 30
61
+ }
62
+ });
63
+
64
+ await rebillyInstruments.setup(setupPayload);
65
+
66
+ expect(spyCreatePaymentInstrument).toBeCalledTimes(1);
67
+ expect(spyCreatePaymentInstrument).toBeCalledWith(
68
+ expect.objectContaining({
69
+ data: expect.objectContaining ({
70
+ token: token.id
71
+ })
72
+ })
73
+ );
74
+
75
+ expect(spySetupPaymentInstrument).toBeCalledTimes(1);
76
+ expect(spySetupPaymentInstrument).toBeCalledWith(
77
+ expect.objectContaining({
78
+ id: expect.stringContaining(paymentInstrumentFields.id),
79
+ data: expect.objectContaining ({
80
+ websiteId: rebillyInstruments.state.options.websiteId,
81
+ currency: 'USD',
82
+ amount: 30
83
+ })
84
+ })
85
+ );
86
+
87
+ expect(spyDispatchSetupCompleted).toBeCalledTimes(1);
88
+
89
+ expect(setupCompletedListener).toBeCalledTimes(1);
90
+ expect(setupCompletedListener).toBeCalledWith(
91
+ expect.objectContaining({
92
+ instrument: paymentInstrumentFields,
93
+ transaction: paymentInstrumentSetupFields
94
+ })
95
+ );
96
+ await avoidUnhandledPromises();
97
+ });
98
+ });
@@ -1,23 +1,33 @@
1
+ import { mountConfirmation } from '../views/confirmation';
2
+ import { mountResult } from '../views/result';
3
+
4
+ /**
5
+ @typedef ShowParams
6
+ @type {Object}
7
+ @property {string} componentName - The name of the component to render to the form.
8
+ @property {object} payload - The extra data to provide the component.
9
+ @property {object} state - The global state.
10
+ */
11
+
1
12
  /**
2
13
  * Register events that will be triggered
3
- * @param {string} componentName - The name of the component to render to the form
4
- * @param {function} payload - The extra data to provide the component
14
+ * @param {ShowParams} params
5
15
  */
6
- export async function Show (componentName, payload = {}) {
7
- switch(componentName) {
16
+ export async function show({ componentName, payload, state }) {
17
+ switch (componentName) {
8
18
  case 'result':
9
- this.iframeComponents = this.iframeComponents.filter(iframe => {
10
- iframe.component.destroy();
19
+ state.iframeComponents = state.iframeComponents.filter((iframe) => {
20
+ iframe.destroy();
11
21
  return false;
12
22
  });
13
- this._mountResult.call(this, payload);
23
+ mountResult({payload, state});
14
24
  break;
15
25
  case 'confirmation':
16
- this.iframeComponents = this.iframeComponents.filter(iframe => {
17
- iframe.component.destroy();
26
+ state.iframeComponents = state.iframeComponents.filter((iframe) => {
27
+ iframe.destroy();
18
28
  return false;
19
29
  });
20
- this._mountConfirmation.call(this, payload);
30
+ mountConfirmation({payload, state});
21
31
  break;
22
32
  default:
23
33
  throw new Error(`'${componentName}' not a supported component`);
@@ -1,39 +1,60 @@
1
- import { MockRebillyInstruments } from 'tests/mocks/rebilly-instruments-mock';
1
+ import * as result from '@/views/result';
2
+ import * as confirmation from '@/views/confirmation';
3
+ import { RenderMockRebillyInstruments } from 'tests/mocks/rebilly-instruments-mock';
4
+ import { RebillyInstrumentsInstance } from '../index';
5
+
6
+ const iframeMock = { destroy: jest.fn() };
2
7
 
3
8
  describe('RebillyInstruments show', () => {
4
- it('should show result component', () => {
9
+ it('should show result component', async () => {
10
+ const mountResult = jest
11
+ .spyOn(result, 'mountResult')
12
+ .mockReturnValue(Promise.resolve());
13
+
14
+ const instance = new RebillyInstrumentsInstance();
15
+ instance.state.iframeComponents.push(iframeMock);
16
+
5
17
  const payload = {
6
18
  test: 'value'
7
19
  };
8
- const rebillyInstruments = MockRebillyInstruments();
9
20
 
10
- rebillyInstruments._mountResult = jest.fn();
11
-
12
- rebillyInstruments.show('result', payload);
21
+ await instance.show('result', payload);
13
22
 
14
- expect(rebillyInstruments._mountResult).toBeCalledTimes(1);
15
- expect(rebillyInstruments._mountResult).toBeCalledWith(payload);
23
+ expect(mountResult).toBeCalledTimes(1);
24
+ expect(mountResult).toBeCalledWith({payload, state: instance.state});
25
+ expect(instance.state.iframeComponents).toEqual([]);
16
26
  });
17
27
 
18
- it('should show confirmation component', () => {
28
+ it('should show confirmation component', async () => {
29
+ const mountConfirmation = jest
30
+ .spyOn(confirmation, 'mountConfirmation')
31
+ .mockReturnValue(Promise.resolve());
32
+
33
+ const instance = new RebillyInstrumentsInstance();
34
+ instance.state.iframeComponents.push(iframeMock);
35
+
19
36
  const payload = {
20
37
  test: 'value'
21
38
  };
22
- const rebillyInstruments = MockRebillyInstruments();
23
39
 
24
- rebillyInstruments._mountConfirmation = jest.fn();
25
-
26
- rebillyInstruments.show('confirmation', payload);
40
+ await instance.show('confirmation', payload);
27
41
 
28
- expect(rebillyInstruments._mountConfirmation).toBeCalledTimes(1);
29
- expect(rebillyInstruments._mountConfirmation).toBeCalledWith(payload);
42
+ expect(mountConfirmation).toBeCalledTimes(1);
43
+ expect(mountConfirmation).toBeCalledWith({payload, state: instance.state});
44
+ expect(instance.state.iframeComponents).toEqual([]);
30
45
  });
31
46
 
32
- it('should fail for non supported component', () => {
33
- const rebillyInstruments = MockRebillyInstruments();
34
-
35
- expect(async () => {
36
- rebillyInstruments.show('not-a-component');
37
- }).rejects.toEqual(new Error(`'not-a-component' not a supported component`));
47
+ it('should fail for non supported component', async () => {
48
+ const rebillyInstruments = new RebillyInstrumentsInstance();
49
+
50
+ let error;
51
+ try {
52
+ await rebillyInstruments.show('not-a-component', 'any payload');
53
+ } catch (e) {
54
+ error = e;
55
+ }
56
+ expect(error.toString()).toBe(
57
+ `Error: 'not-a-component' not a supported component`
58
+ );
38
59
  });
39
- });
60
+ });
@@ -1,8 +1,28 @@
1
1
  import merge from 'lodash.merge';
2
- import isEqual from 'lodash.isequal';
2
+ import { destroy } from './destroy';
3
+ import { mount } from './mount';
3
4
 
4
- export async function Update (newConfig) {
5
- if (!this.hasMounted) {
5
+ function sanitizeOldPurchaseData({newOptions}) {
6
+ let purchaseData = {};
7
+ const hasPurchaseDataKeys = Object.keys(newOptions).some(key => [
8
+ 'items',
9
+ 'money',
10
+ 'invoiceId',
11
+ 'transactionId'
12
+ ].includes(key));
13
+
14
+ purchaseData = hasPurchaseDataKeys ? {
15
+ items: null,
16
+ money: null,
17
+ invoiceId: null,
18
+ transactionId: null
19
+ } : purchaseData;
20
+
21
+ return purchaseData;
22
+ };
23
+
24
+ export async function update({state, newOptions = {}}) {
25
+ if (!state.hasMounted) {
6
26
  throw Error('Update method cannot be called before mounting instruments');
7
27
  }
8
28
 
@@ -11,29 +31,32 @@ export async function Update (newConfig) {
11
31
  * https://github.com/Rebilly/framepay/issues/450
12
32
  * That's why this toggle disables real time locale update until that issue is fixed.
13
33
  */
14
- const temporaryDisableRealTimeLocaleUpdate = true;
15
-
16
- const updatingJustLocale = newConfig?.options && isEqual(Object.keys(newConfig?.options), ['locale']);
17
- if (updatingJustLocale && !temporaryDisableRealTimeLocaleUpdate) {
18
- const updatedOptions = merge(this.options, newConfig.options);
19
- const newLocale = newConfig?.options.locale;
20
- this.options = updatedOptions;
21
- this.translate.updateTranslationsToNewLocale(newLocale);
22
- this.iframeComponents.forEach(iframe => iframe.component.call('changeLocale', newLocale));
23
- return;
24
- }
25
-
26
- const updatedOptions = merge(this.options, newConfig.options);
27
- await this.destroy()
28
-
29
- this.initialize(this.configs);
30
-
31
- this.mount({
32
- form: this.form,
33
- summary: this.summary,
34
- options: updatedOptions,
35
- _dev: this._dev
36
- })
37
- };
34
+ // const updatingJustLocale =
35
+ // newOptions?.options && isEqual(Object.keys(newOptions), ['locale']);
36
+ // if (updatingJustLocale) {
37
+ // const updatedOptions = merge(state.options, newOptions);
38
+ // const newLocale = newOptions?.locale;
39
+ // state.options = updatedOptions;
40
+ // state.translate.updateTranslationsToNewLocale(newLocale);
41
+ // state.iframeComponents.forEach((iframe) =>
42
+ // iframe.component.call('changeLocale', newLocale)
43
+ // );
44
+ // return;
45
+ // }
46
+
47
+ const oldOptions = {
48
+ ...state.options,
49
+ ...sanitizeOldPurchaseData({newOptions})
50
+ };
51
+
52
+ const updatedOptions = merge({...oldOptions}, newOptions);
38
53
 
54
+ await destroy({ state });
39
55
 
56
+ mount({
57
+ state,
58
+ form: state.form,
59
+ summary: state.summary,
60
+ ...updatedOptions,
61
+ });
62
+ }
@@ -1,65 +1,100 @@
1
1
  import { RenderMockRebillyInstruments } from 'tests/mocks/rebilly-instruments-mock';
2
+ import { avoidUnhandledPromises } from 'tests/async-utilities';
2
3
 
3
4
  describe('RebillyInstruments Update', () => {
4
5
  it('should throw an error when methods are not mounted', async () => {
5
6
  const rebillyInstruments = await RenderMockRebillyInstruments();
6
7
 
7
- rebillyInstruments.hasMounted = false;
8
-
8
+ rebillyInstruments.state.hasMounted = false;
9
+
9
10
  let error;
10
11
  try {
11
12
  await rebillyInstruments.update();
12
13
  } catch (e) {
13
14
  error = e;
14
15
  }
15
-
16
- expect(error).toEqual(new Error('Update method cannot be called before mounting instruments'));
16
+
17
+ expect(error).toEqual(
18
+ new Error('Update method cannot be called before mounting instruments')
19
+ );
17
20
  });
18
21
 
19
- // TODO: Un skip this test when we activate real time update (currently disabled by feature toggle in update.js)
22
+ // TODO: Un skip this test when we activate real time update (currently disabled by feature toggle in update.js)
20
23
  it.skip('should call changeLocale when updating locale options', async () => {
21
24
  const rebillyInstruments = await RenderMockRebillyInstruments();
22
-
25
+
23
26
  const call = jest.fn();
24
- //Simulate mounted iframe
27
+ // Simulate mounted iframe
25
28
  const fakeIFrameComponent = {
26
29
  destroy: jest.fn(),
27
30
  component: {
28
31
  call
29
32
  }
30
33
  };
31
- rebillyInstruments.iframeComponents = [fakeIFrameComponent];
32
-
33
- await rebillyInstruments.update({options: {locale: 'ja'}});
34
+ rebillyInstruments.state.iframeComponents = [fakeIFrameComponent];
35
+
36
+ await rebillyInstruments.update({ locale: 'ja' });
34
37
 
35
38
  expect(call).toBeCalledWith('changeLocale', 'ja');
36
- // It would be better to match real UI instead of implementation state
37
- expect(rebillyInstruments.options.locale).toBe('ja');
38
- expect(rebillyInstruments.translate.locale).toBe('ja');
39
+ // It would be better to match real UI instead of implementation state
40
+ expect(rebillyInstruments.state.options.locale).toBe('ja');
41
+ expect(rebillyInstruments.state.translate.locale).toBe('ja');
39
42
  });
40
43
 
41
44
  it('should destroy and update when updating options different than locale', async () => {
42
45
  const rebillyInstruments = await RenderMockRebillyInstruments();
43
46
  const formElement = document.querySelector('.form-selector');
44
47
  const summaryElement = document.querySelector('.summary-selector');
45
-
48
+
46
49
  expect(formElement.innerHTML).not.toEqual('');
47
50
  expect(summaryElement.innerHTML).not.toEqual('');
48
-
51
+
52
+
49
53
  const call = jest.fn();
50
- //Simulate mounted iframe
54
+ // Simulate mounted iframe
51
55
  const fakeIFrameComponent = {
56
+ destroy: jest.fn(),
57
+ component: {
58
+ call
59
+ }
60
+ };
61
+ rebillyInstruments.state.iframeComponents = [fakeIFrameComponent];
62
+
63
+ await rebillyInstruments.update({
64
+ countryCode: 'ES'
65
+ });
66
+
67
+ expect(rebillyInstruments.state.options.countryCode).toEqual('ES');
68
+ await avoidUnhandledPromises();
69
+ });
70
+ it('should replace the items with different options', async () => {
71
+ const rebillyInstruments = await RenderMockRebillyInstruments();
72
+ const formElement = document.querySelector('.form-selector');
73
+ const summaryElement = document.querySelector('.summary-selector');
74
+
75
+ expect(formElement.innerHTML).not.toEqual('');
76
+ expect(summaryElement.innerHTML).not.toEqual('');
77
+
78
+ const call = jest.fn();
79
+ // Simulate mounted iframe
80
+ const fakeIFrameComponent = {
81
+ destroy: jest.fn(),
52
82
  component: {
53
- destroy: jest.fn(),
54
83
  call
55
84
  }
56
85
  };
57
86
  rebillyInstruments.iframeComponents = [fakeIFrameComponent];
58
87
 
59
- await rebillyInstruments.update({options: {intent: {countryCode: 'ES'}}});
60
-
61
- expect(rebillyInstruments.options.intent.countryCode).toEqual('ES');
62
- expect(formElement.innerHTML).toEqual('');
63
- expect(summaryElement.innerHTML).toEqual('');
88
+ await rebillyInstruments.update({
89
+ items: [
90
+ {
91
+ planId: 'test-plan-id-1',
92
+ quantity: 5
93
+ }
94
+ ]
95
+ });
96
+
97
+ expect(rebillyInstruments.state.options.items[0].quantity).toEqual(5);
98
+ await avoidUnhandledPromises();
64
99
  });
65
100
  });
package/src/i18n/en.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "en": {
3
3
  "summary": {
4
4
  "subTotal": "Sub Total",
5
- "discounts": "Discouts",
5
+ "discounts": "Discounts",
6
6
  "taxes": "Taxes",
7
7
  "shipping": "Shipping",
8
8
  "total": "Total"
@@ -10,7 +10,10 @@
10
10
  "form": {
11
11
  "expressCheckout": "Express checkout",
12
12
  "or": "Or",
13
- "popupOverlayText": "Click here to show popup window"
13
+ "popupOverlayText": "Click here to show popup window",
14
+ "error": {
15
+ "noPaymentMethods": "No payment methods available for this transaction, please contact support."
16
+ }
14
17
  },
15
18
  "paymentMethods": {
16
19
  "payment-card": "Payment card"
package/src/i18n/es.json CHANGED
@@ -10,7 +10,10 @@
10
10
  "form": {
11
11
  "expressCheckout": "Chequeo rápido",
12
12
  "or": "O pague con",
13
- "popupOverlayText": "Haga clic aquí para mostrar la ventana emergente"
13
+ "popupOverlayText": "Haga clic aquí para mostrar la ventana emergente",
14
+ "error": {
15
+ "noPaymentMethods": "No hay métodos de pago disponibles para esta transacción, por favor, póngase en contacto con el servicio de asistencia."
16
+ }
14
17
  },
15
18
  "paymentMethods": {
16
19
  "payment-card": "Tarjeta de crédito"
@@ -1,5 +1,5 @@
1
1
  import { Translate } from './index';
2
-
2
+
3
3
  describe('i18n Class', () => {
4
4
  const canTranslate = document.createElement('div');
5
5
  canTranslate.innerHTML = 'Discounts';
@@ -16,9 +16,11 @@ describe('i18n Class', () => {
16
16
  const translate = new Translate();
17
17
  translate.init('es-US');
18
18
  translate.translateItems();
19
-
20
- const [translated, untouched] = document.querySelectorAll('[data-rebilly-i18n]');
21
-
19
+
20
+ const [translated, untouched] = document.querySelectorAll(
21
+ '[data-rebilly-i18n]'
22
+ );
23
+
22
24
  expect(translated.innerHTML).toEqual('Descuentos');
23
25
  expect(untouched.innerHTML).toEqual('Untouched');
24
26
  });
package/src/i18n/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import merge from 'lodash.merge';
2
2
  import en from './en.json';
3
3
  import es from './es.json';
4
-
4
+
5
5
  export class Translate {
6
6
  constructor() {
7
7
  this.locale = '';
@@ -12,15 +12,18 @@ export class Translate {
12
12
  init(locale, messages) {
13
13
  this.items = document.querySelectorAll('[data-rebilly-i18n]');
14
14
  this.locale = this.getLocale(locale);
15
- this.languages = merge({...en, ...es}, messages);
15
+ this.languages = merge({ ...en, ...es }, messages);
16
16
  }
17
17
 
18
18
  translateItems() {
19
19
  this.items = document.querySelectorAll('[data-rebilly-i18n]');
20
-
20
+
21
21
  if (this.locale in this.languages) {
22
- return this.items.forEach(item => {
23
- const translate = this.getTranslation(this.languages[this.locale], item.dataset.rebillyI18n);
22
+ return this.items.forEach((item) => {
23
+ const translate = this.getTranslation(
24
+ this.languages[this.locale],
25
+ item.dataset.rebillyI18n
26
+ );
24
27
  if (translate) {
25
28
  item.innerHTML = translate;
26
29
  }
@@ -35,8 +38,7 @@ export class Translate {
35
38
  }
36
39
 
37
40
  getLocale(locale = this.locale) {
38
- if (!locale.includes('-')
39
- || locale in this.languages) {
41
+ if (!locale.includes('-') || locale in this.languages) {
40
42
  return locale;
41
43
  }
42
44
  locale = locale.replace(/[-._]\w+$/gi, '');
@@ -45,12 +47,13 @@ export class Translate {
45
47
 
46
48
  translateItem(item) {
47
49
  const locale = this.getLocale();
48
- return this.getTranslation(this.languages[locale], item.dataset.rebillyI18n);
50
+ return this.getTranslation(
51
+ this.languages[locale],
52
+ item.dataset.rebillyI18n
53
+ );
49
54
  }
50
55
 
51
56
  getTranslation(lan = this.locale, prop) {
52
- return prop
53
- .split('.')
54
- .reduce((acc, val) => acc?.[val], lan);
57
+ return prop.split('.').reduce((acc, val) => acc?.[val], lan);
55
58
  }
56
59
  }