@rebilly/instruments 3.16.2-beta.0 → 3.16.5-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.
- package/dist/index.js +9 -9
- package/dist/index.min.js +8 -8
- package/package.json +3 -3
- package/src/functions/mount/fetch-data.js +14 -10
- package/src/functions/mount/fetch-data.spec.js +11 -11
- package/src/functions/mount/index.js +5 -0
- package/src/functions/mount/setup-storefront.js +3 -2
- package/src/i18n/index.js +3 -3
- package/src/storefront/invoices.js +13 -3
- package/src/storefront/invoices.spec.js +88 -0
- package/tests/mocks/storefront-api-mock.js +14 -13
- package/src/storefront/products.js +0 -27
- package/src/storefront/products.spec.js +0 -73
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebilly/instruments",
|
|
3
|
-
"version": "3.16.
|
|
3
|
+
"version": "3.16.5-beta.0",
|
|
4
4
|
"author": "Rebilly",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"lodash.kebabcase": "^4.1.1",
|
|
24
24
|
"lodash.merge": "^4.6.2",
|
|
25
25
|
"popostmate": "^1.6.4",
|
|
26
|
-
"rebilly-js-sdk": "^47.
|
|
26
|
+
"rebilly-js-sdk": "^47.13.2",
|
|
27
27
|
"values.js": "^2.0.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"component-emitter": "^1.3.0",
|
|
38
38
|
"core-js": "3.23.3",
|
|
39
39
|
"jest": "^27.0.6",
|
|
40
|
-
"msw": "0.
|
|
40
|
+
"msw": "0.45.0",
|
|
41
41
|
"msw-when-then": "^1.5.1",
|
|
42
42
|
"rollup": "^2.35.1",
|
|
43
43
|
"rollup-plugin-ignore": "^1.0.10",
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { collectData } from '@rebilly/risk-data-collector';
|
|
2
2
|
import { fetchProductsFromPlans } from '../../storefront/fetch-products-from-plans';
|
|
3
|
-
import { fetchProducts } from '../../storefront/products';
|
|
4
3
|
import { fetchReadyToPay } from '../../storefront/ready-to-pay';
|
|
5
4
|
import { fetchSummary } from '../../storefront/summary';
|
|
6
|
-
import {
|
|
5
|
+
import { fetchInvoiceAndProducts as FetchInvoiceAndProducts } from '../../storefront/invoices';
|
|
7
6
|
import { fetchTransaction as FetchTransaction } from '../../storefront/transactions';
|
|
8
7
|
import { fetchAccount as FetchAccount } from '../../storefront/account';
|
|
9
8
|
import { fetchPaymentInstrument as FetchInstruments } from '../../storefront/payment-instruments';
|
|
@@ -121,7 +120,7 @@ export async function fetchData({
|
|
|
121
120
|
riskMetadata = null,
|
|
122
121
|
|
|
123
122
|
// Dependency injectable functions
|
|
124
|
-
|
|
123
|
+
fetchInvoiceAndProducts = FetchInvoiceAndProducts,
|
|
125
124
|
fetchTransaction = FetchTransaction,
|
|
126
125
|
fetchAccount = FetchAccount,
|
|
127
126
|
fetchInstruments = FetchInstruments
|
|
@@ -146,10 +145,16 @@ export async function fetchData({
|
|
|
146
145
|
}, state});
|
|
147
146
|
}
|
|
148
147
|
|
|
148
|
+
let productsPromise;
|
|
149
149
|
if (state.options?.invoiceId || state.data?.transaction?.hasInvoice) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
const { invoice, products } = await fetchInvoiceAndProducts({
|
|
151
|
+
data: {
|
|
152
|
+
id: state.options?.invoiceId || state.data?.transaction?.invoiceId
|
|
153
|
+
},
|
|
154
|
+
state,
|
|
155
|
+
});
|
|
156
|
+
productsPromise = Promise.resolve(products);
|
|
157
|
+
state.data.invoice = invoice;
|
|
153
158
|
}
|
|
154
159
|
|
|
155
160
|
if (!riskMetadata) {
|
|
@@ -163,10 +168,9 @@ export async function fetchData({
|
|
|
163
168
|
const previewPurchasePromise = state.options.items ?
|
|
164
169
|
fetchSummary({ state }) : null;
|
|
165
170
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
} else {
|
|
171
|
+
if (!state.options?.jwt) {
|
|
172
|
+
// There is no invoice, only plans through the static options,
|
|
173
|
+
// so we should fetch the products from the provided plan names.
|
|
170
174
|
productsPromise = fetchProductsFromPlans({ state });
|
|
171
175
|
}
|
|
172
176
|
|
|
@@ -4,7 +4,7 @@ import TransactionModel from '../../storefront/models/transaction-model';
|
|
|
4
4
|
|
|
5
5
|
describe('fetchData function', () => {
|
|
6
6
|
it('Should use correct invoice id for invoiceId', async () => {
|
|
7
|
-
const
|
|
7
|
+
const mockFetchInvoiceAndProducts = jest.fn();
|
|
8
8
|
const invoiceId = 'test-invoice-id';
|
|
9
9
|
const state = new StorefontTestingInstance({
|
|
10
10
|
options: {
|
|
@@ -14,11 +14,11 @@ describe('fetchData function', () => {
|
|
|
14
14
|
|
|
15
15
|
await fetchData({
|
|
16
16
|
state,
|
|
17
|
-
|
|
17
|
+
fetchInvoiceAndProducts: mockFetchInvoiceAndProducts
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
expect(
|
|
21
|
-
expect(
|
|
20
|
+
expect(mockFetchInvoiceAndProducts).toBeCalledTimes(1);
|
|
21
|
+
expect(mockFetchInvoiceAndProducts).toBeCalledWith(
|
|
22
22
|
expect.objectContaining({
|
|
23
23
|
data:{
|
|
24
24
|
id: invoiceId
|
|
@@ -28,7 +28,7 @@ describe('fetchData function', () => {
|
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
it('Should use correct invoice id for transaction with invoiceIds', async () => {
|
|
31
|
-
const
|
|
31
|
+
const mockFetchInvoiceAndProducts = jest.fn();
|
|
32
32
|
const invoiceId = 'test-invoice-id';
|
|
33
33
|
const state = new StorefontTestingInstance({
|
|
34
34
|
options: {},
|
|
@@ -41,11 +41,11 @@ describe('fetchData function', () => {
|
|
|
41
41
|
|
|
42
42
|
await fetchData({
|
|
43
43
|
state,
|
|
44
|
-
|
|
44
|
+
fetchInvoiceAndProducts: mockFetchInvoiceAndProducts
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
expect(
|
|
48
|
-
expect(
|
|
47
|
+
expect(mockFetchInvoiceAndProducts).toBeCalledTimes(1);
|
|
48
|
+
expect(mockFetchInvoiceAndProducts).toBeCalledWith(
|
|
49
49
|
expect.objectContaining({
|
|
50
50
|
data:{
|
|
51
51
|
id: invoiceId
|
|
@@ -55,7 +55,7 @@ describe('fetchData function', () => {
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
it('Should not fetch invoice for transaction with no invoice Ids', async () => {
|
|
58
|
-
const
|
|
58
|
+
const mockFetchInvoiceAndProducts = jest.fn();
|
|
59
59
|
const invoiceState = {
|
|
60
60
|
options: {},
|
|
61
61
|
data: {
|
|
@@ -67,10 +67,10 @@ describe('fetchData function', () => {
|
|
|
67
67
|
|
|
68
68
|
fetchData({
|
|
69
69
|
state: invoiceState,
|
|
70
|
-
|
|
70
|
+
fetchInvoiceAndProducts: mockFetchInvoiceAndProducts
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
expect(
|
|
73
|
+
expect(mockFetchInvoiceAndProducts).toBeCalledTimes(0);
|
|
74
74
|
|
|
75
75
|
});
|
|
76
76
|
|
|
@@ -135,6 +135,11 @@ export async function mount({
|
|
|
135
135
|
}
|
|
136
136
|
state.i18n({state});
|
|
137
137
|
state.hasMounted = true;
|
|
138
|
+
|
|
139
|
+
if (!data.readyToPay.length) {
|
|
140
|
+
state.loader.stopLoading({id: 'rebilly-instruments-form'});
|
|
141
|
+
showError(state.translate.getTranslation('form.error.noPaymentMethods'))
|
|
142
|
+
}
|
|
138
143
|
} catch (error) {
|
|
139
144
|
showError(error);
|
|
140
145
|
throw error;
|
|
@@ -3,13 +3,14 @@ import Storefront from '../../storefront';
|
|
|
3
3
|
export default ({
|
|
4
4
|
options:{
|
|
5
5
|
publishableKey,
|
|
6
|
-
|
|
6
|
+
organizationId,
|
|
7
7
|
apiMode,
|
|
8
8
|
_dev
|
|
9
9
|
}
|
|
10
10
|
}) => {
|
|
11
|
+
|
|
11
12
|
const storefront = {
|
|
12
|
-
|
|
13
|
+
organizationId,
|
|
13
14
|
mode: apiMode || 'live'
|
|
14
15
|
};
|
|
15
16
|
|
package/src/i18n/index.js
CHANGED
|
@@ -21,8 +21,8 @@ export class Translate {
|
|
|
21
21
|
if (this.locale in this.languages) {
|
|
22
22
|
return this.items.forEach((item) => {
|
|
23
23
|
const translate = this.getTranslation(
|
|
24
|
+
item.dataset.rebillyI18n,
|
|
24
25
|
this.languages[this.locale],
|
|
25
|
-
item.dataset.rebillyI18n
|
|
26
26
|
);
|
|
27
27
|
if (translate) {
|
|
28
28
|
item.innerHTML = translate;
|
|
@@ -48,12 +48,12 @@ export class Translate {
|
|
|
48
48
|
translateItem(item) {
|
|
49
49
|
const locale = this.getLocale();
|
|
50
50
|
return this.getTranslation(
|
|
51
|
+
item.dataset.rebillyI18n,
|
|
51
52
|
this.languages[locale],
|
|
52
|
-
item.dataset.rebillyI18n
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
getTranslation(lan = this.locale
|
|
56
|
+
getTranslation(prop, lan = this.languages[this.locale]) {
|
|
57
57
|
return prop.split('.').reduce((acc, val) => acc?.[val], lan);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import InvoiceModel from './models/invoice-model';
|
|
2
|
+
import ProductModel from './models/product-model';
|
|
2
3
|
import { Endpoint } from './index';
|
|
3
4
|
|
|
4
|
-
export async function
|
|
5
|
+
export async function fetchInvoiceAndProducts({ data = null, state = null }) {
|
|
5
6
|
return Endpoint({state}, async () => {
|
|
6
7
|
state.storefront.setSessionToken(state.options.jwt);
|
|
7
|
-
const {fields} = await state.storefront.invoices.get(
|
|
8
|
+
const {fields} = await state.storefront.invoices.get({
|
|
9
|
+
...data,
|
|
10
|
+
expand: 'items.*.product'
|
|
11
|
+
});
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
const products = fields.items.filter(item => item._embedded)
|
|
14
|
+
.map(items => new ProductModel(items._embedded.product));
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
products,
|
|
18
|
+
invoice: new InvoiceModel(fields),
|
|
19
|
+
};
|
|
10
20
|
});
|
|
11
21
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { StorefontTestingInstance } from 'tests/mocks/storefront-mock';
|
|
2
|
+
import { ok, get } from 'msw-when-then';
|
|
3
|
+
import { when } from 'tests/msw/server';
|
|
4
|
+
import { storefrontURL } from 'tests/mocks/storefront-api-mock';
|
|
5
|
+
import { fetchInvoiceAndProducts } from './invoices';
|
|
6
|
+
import ProductModel from './models/product-model';
|
|
7
|
+
import InvoiceModel from './models/invoice-model';
|
|
8
|
+
|
|
9
|
+
describe('Storefront Invoices', () => {
|
|
10
|
+
it('can fetch an invoice and its embedded products', async () => {
|
|
11
|
+
const id = '1234';
|
|
12
|
+
const testInvoice = {
|
|
13
|
+
id: 'test-invoice-id-1',
|
|
14
|
+
items: [
|
|
15
|
+
{
|
|
16
|
+
_embedded: {
|
|
17
|
+
product: {
|
|
18
|
+
id: 'test-product-id'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
when(get(`${storefrontURL}/invoices/${id}`)).thenReturn(ok(testInvoice));
|
|
26
|
+
|
|
27
|
+
const instance = new StorefontTestingInstance({
|
|
28
|
+
data: {
|
|
29
|
+
invoice: {
|
|
30
|
+
id
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
jest.spyOn(instance.storefront.invoices, 'get');
|
|
36
|
+
|
|
37
|
+
const { invoice, products } = await fetchInvoiceAndProducts({
|
|
38
|
+
data: { id },
|
|
39
|
+
state: instance
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(instance.storefront.invoices.get).toBeCalledTimes(1);
|
|
43
|
+
expect(instance.storefront.invoices.get).toBeCalledWith({
|
|
44
|
+
id,
|
|
45
|
+
expand: 'items.*.product'
|
|
46
|
+
});
|
|
47
|
+
expect(invoice).toBeInstanceOf(InvoiceModel);
|
|
48
|
+
expect(products).toBeInstanceOf(Array);
|
|
49
|
+
expect(products[0]).toBeInstanceOf(ProductModel);
|
|
50
|
+
expect(products[0]).toMatchInlineSnapshot(`
|
|
51
|
+
ProductModel {
|
|
52
|
+
"id": "test-product-id",
|
|
53
|
+
}
|
|
54
|
+
`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('can fetch an invoice when it does not have any products', async () => {
|
|
58
|
+
const id = '1234';
|
|
59
|
+
const testInvoice = {
|
|
60
|
+
id: 'test-invoice-id-1',
|
|
61
|
+
items: [
|
|
62
|
+
{
|
|
63
|
+
id: 'test',
|
|
64
|
+
price: 0.99,
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
when(get(`${storefrontURL}/invoices/${id}`)).thenReturn(ok(testInvoice));
|
|
70
|
+
|
|
71
|
+
const instance = new StorefontTestingInstance({
|
|
72
|
+
data: {
|
|
73
|
+
invoice: {
|
|
74
|
+
id
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const { invoice, products } = await fetchInvoiceAndProducts({
|
|
80
|
+
data: { id },
|
|
81
|
+
state: instance
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(invoice).toBeInstanceOf(InvoiceModel);
|
|
85
|
+
expect(products).toBeInstanceOf(Array);
|
|
86
|
+
expect(products.length).toBe(0);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -6,42 +6,43 @@ export const initStoreFrontApiMocks = (when) => {
|
|
|
6
6
|
const mocks = [
|
|
7
7
|
/* GET */
|
|
8
8
|
{
|
|
9
|
-
url: '
|
|
9
|
+
url: '*/account',
|
|
10
10
|
method: get,
|
|
11
11
|
response: () => ok({})
|
|
12
12
|
}, {
|
|
13
|
-
url: '
|
|
13
|
+
url: '*/plans',
|
|
14
14
|
method: get,
|
|
15
15
|
response: () => ok([])
|
|
16
16
|
}, {
|
|
17
|
-
url: '
|
|
17
|
+
url: '*/payment-instruments',
|
|
18
18
|
method: get,
|
|
19
19
|
response: () => ok([])
|
|
20
20
|
}, {
|
|
21
|
-
url: '/
|
|
22
|
-
method: get,
|
|
23
|
-
response: () => ok([])
|
|
24
|
-
}, {
|
|
25
|
-
url: '/transactions/test-transaction-id',
|
|
21
|
+
url: '*/transactions/test-transaction-id',
|
|
26
22
|
method: get,
|
|
27
23
|
response: () => ok({})
|
|
28
24
|
},
|
|
29
25
|
|
|
30
26
|
/* POST */
|
|
31
27
|
{
|
|
32
|
-
url: '
|
|
28
|
+
url: '*/ready-to-pay',
|
|
33
29
|
method: post,
|
|
34
|
-
response: () => ok([
|
|
30
|
+
response: () => ok([
|
|
31
|
+
{
|
|
32
|
+
filters: [],
|
|
33
|
+
method: "method",
|
|
34
|
+
}
|
|
35
|
+
])
|
|
35
36
|
}, {
|
|
36
|
-
url: '
|
|
37
|
+
url: '*/preview-purchase',
|
|
37
38
|
method: post,
|
|
38
39
|
response: () => ok({})
|
|
39
40
|
}, {
|
|
40
|
-
url: '
|
|
41
|
+
url: '*/payment-instruments',
|
|
41
42
|
method: post,
|
|
42
43
|
response: () => ok([])
|
|
43
44
|
}, {
|
|
44
|
-
url: '
|
|
45
|
+
url: '*/payment-instruments/*/setup',
|
|
45
46
|
method: post,
|
|
46
47
|
response: () => ok([])
|
|
47
48
|
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import ProductModel from './models/product-model';
|
|
2
|
-
import { Endpoint } from './index';
|
|
3
|
-
|
|
4
|
-
export async function fetchProducts({ state }) {
|
|
5
|
-
return Endpoint({state}, async () => {
|
|
6
|
-
const plansData = state.data.invoice?.items ?? [];
|
|
7
|
-
|
|
8
|
-
const filterByProductId = {
|
|
9
|
-
filter: ''
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
if (plansData.length) {
|
|
13
|
-
filterByProductId.filter = `id:${plansData
|
|
14
|
-
.map((item) => item.productId)
|
|
15
|
-
.join(',')}`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Only fetch products if we hae specific products to fetch
|
|
19
|
-
if (filterByProductId.filter.length) {
|
|
20
|
-
const { items: productItems } = await state.storefront.products.getAll(
|
|
21
|
-
filterByProductId
|
|
22
|
-
);
|
|
23
|
-
return productItems.map(({ fields }) => new ProductModel(fields));
|
|
24
|
-
}
|
|
25
|
-
return [];
|
|
26
|
-
});
|
|
27
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { StorefontTestingInstance } from 'tests/mocks/storefront-mock';
|
|
2
|
-
import { ok, get } from 'msw-when-then';
|
|
3
|
-
import { when } from 'tests/msw/server';
|
|
4
|
-
import { storefrontURL } from 'tests/mocks/storefront-api-mock';
|
|
5
|
-
import { fetchProducts } from './products';
|
|
6
|
-
import ProductModel from './models/product-model';
|
|
7
|
-
import { expectConfigurationError } from 'tests/async-utilities';
|
|
8
|
-
|
|
9
|
-
describe('Storefront API Plan', () => {
|
|
10
|
-
it('can fetch products', async () => {
|
|
11
|
-
const testProduct = { name: 'Test Product', id: 'test-product-id-1' };
|
|
12
|
-
|
|
13
|
-
when(get(`${storefrontURL}/products`)).thenReturn(ok([testProduct]));
|
|
14
|
-
|
|
15
|
-
const instance = new StorefontTestingInstance({
|
|
16
|
-
data: {
|
|
17
|
-
invoice: {
|
|
18
|
-
items: [
|
|
19
|
-
{
|
|
20
|
-
productId: 'test-product-id'
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
jest.spyOn(instance.storefront.products, 'getAll');
|
|
28
|
-
|
|
29
|
-
const response = await fetchProducts({ state: instance });
|
|
30
|
-
|
|
31
|
-
expect(instance.storefront.products.getAll).toBeCalledTimes(1);
|
|
32
|
-
expect(instance.storefront.products.getAll).toBeCalledWith({
|
|
33
|
-
filter: 'id:test-product-id'
|
|
34
|
-
});
|
|
35
|
-
expect(response).toBeInstanceOf(Array);
|
|
36
|
-
expect(response[0]).toBeInstanceOf(ProductModel);
|
|
37
|
-
expect(response).toEqual([new ProductModel(testProduct)]);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('can fetch products with filter', async () => {
|
|
41
|
-
const instance = new StorefontTestingInstance({
|
|
42
|
-
data: {
|
|
43
|
-
invoice: {
|
|
44
|
-
items: [
|
|
45
|
-
{
|
|
46
|
-
productId: 'test-product-1'
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
productId: 'test-product-2'
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
jest.spyOn(instance.storefront.products, 'getAll');
|
|
57
|
-
|
|
58
|
-
await fetchProducts({ state: instance });
|
|
59
|
-
|
|
60
|
-
expect(instance.storefront.products.getAll).toBeCalledWith({
|
|
61
|
-
filter: 'id:test-product-1,test-product-2'
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should throw errors with no options', async () => {
|
|
66
|
-
const noConfigOrOptionsInstance = new StorefontTestingInstance({
|
|
67
|
-
options: null
|
|
68
|
-
});
|
|
69
|
-
await expectConfigurationError(
|
|
70
|
-
fetchProducts({ state: noConfigOrOptionsInstance })
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
});
|