@rebilly/framepay-react 2.0.0 → 2.1.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 (80) hide show
  1. package/.env.example +2 -0
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +7 -6
  4. package/env.js +7 -0
  5. package/package.json +23 -17
  6. package/src/index.spec.ts +19 -0
  7. package/src/index.ts +42 -0
  8. package/src/lib/components/elements/applepay-element.tsx +36 -0
  9. package/src/lib/components/elements/bank-element.spec.tsx +119 -0
  10. package/src/lib/components/elements/bank-element.tsx +74 -0
  11. package/src/lib/components/elements/base-element.tsx +100 -0
  12. package/src/lib/components/elements/card-element.spec.tsx +113 -0
  13. package/src/lib/components/elements/card-element.tsx +73 -0
  14. package/src/lib/components/elements/googlepay-element.tsx +36 -0
  15. package/src/lib/components/elements/iban-element.spec.tsx +104 -0
  16. package/src/lib/components/elements/iban-element.tsx +75 -0
  17. package/src/lib/components/elements/paypal-element.tsx +36 -0
  18. package/src/lib/components/injector.spec.tsx +162 -0
  19. package/src/lib/components/injector.tsx +495 -0
  20. package/src/lib/components/provider.spec.tsx +98 -0
  21. package/src/lib/components/provider.tsx +111 -0
  22. package/src/lib/constants.ts +43 -0
  23. package/src/lib/context.ts +24 -0
  24. package/src/lib/dom-util.ts +35 -0
  25. package/src/lib/framepay-error.ts +59 -0
  26. package/src/lib/get-rebilly-api.ts +11 -0
  27. package/test/e2e/assets/prop-types.js +849 -0
  28. package/test/e2e/assets/react-0.14.0.js +18759 -0
  29. package/test/e2e/assets/react-15.0.0.js +19309 -0
  30. package/test/e2e/assets/react-16.js +3318 -0
  31. package/test/e2e/assets/react-17.js +3357 -0
  32. package/test/e2e/assets/react-18.js +3342 -0
  33. package/test/e2e/assets/react-dom-0.14.0.js +42 -0
  34. package/test/e2e/assets/react-dom-15.0.0.js +42 -0
  35. package/test/e2e/assets/react-dom-16.js +25147 -0
  36. package/test/e2e/assets/react-dom-17.js +26292 -0
  37. package/test/e2e/assets/react-dom-18.js +29869 -0
  38. package/test/e2e/cypress-support.js +2 -0
  39. package/test/e2e/cypress.d.ts +1 -0
  40. package/test/e2e/fixtures/apple-pay.html +15 -0
  41. package/test/e2e/fixtures/apple-pay.js +63 -0
  42. package/test/e2e/fixtures/bank-separate.html +12 -0
  43. package/test/e2e/fixtures/bank-separate.js +323 -0
  44. package/test/e2e/fixtures/card-separate-brands.html +12 -0
  45. package/test/e2e/fixtures/card-separate-brands.js +260 -0
  46. package/test/e2e/fixtures/card-separate-rebilly-fields.html +12 -0
  47. package/test/e2e/fixtures/card-separate-rebilly-fields.js +281 -0
  48. package/test/e2e/fixtures/card-separate.html +12 -0
  49. package/test/e2e/fixtures/card-separate.js +227 -0
  50. package/test/e2e/fixtures/checkout-combined.html +12 -0
  51. package/test/e2e/fixtures/checkout-combined.js +199 -0
  52. package/test/e2e/fixtures/google-pay.html +15 -0
  53. package/test/e2e/fixtures/google-pay.js +63 -0
  54. package/test/e2e/fixtures/iban.html +12 -0
  55. package/test/e2e/fixtures/iban.js +239 -0
  56. package/test/e2e/fixtures/multiple-methods.html +12 -0
  57. package/test/e2e/fixtures/multiple-methods.js +470 -0
  58. package/test/e2e/fixtures/nav.js +20 -0
  59. package/test/e2e/fixtures/paypal.html +15 -0
  60. package/test/e2e/fixtures/paypal.js +62 -0
  61. package/test/e2e/fixtures/style.css +55 -0
  62. package/test/e2e/fixtures/util.js +71 -0
  63. package/test/e2e/local-server.mjs +12 -0
  64. package/test/e2e/server.mjs +43 -0
  65. package/test/e2e/specs/bank-separate.cy.ts +27 -0
  66. package/test/e2e/specs/card-separate-brands.cy.ts +70 -0
  67. package/test/e2e/specs/card-separate.cy.ts +27 -0
  68. package/test/e2e/specs/checkout-combined.cy.ts +24 -0
  69. package/test/e2e/specs/google-pay.cy.ts +13 -0
  70. package/test/e2e/specs/iban.cy.ts +17 -0
  71. package/test/e2e/specs/multiple-methods.cy.ts +130 -0
  72. package/test/e2e/specs/paypal.cy.ts +13 -0
  73. package/test/e2e/specs/react-version.cy.ts +12 -0
  74. package/test/e2e/switch-react-version.js +42 -0
  75. package/test/e2e/tsconfig.json +8 -0
  76. package/test/unit/jest.config.js +29 -0
  77. package/test/unit/specs/declaration-mock.spec.tsx +143 -0
  78. package/tsconfig.json +31 -0
  79. package/tsconfig.spec.json +13 -0
  80. package/tslint.json +36 -0
@@ -0,0 +1,73 @@
1
+ import * as React from 'react';
2
+ import FramePayError from '../../framepay-error';
3
+ import BaseElement from './base-element';
4
+
5
+ export default class CardElement extends BaseElement<CardProps, CardState> {
6
+ setupElement() {
7
+ const { onReady, onChange, onFocus, onBlur, elementType } = this.props;
8
+
9
+ const makeElement = () => {
10
+ // elementNode already checked in BaseElement.handleSetupElement
11
+ // just ts checks fix
12
+ if (!this.elementNode) {
13
+ throw FramePayError({
14
+ code: FramePayError.codes.elementMountError,
15
+ details: `CardElement invalid elementNode, elementType: ${elementType ||
16
+ 'default'}`
17
+ });
18
+ }
19
+
20
+ try {
21
+ return this.props.Rebilly.card.mount(
22
+ this.elementNode,
23
+ elementType
24
+ );
25
+ } catch (e) {
26
+ throw FramePayError({
27
+ code: FramePayError.codes.elementMountError,
28
+ details: `CardElement error in remote api call, elementType: ${elementType ||
29
+ 'default'}`,
30
+ trace: e
31
+ });
32
+ }
33
+ };
34
+
35
+ const element = makeElement();
36
+
37
+ try {
38
+ element.on('ready', () => {
39
+ this.setState({ ready: true }, () => {
40
+ if (onReady) {
41
+ onReady();
42
+ }
43
+ });
44
+ });
45
+
46
+ element.on('change', (data: PaymentElementOnChangeEventData) => {
47
+ if (onChange) {
48
+ onChange(data);
49
+ }
50
+ });
51
+
52
+ element.on('focus', () => {
53
+ if (onFocus) {
54
+ onFocus();
55
+ }
56
+ });
57
+ element.on('blur', () => {
58
+ if (onBlur) {
59
+ onBlur();
60
+ }
61
+ });
62
+
63
+ this.setState({ element });
64
+ } catch (e) {
65
+ throw FramePayError({
66
+ code: FramePayError.codes.elementMountError,
67
+ details: `CardElement events binding error, elementType: ${elementType ||
68
+ 'default'}`,
69
+ trace: e
70
+ });
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import FramePayError from '../../framepay-error';
3
+ import BaseElement from './base-element';
4
+
5
+ export default class GooglePayElement extends BaseElement<
6
+ GooglePayProps,
7
+ GooglePayState
8
+ > {
9
+ setupElement() {
10
+ const { Rebilly } = this.props;
11
+
12
+ const makeElement = () => {
13
+ // elementNode already checked in BaseElement.handleSetupElement
14
+ // just ts checks fix
15
+ if (!this.elementNode) {
16
+ throw FramePayError({
17
+ code: FramePayError.codes.elementMountError,
18
+ details: `GooglePayElement invalid elementNode`
19
+ });
20
+ }
21
+
22
+ try {
23
+ return Rebilly.googlePay.mount(this.elementNode);
24
+ } catch (e) {
25
+ throw FramePayError({
26
+ code: FramePayError.codes.elementMountError,
27
+ details: `GooglePayElement error in remote api call`,
28
+ trace: e
29
+ });
30
+ }
31
+ };
32
+
33
+ const element = makeElement();
34
+ this.setState({ element });
35
+ }
36
+ }
@@ -0,0 +1,104 @@
1
+ import { Substitute } from '@fluffy-spoon/substitute';
2
+ import { render } from '@testing-library/react';
3
+ import * as React from 'react';
4
+ import FramePayError from '../../framepay-error';
5
+ import IBANElement from './iban-element';
6
+
7
+ describe('IBANElement', () => {
8
+ it('should not setup the element while api is not ready', () => {
9
+ const props = Substitute.for<IBANProps>();
10
+
11
+ props.Rebilly.ready.returns(false);
12
+
13
+ const spy = jest.spyOn(IBANElement.prototype, 'setupElement');
14
+
15
+ render(<IBANElement {...props} Rebilly={props.Rebilly} />);
16
+ expect(spy).not.toHaveBeenCalled();
17
+ });
18
+
19
+ it('should setup the element when api is ready', () => {
20
+ const props = Substitute.for<IBANProps>();
21
+ const spy = jest.spyOn(IBANElement.prototype, 'setupElement');
22
+
23
+ props.Rebilly.ready.returns(false);
24
+
25
+ const { rerender } = render(
26
+ <IBANElement {...props} Rebilly={props.Rebilly} />
27
+ );
28
+
29
+ expect(spy).toHaveBeenCalledTimes(0);
30
+
31
+ const nextProps = Substitute.for<IBANProps>();
32
+
33
+ rerender(
34
+ <IBANElement
35
+ {...nextProps}
36
+ Rebilly={{ iban: nextProps.Rebilly.iban, ready: true }}
37
+ />
38
+ );
39
+
40
+ expect(spy).toHaveBeenCalledTimes(1);
41
+ });
42
+
43
+ it('should render the empty div element', () => {
44
+ const props = Substitute.for<IBANProps>();
45
+ const { container } = render(
46
+ <IBANElement {...props} Rebilly={props.Rebilly} />
47
+ );
48
+ expect(container.firstChild).toMatchInlineSnapshot(`<div />`);
49
+ });
50
+
51
+ it('should destroy the element on component unmount', done => {
52
+ const props = Substitute.for<IBANProps>();
53
+ const element = Substitute.for<PaymentElement>();
54
+
55
+ element.destroy().mimicks(() => {
56
+ done();
57
+ });
58
+
59
+ class TmpComponent extends React.Component {
60
+ render() {
61
+ return (
62
+ <IBANElement
63
+ {...props}
64
+ Rebilly={{
65
+ ...props.Rebilly,
66
+ iban: {
67
+ ...props.Rebilly.iban,
68
+ mount: () => element
69
+ },
70
+ ready: true
71
+ }}
72
+ />
73
+ );
74
+ }
75
+ }
76
+
77
+ const { unmount } = render(<TmpComponent />);
78
+ unmount();
79
+ });
80
+
81
+ it('should fail the element mount on remote error', () => {
82
+ const props = Substitute.for<IBANProps>();
83
+
84
+ try {
85
+ render(
86
+ <IBANElement
87
+ {...props}
88
+ Rebilly={{
89
+ ...props.Rebilly,
90
+ iban: {
91
+ ...props.Rebilly.iban,
92
+ mount: null
93
+ },
94
+ ready: true
95
+ }}
96
+ />
97
+ );
98
+ // never
99
+ expect(true).toEqual(false);
100
+ } catch (error) {
101
+ expect(error.code).toEqual(FramePayError.codes.elementMountError);
102
+ }
103
+ });
104
+ });
@@ -0,0 +1,75 @@
1
+ import * as React from 'react';
2
+ import FramePayError from '../../framepay-error';
3
+ import BaseElement from './base-element';
4
+
5
+ export default class IBANElement extends BaseElement<IBANProps, IBANState> {
6
+ setupElement() {
7
+ const { onReady, onChange, onFocus, onBlur, elementType } = this.props;
8
+
9
+ const makeElement = () => {
10
+ // elementNode already checked in BaseElement.handleSetupElement
11
+ // just ts checks fix
12
+ if (!this.elementNode) {
13
+ throw FramePayError({
14
+ code: FramePayError.codes.elementMountError,
15
+ details: `IBANElement invalid elementNode, elementType: ${elementType ||
16
+ 'default'}`
17
+ });
18
+ }
19
+
20
+ try {
21
+ return this.props.Rebilly.iban.mount(this.elementNode);
22
+ } catch (e) {
23
+ throw FramePayError({
24
+ code: FramePayError.codes.elementMountError,
25
+ details: `IBANElement error in remote api call, elementType: ${elementType ||
26
+ 'default'}`,
27
+ trace: e
28
+ });
29
+ }
30
+ };
31
+
32
+ const element = makeElement();
33
+
34
+ try {
35
+ element.on('ready', () => {
36
+ this.setState({ ready: true }, () => {
37
+ if (onReady) {
38
+ onReady();
39
+ }
40
+ });
41
+ });
42
+
43
+ element.on('change', (data: PaymentElementOnChangeEventData) => {
44
+ if (onChange) {
45
+ onChange(data);
46
+ }
47
+ });
48
+
49
+ element.on('focus', () => {
50
+ if (onFocus) {
51
+ onFocus();
52
+ }
53
+ });
54
+
55
+ element.on('blur', () => {
56
+ if (onBlur) {
57
+ onBlur();
58
+ }
59
+ });
60
+
61
+ this.setState({ element });
62
+ } catch (e) {
63
+ throw FramePayError({
64
+ code: FramePayError.codes.elementMountError,
65
+ details: `IBANElement events binding error, elementType: ${elementType ||
66
+ 'default'}`,
67
+ trace: e
68
+ });
69
+ }
70
+ }
71
+
72
+ render() {
73
+ return <div ref={node => (this.elementNode = node)} />;
74
+ }
75
+ }
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import FramePayError from '../../framepay-error';
3
+ import BaseElement from './base-element';
4
+
5
+ export default class PaypalElement extends BaseElement<
6
+ PaypalProps,
7
+ PaypalState
8
+ > {
9
+ setupElement() {
10
+ const { Rebilly } = this.props;
11
+
12
+ const makeElement = () => {
13
+ // elementNode already checked in BaseElement.handleSetupElement
14
+ // just ts checks fix
15
+ if (!this.elementNode) {
16
+ throw FramePayError({
17
+ code: FramePayError.codes.elementMountError,
18
+ details: `PaypalElement invalid elementNode`
19
+ });
20
+ }
21
+
22
+ try {
23
+ return Rebilly.paypal.mount(this.elementNode);
24
+ } catch (e) {
25
+ throw FramePayError({
26
+ code: FramePayError.codes.elementMountError,
27
+ details: `PaypalElement error in remote api call`,
28
+ trace: e
29
+ });
30
+ }
31
+ };
32
+
33
+ const element = makeElement();
34
+ this.setState({ element });
35
+ }
36
+ }
@@ -0,0 +1,162 @@
1
+ // tslint:disable:max-classes-per-file
2
+ import { Substitute } from '@fluffy-spoon/substitute';
3
+ import { render } from '@testing-library/react';
4
+ import * as React from 'react';
5
+
6
+ import { withFramePay } from '../..';
7
+
8
+ import { FramePayComponentProps } from '../../../types/injector';
9
+
10
+ import { ContextProvider } from '../context';
11
+
12
+ describe('Injector', () => {
13
+ it('withFramePay HOC the context data should be provided', () => {
14
+ expect(true).toEqual(true);
15
+
16
+ interface OriginalProps {
17
+ readonly someProperty: string;
18
+ }
19
+
20
+ class TMPComponent extends React.Component<
21
+ OriginalProps & FramePayComponentProps
22
+ > {
23
+ render() {
24
+ return (
25
+ <div>
26
+ error: {this.props.Rebilly.error}
27
+ ready: {String(this.props.Rebilly.ready)}
28
+ </div>
29
+ );
30
+ }
31
+ }
32
+
33
+ const context = Substitute.for<FramePayContext>();
34
+ const TMPWrapper = withFramePay(TMPComponent);
35
+
36
+ class App extends React.Component {
37
+ render() {
38
+ return (
39
+ <ContextProvider
40
+ value={{
41
+ ...context,
42
+ error: 'withFramePay',
43
+ ready: true
44
+ }}
45
+ >
46
+ <TMPWrapper />
47
+ </ContextProvider>
48
+ );
49
+ }
50
+ }
51
+
52
+ const { container } = render(<App />);
53
+ expect(container.firstChild).toMatchInlineSnapshot(`
54
+ <div>
55
+ error:
56
+ withFramePay
57
+ ready:
58
+ true
59
+ </div>
60
+ `);
61
+ });
62
+
63
+ it('withFramePayCardComponent HOC the context data should be provided', () => {
64
+ expect(true).toEqual(true);
65
+
66
+ interface OriginalProps {
67
+ readonly someProperty: string;
68
+ }
69
+
70
+ class TMPComponent extends React.Component<
71
+ OriginalProps & FramePayComponentProps
72
+ > {
73
+ render() {
74
+ return (
75
+ <div>
76
+ error: {this.props.Rebilly.error}
77
+ ready: {String(this.props.Rebilly.ready)}
78
+ </div>
79
+ );
80
+ }
81
+ }
82
+
83
+ const context = Substitute.for<FramePayContext>();
84
+ const TMPWrapper = withFramePay(TMPComponent);
85
+
86
+ class App extends React.Component {
87
+ render() {
88
+ return (
89
+ <ContextProvider
90
+ value={{
91
+ ...context,
92
+ error: 'withFramePayCardComponent',
93
+ ready: true
94
+ }}
95
+ >
96
+ <TMPWrapper />
97
+ </ContextProvider>
98
+ );
99
+ }
100
+ }
101
+
102
+ const { container } = render(<App />);
103
+ expect(container.firstChild).toMatchInlineSnapshot(`
104
+ <div>
105
+ error:
106
+ withFramePayCardComponent
107
+ ready:
108
+ true
109
+ </div>
110
+ `);
111
+ });
112
+
113
+ it('withFramePayBankComponent HOC the context data should be provided', () => {
114
+ expect(true).toEqual(true);
115
+
116
+ interface OriginalProps {
117
+ readonly someProperty: string;
118
+ }
119
+
120
+ class TMPComponent extends React.Component<
121
+ OriginalProps & FramePayComponentProps
122
+ > {
123
+ render() {
124
+ return (
125
+ <div>
126
+ error: {this.props.Rebilly.error}
127
+ ready: {String(this.props.Rebilly.ready)}
128
+ </div>
129
+ );
130
+ }
131
+ }
132
+
133
+ const context = Substitute.for<FramePayContext>();
134
+ const TMPWrapper = withFramePay(TMPComponent);
135
+
136
+ class App extends React.Component {
137
+ render() {
138
+ return (
139
+ <ContextProvider
140
+ value={{
141
+ ...context,
142
+ error: 'withFramePayBankComponent',
143
+ ready: true
144
+ }}
145
+ >
146
+ <TMPWrapper />
147
+ </ContextProvider>
148
+ );
149
+ }
150
+ }
151
+
152
+ const { container } = render(<App />);
153
+ expect(container.firstChild).toMatchInlineSnapshot(`
154
+ <div>
155
+ error:
156
+ withFramePayBankComponent
157
+ ready:
158
+ true
159
+ </div>
160
+ `);
161
+ });
162
+ });