@rebilly/instruments 3.16.3-beta.0 → 3.16.4-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 +2 -2
- package/src/functions/mount/fetch-data.js +15 -10
- package/src/functions/mount/fetch-data.spec.js +11 -11
- package/src/functions/mount/index.js +5 -0
- package/src/i18n/index.js +3 -3
- package/src/storefront/invoices.js +12 -3
- package/src/storefront/invoices.spec.js +56 -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.4-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": {
|
|
@@ -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,17 @@ 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
|
+
{
|
|
152
|
+
data: {
|
|
153
|
+
id: state.options?.invoiceId || state.data?.transaction?.invoiceId
|
|
154
|
+
},
|
|
155
|
+
state,
|
|
156
|
+
});
|
|
157
|
+
productsPromise = Promise.resolve(products);
|
|
158
|
+
state.data.invoice = invoice;
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
if (!riskMetadata) {
|
|
@@ -163,10 +169,9 @@ export async function fetchData({
|
|
|
163
169
|
const previewPurchasePromise = state.options.items ?
|
|
164
170
|
fetchSummary({ state }) : null;
|
|
165
171
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
} else {
|
|
172
|
+
if (!state.options?.jwt) {
|
|
173
|
+
// There is no invoice, only plans through the static options,
|
|
174
|
+
// so we should fetch the products from the provided plan names.
|
|
170
175
|
productsPromise = fetchProductsFromPlans({ state });
|
|
171
176
|
}
|
|
172
177
|
|
|
@@ -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;
|
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,20 @@
|
|
|
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.map(items => new ProductModel(items._embedded.product));
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
products,
|
|
17
|
+
invoice: new InvoiceModel(fields),
|
|
18
|
+
};
|
|
10
19
|
});
|
|
11
20
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
});
|
|
@@ -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
|
-
});
|