Skip to content

Commit

Permalink
Cherry-pick fix stored card holder name #2800 c580d1e (#2813)
Browse files Browse the repository at this point in the history
  • Loading branch information
m1aw authored Aug 26, 2024
1 parent a60e655 commit dd4a613
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/healthy-drinks-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@adyen/adyen-web": patch
---

Fixes `props.holderName` not being used in stored cards as `paymetMethod.holderName`.
1 change: 1 addition & 0 deletions packages/lib/config/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ import './testMocks/core.mock';
import './testMocks/analyticsMock';
// eslint-disable-next-line import/no-extraneous-dependencies
import 'whatwg-fetch';
import './testMocks/srPanelMock';

configure({ adapter: new Adapter() });
11 changes: 11 additions & 0 deletions packages/lib/config/testMocks/srPanelMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { mock } from 'jest-mock-extended';
import { SRPanel } from '../../src/core/Errors/SRPanel';

function setupSRPanelMock() {
const srPanel = mock<SRPanel>({
moveFocus: true
});
return srPanel;
}

global.srPanel = setupSRPanelMock();
57 changes: 56 additions & 1 deletion packages/lib/src/components/Card/Card.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { CardElement } from './Card';
import { render, screen } from '@testing-library/preact';
import { render, screen, waitFor } from '@testing-library/preact';
import { CoreProvider } from '../../core/Context/CoreProvider';

describe('Card', () => {
Expand Down Expand Up @@ -157,6 +157,61 @@ describe('Card', () => {
});
});

describe('formatData', () => {
const i18n = global.i18n;
const resources = global.resources;
const srPanel = global.srPanel;

const props = { loadingContext: 'test', i18n, modules: { resources, srPanel } };
const storedCardProps = { supportedShopperInteractions: ['Ecommerce'], storedPaymentMethodId: 'xxx' };

test('should echo back holderName if is a stored card', () => {
const card = new CardElement(global.core, { ...props, ...storedCardProps, holderName: 'Test Holder' });
render(card.render());

expect(card.formatData().paymentMethod.holderName).toContain('Test Holder');
});

test('should NOT echo back holderName from data if is a stored card', () => {
const card = new CardElement(global.core, { ...props, ...storedCardProps, data: { holderName: 'Test Holder' } });
render(card.render());

expect(card.formatData().paymentMethod.holderName).toContain('');
});

test('if no holderName specificed and is stored card, holder name should be empty string', () => {
const card = new CardElement(global.core, { ...props, ...storedCardProps });

expect(card.formatData().paymentMethod.holderName).toContain('');
});

test('if no holderName specificed and is not stored card, holder name should be empty string', () => {
const card = new CardElement(global.core, { ...props, ...storedCardProps });

expect(card.formatData().paymentMethod.holderName).toContain('');
});

test('should NOT echo back holderName if is not a stored card', () => {
const card = new CardElement(global.core, { ...props, holderName: 'Test Holder' });
render(card.render());
expect(card.formatData().paymentMethod.holderName).toContain('');
});

test('should set holderName if passed via data', async () => {
const card = new CardElement(global.core, { ...props, hasHolderName: true, data: { holderName: 'Test Holder' } });
render(card.render());
// we need to wait here for the screen to render / hook to trigger
await waitFor(() => expect(card.formatData().paymentMethod.holderName).toContain('Test Holder'));
});

test('should have empty holderName by default', async () => {
const card = new CardElement(global.core, { ...props });
render(card.render());
// we need to wait here for the screen to render / hook to trigger
await waitFor(() => expect(card.formatData().paymentMethod.holderName).toContain(''));
});
});

describe('isValid', () => {
test('returns false if there is no state', () => {
const card = new CardElement(global.core);
Expand Down
5 changes: 4 additions & 1 deletion packages/lib/src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,10 @@ export class CardElement extends UIElement<CardConfiguration> {
paymentMethod: {
type: CardElement.type,
...this.state.data,
...(this.props.storedPaymentMethodId && { storedPaymentMethodId: this.props.storedPaymentMethodId }),
...(this.props.storedPaymentMethodId && {
storedPaymentMethodId: this.props.storedPaymentMethodId,
holderName: this.props.holderName ?? ''
}),
...(cardBrand && { brand: cardBrand }),
...(this.props.fundingSource && { fundingSource: this.props.fundingSource })
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export interface CardInputProps {
fundingSource?: 'debit' | 'credit';
hasCVC?: boolean;
hasHolderName?: boolean;
holderName?: string;
holderNameRequired?: boolean;
i18n?: Language;
implementationType?: string;
Expand Down
6 changes: 6 additions & 0 deletions packages/lib/src/components/Card/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ export interface CardConfiguration extends UIElementProps {
*/
hasHolderName?: boolean;

/**
* holderName coming from a stored card in /paymentMethods response
* @internal
*/
holderName?: string;

/**
* Show/hide the Security Code field
* - merchant set config option
Expand Down

0 comments on commit dd4a613

Please sign in to comment.