Skip to content

Commit

Permalink
Merge pull request #2295 from Adyen/feat/apply_bento_styles
Browse files Browse the repository at this point in the history
feat: bento styles, add contextual text & remove placeholders
  • Loading branch information
longyulongyu authored Sep 27, 2023
2 parents 6fff003 + f6f48d5 commit 28ae874
Show file tree
Hide file tree
Showing 248 changed files with 3,305 additions and 2,920 deletions.
13 changes: 13 additions & 0 deletions .changeset/short-dolphins-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@adyen/adyen-web': major
---

Redesign with Bento design tokens.\
Remove default placeholders, merchants can configure placeholders via the configuration object.\
Add `showContextualElement` and `contextualText` for the form field, merchants can configure them via configuration object.\
New spinner.\
Phone prefix drop down contains flag icons.\
Ideal issuer names align to the right.\
Fix the stored card icon overlapping with the error icon.\
Scss code refactoring.\
Use the same syntax for 'required' error message in the Personal details and Address components.
66 changes: 63 additions & 3 deletions packages/e2e-playwright/models/card.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,77 @@
import { Locator, Page } from '@playwright/test';
import { USER_TYPE_DELAY } from '../tests/utils/constants';
import LANG from '../../lib/src/language/locales/en-US.json';

const CARD_IFRAME_TITLE = LANG['creditCard.encryptedCardNumber.aria.iframeTitle'];
const EXPIRY_DATE_IFRAME_TITLE = LANG['creditCard.encryptedExpiryDate.aria.iframeTitle'];
const CVC_IFRAME_TITLE = LANG['creditCard.encryptedSecurityCode.aria.iframeTitle'];

const CARD_IFRAME_LABEL = LANG['creditCard.cardNumber.label'];
const EXPIRY_DATE_IFRAME_LABEL = LANG['creditCard.expiryDate.label'];
const CVC_IFRAME_LABEL = LANG['creditCard.securityCode.label'];

class Card {
readonly rootElement: Locator;
readonly rootElementSelector: string;

readonly cardNumberField: Locator;
readonly cardNumberErrorElement: Locator;
readonly cardNumberInput: Locator;

readonly expiryDateField: Locator;
readonly expiryDateContextualElement: Locator;
readonly expiryDateInput: Locator;
readonly expiryDateIframeContextualElement: Locator;

readonly cvcField: Locator;
readonly cvcErrorElement: Locator;
readonly cvcContextualElement: Locator;
readonly cvcInput: Locator;
readonly cvcIframeContextualElement: Locator;

constructor(page: Page, rootElementSelector = '.adyen-checkout__card-input') {
this.rootElement = page.locator(rootElementSelector);
this.rootElementSelector = rootElementSelector;

this.cardNumberInput = this.rootElement.frameLocator('[title="Iframe for card number"]').locator('input[aria-label="Card number"]');
this.expiryDateInput = this.rootElement.frameLocator('[title="Iframe for expiry date"]').locator('input[aria-label="Expiry date"]');
this.cvcInput = this.rootElement.frameLocator('[title="Iframe for security code"]').locator('input[aria-label="Security code"]');
/**
* Card Number elements, in Checkout
*/
this.cardNumberField = this.rootElement.locator('.adyen-checkout__field--cardNumber'); // Holder

this.cardNumberErrorElement = this.cardNumberField.locator('.adyen-checkout-contextual-text--error');

/**
* Card Number elements, in iframe
*/
const cardNumberIframe = this.rootElement.frameLocator(`[title="${CARD_IFRAME_TITLE}"]`);
this.cardNumberInput = cardNumberIframe.locator(`input[aria-label="${CARD_IFRAME_LABEL}"]`);

/**
* Expiry Date elements, in Checkout
*/
this.expiryDateField = this.rootElement.locator('.adyen-checkout__field--expiryDate'); // Holder
this.expiryDateContextualElement = this.expiryDateField.locator('.adyen-checkout-contextual-text'); // Related contextual element

/**
* Expiry Date elements, in iframe
*/
const expiryDateIframe = this.rootElement.frameLocator(`[title="${EXPIRY_DATE_IFRAME_TITLE}"]`);
this.expiryDateInput = expiryDateIframe.locator(`input[aria-label="${EXPIRY_DATE_IFRAME_LABEL}"]`);
this.expiryDateIframeContextualElement = expiryDateIframe.locator('.aria-context');

/**
* Security code elements, in Checkout
*/
this.cvcField = this.rootElement.locator('.adyen-checkout__field--securityCode'); // Holder
this.cvcErrorElement = this.cvcField.locator('.adyen-checkout-contextual-text--error'); // Related erro element
this.cvcContextualElement = this.cvcField.locator('.adyen-checkout-contextual-text'); // Related contextual element

/**
* Security code elements, in iframe
*/
const cvcIframe = this.rootElement.frameLocator(`[title="${CVC_IFRAME_TITLE}"]`);
this.cvcInput = cvcIframe.locator(`input[aria-label="${CVC_IFRAME_LABEL}"]`);
this.cvcIframeContextualElement = cvcIframe.locator('.aria-context');
}

async isComponentVisible() {
Expand All @@ -28,6 +84,10 @@ class Card {
await this.cardNumberInput.type(cardNumber, { delay: USER_TYPE_DELAY });
}

async deleteCardNumber() {
await this.cardNumberInput.clear();
}

async typeExpiryDate(expiryDate: string) {
await this.expiryDateInput.type(expiryDate, { delay: USER_TYPE_DELAY });
}
Expand Down
11 changes: 11 additions & 0 deletions packages/e2e-playwright/pages/cards/card.fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { optionalDateAndCvcMock } from '../../mocks/binLookup/binLookup.data';
type Fixture = {
cardPage: CardPage;
cardAvsPage: CardAvsPage;
cardNoContextualElementPage: CardPage;
};

const test = base.extend<Fixture>({
Expand All @@ -26,6 +27,16 @@ const test = base.extend<Fixture>({
const cardAvsPage = new CardAvsPage(page);
await cardAvsPage.goto();
await use(cardAvsPage);
},

cardNoContextualElementPage: async ({ page }, use) => {
await page.addInitScript({
content: 'window.cardConfig = { showContextualElement: false}'
});

const cardPage = new CardPage(page);
await cardPage.goto();
await use(cardPage);
}
});

Expand Down
36 changes: 18 additions & 18 deletions packages/e2e-playwright/playwright-report/index.html

Large diffs are not rendered by default.

102 changes: 102 additions & 0 deletions packages/e2e-playwright/tests/card/card.contextualTexts.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { test, expect } from '../../pages/cards/card.fixture';
import { AMEX_CARD } from '../utils/constants';
import LANG from '../../../lib/src/language/locales/en-US.json';

const EXPIRY_DATE_CONTEXTUAL_TEXT = LANG['creditCard.expiryDate.contextualText'];
const CVC_CONTEXTUAL_TEXT_3_DIGITS = LANG['creditCard.securityCode.contextualText.3digits'];
const CVC_CONTEXTUAL_TEXT_4_DIGITS = LANG['creditCard.securityCode.contextualText.4digits'];
const CVC_ERROR = LANG['error.va.sf-cc-cvc.01'];

test('#1 Should inspect the card inputs and see they have contextual elements set', async ({ cardPage }) => {
const { card, page } = cardPage;

await card.isComponentVisible();

// checkout expiryDate element
await expect(card.expiryDateContextualElement).toHaveText(EXPIRY_DATE_CONTEXTUAL_TEXT);
const expiryDateAriaHidden = await card.expiryDateContextualElement.getAttribute('aria-hidden');
await expect(expiryDateAriaHidden).toEqual('true');

// iframe expiryDate element
await expect(card.expiryDateIframeContextualElement).toHaveText(EXPIRY_DATE_CONTEXTUAL_TEXT);

// checkout security code contextual element
await expect(card.cvcContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_3_DIGITS);
const cvcAriaHidden = await card.cvcContextualElement.getAttribute('aria-hidden');
await expect(cvcAriaHidden).toEqual('true');

// iframe security code element
await expect(card.cvcIframeContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_3_DIGITS);

// Type amex number and see the contextual element change in the CVC field
await card.typeCardNumber(AMEX_CARD);

await expect(card.cvcContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_4_DIGITS);
await expect(card.cvcIframeContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_4_DIGITS);

// Delete the card number and see the contextual element reset in the CVC field
await card.deleteCardNumber();

await expect(card.cvcContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_3_DIGITS);
await expect(card.cvcIframeContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_3_DIGITS);
});

test('#2 Should inspect the cvc input for a contextual text set, then it should be replaced by an error, then reset', async ({ cardPage }) => {
const { card, page } = cardPage;

await card.isComponentVisible();

// checkout security code contextual element
await expect(card.cvcContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_3_DIGITS);
let cvcAriaHidden = await card.cvcContextualElement.getAttribute('aria-hidden');
await expect(cvcAriaHidden).toEqual('true');

// error element hidden
await expect(card.cvcErrorElement).not.toBeVisible();

// iframe security code contextual element
await expect(card.cvcIframeContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_3_DIGITS);

// press pay to generate errors
await cardPage.pay();

// checkout security code error element
await expect(card.cvcErrorElement).toBeVisible();
await expect(card.cvcErrorElement).toHaveText(CVC_ERROR);
cvcAriaHidden = await card.cvcErrorElement.getAttribute('aria-hidden');
await expect(cvcAriaHidden).toEqual('true');

// contextual element being hidden
await expect(card.cvcContextualElement).not.toBeVisible();

// iframe contextual (error) element
await expect(card.cvcIframeContextualElement).toHaveText(CVC_ERROR);

// Allow default focusing after validation to happen
await page.waitForTimeout(1000);

// type
await card.typeCvc('737');

// reset
await expect(card.cvcContextualElement).toBeVisible();
await expect(card.cvcContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_3_DIGITS);
// error element hidden
await expect(card.cvcErrorElement).not.toBeVisible();

await expect(card.cvcIframeContextualElement).toHaveText(CVC_CONTEXTUAL_TEXT_3_DIGITS);
});

test('#3 Should find no contextualElements because the config says to not show them', async ({ cardNoContextualElementPage }) => {
const { card, page } = cardNoContextualElementPage;

await card.isComponentVisible();

// checkout contextual elements not present
await expect(card.expiryDateContextualElement).not.toBeVisible();
await expect(card.cvcContextualElement).not.toBeVisible();

// iframe contextual elements - present but without text
await expect(card.expiryDateIframeContextualElement).toHaveText('');
await expect(card.cvcIframeContextualElement).toHaveText('');
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ test('should select highlighted issuer and update pay button label', async ({ is
await expect(issuerList.submitButton).toHaveText('Continue to Test Issuer 4');

await expect(issuerList.highlightedIssuerButtonGroup.getByRole('button', { pressed: true })).toHaveText('Test Issuer 4');
await expect(issuerList.selectorCombobox).toHaveValue('Select your bank');
});

test('it should be able to filter and select using the keyboard', async ({ issuerListPage }) => {
Expand Down
1 change: 1 addition & 0 deletions packages/e2e-playwright/tests/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const BIN_LOOKUP_VERSION = 'v3';

export const REGULAR_TEST_CARD = '5500000000000004';
export const AMEX_CARD = '370000000000002';

export const TEST_DATE_VALUE = '03/30';
export const TEST_CVC_VALUE = '737';
Expand Down
2 changes: 1 addition & 1 deletion packages/e2e/tests/_models/Address.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default class AddressComponent {

this.postalCodeLabel = this.baseEl.find('.adyen-checkout__field--postalCode .adyen-checkout__label__text');
this.postalCodeInput = this.baseEl.find('.adyen-checkout__input--postalCode');
this.postalCodeInputError = this.baseEl.find('.adyen-checkout__field--postalCode .adyen-checkout__error-text');
this.postalCodeInputError = this.baseEl.find('.adyen-checkout__field--postalCode .adyen-checkout-contextual-text--error');
}

async fillPostalCode(value = '') {
Expand Down
14 changes: 8 additions & 6 deletions packages/e2e/tests/_models/CardComponent.page.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default class CardPage extends BasePage {
this.numSpan = Selector(`${BASE_EL} .adyen-checkout__card__cardNumber__input`);

// The <span> that holds the error text
this.numErrorText = Selector(`${BASE_EL} .adyen-checkout__field--cardNumber .adyen-checkout__error-text`);
this.numErrorText = Selector(`${BASE_EL} .adyen-checkout__field--cardNumber .adyen-checkout-contextual-text--error`);

// The <img> el that holds the card brand logo (actually a child of this.numSpan)
this.brandingIcon = Selector(`${BASE_EL} .adyen-checkout__card__cardNumber__brandIcon`);
Expand All @@ -60,7 +60,7 @@ export default class CardPage extends BasePage {
this.dateSpan = Selector(`${BASE_EL} .adyen-checkout__card__exp-date__input`);

// The <span> that holds the error text
this.dateErrorText = Selector(`${BASE_EL} .adyen-checkout__field__exp-date .adyen-checkout__error-text`);
this.dateErrorText = Selector(`${BASE_EL} .adyen-checkout__field__exp-date .adyen-checkout-contextual-text--error`);

this.storedCardExpiryDate = Selector(`${BASE_EL} .adyen-checkout__field--storedCard .adyen-checkout__card__exp-date__input--oneclick`);

Expand All @@ -81,7 +81,7 @@ export default class CardPage extends BasePage {
this.cvcSpan = Selector(`${BASE_EL} .adyen-checkout__card__cvc__input`);

// The <span> that holds the error text
this.cvcErrorText = Selector(`${BASE_EL} .adyen-checkout__field__cvc .adyen-checkout__error-text`);
this.cvcErrorText = Selector(`${BASE_EL} .adyen-checkout__field__cvc .adyen-checkout-contextual-text--error`);

/**
* Dual branding
Expand All @@ -96,18 +96,20 @@ export default class CardPage extends BasePage {
this.kcpTaxNumberLabelWithFocus = Selector(`${BASE_EL} .adyen-checkout__field--kcp-taxNumber .adyen-checkout__label--focused`);
this.kcpTaxNumberInput = Selector(`${BASE_EL} .adyen-checkout__field--kcp-taxNumber .adyen-checkout__card__kcp-taxNumber__input`);
this.pwdSpan = Selector(`${BASE_EL} [data-cse="encryptedPassword"]`);
this.pwdErrorText = Selector(`${BASE_EL} .adyen-checkout__field--koreanAuthentication-encryptedPassword .adyen-checkout__error-text`);
this.pwdErrorText = Selector(
`${BASE_EL} .adyen-checkout__field--koreanAuthentication-encryptedPassword .adyen-checkout-contextual-text--error`
);

/**
* AVS
*/
this.addressLabelWithFocus = Selector(`${BASE_EL} .adyen-checkout__field--street .adyen-checkout__label--focused`);
this.addressLabelErrorText = Selector(`${BASE_EL} .adyen-checkout__field--street .adyen-checkout__error-text`);
this.addressLabelErrorText = Selector(`${BASE_EL} .adyen-checkout__field--street .adyen-checkout-contextual-text--error`);
this.addressLabel = Selector(`${BASE_EL} .adyen-checkout__field--street .adyen-checkout__label`);
this.addressInput = Selector(`${BASE_EL} .adyen-checkout__field--street .adyen-checkout__input--street`);

this.postalCodeInput = Selector(`${BASE_EL} .adyen-checkout__field--postalCode .adyen-checkout__input--postalCode`);
this.postalCodeErrorText = Selector(`${BASE_EL} .adyen-checkout__field--postalCode .adyen-checkout__error-text`);
this.postalCodeErrorText = Selector(`${BASE_EL} .adyen-checkout__field--postalCode .adyen-checkout-contextual-text--error`);

this.houseNumberLabelWithFocus = Selector(`${BASE_EL} .adyen-checkout__field--houseNumberOrName .adyen-checkout__label--focused`);

Expand Down
2 changes: 0 additions & 2 deletions packages/e2e/tests/address/address.postalCode.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ test('should show error when switching from country that has valid postal code t
await addressComponent.selectCountry('United States');
await addressComponent.fillPostalCode('12345');

await t.expect(addressComponent.postalCodeInputError.exists).ok(); // error fields should always be in DOM

await addressComponent.selectCountry('Brazil');
await t.expect(addressComponent.postalCodeInputError.innerText).contains('Invalid format. Expected format: 12345678 or 12345-678');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ test('#3 Enter card number, that we mock to co-branded bcmc/visa ' + 'then compl
});

test(
'#4 Enter card number, that we mock to co-branded bcmc/visa ' +
'#4 Enter card number (co-branded bcmc/visa) ' +
'then complete expiryDate and expect comp to be valid' +
'then click Visa logo and expect comp to not be valid' +
'then click BCMC logo and expect comp to be valid again',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const logger = RequestLogger(
}
);

const errorLabel = Selector('.card-field .adyen-checkout__error-text');
const errorLabel = Selector('.card-field .adyen-checkout-contextual-text--error');

const UNSUPPORTED_CARD = LANG['error.va.sf-cc-num.03'];

Expand All @@ -29,10 +29,7 @@ const iframeSelector = getIframeSelector('.card-field iframe');

const cardUtils = cu(iframeSelector);

fixture`Testing binLookup v2 response`
.page(CARDS_URL)
.clientScripts('binLookup.clientScripts.js')
.requestHooks(logger);
fixture`Testing binLookup v2 response`.page(CARDS_URL).clientScripts('binLookup.clientScripts.js').requestHooks(logger);

test('#1 Enter number of known dual branded card, ' + 'then inspect response body for expected properties ', async t => {
// Start, allow time for iframes to load
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ test('#1 Enter number of unsupported card, ' + 'then check UI shows an error ' +
await cardPage.cardUtils.fillCardNumber(t, REGULAR_TEST_CARD, 'paste');

// Test UI shows "Unsupported card" error has gone
await t.expect(cardPage.numErrorText.withExactText('').exists).ok();
await t.expect(cardPage.numErrorText.exists).notOk();
});

test(
Expand Down Expand Up @@ -77,7 +77,7 @@ test(
await cardPage.cardUtils.fillCardNumber(t, REGULAR_TEST_CARD, 'paste');

// Test UI shows "Unsupported card" error has gone
await t.expect(cardPage.numErrorText.withExactText('').exists).ok();
await t.expect(cardPage.numErrorText.exists).notOk();

// PAN error cleared but other errors persist
await t
Expand Down Expand Up @@ -109,7 +109,7 @@ test('#3 Enter number of unsupported card, ' + 'then check UI shows an error ' +
await cardPage.cardUtils.fillCardNumber(t, UNKNOWN_VISA_CARD, 'paste');

// Test UI shows "Unsupported card" error has gone
await t.expect(cardPage.numErrorText.withExactText('').exists).ok();
await t.expect(cardPage.numErrorText.exists).notOk();
});

test('#4 Enter number of unsupported card, ' + 'then check UI shows an error ' + 'then delete PAN & check UI error is cleared', async t => {
Expand All @@ -131,5 +131,5 @@ test('#4 Enter number of unsupported card, ' + 'then check UI shows an error ' +
await cardPage.cardUtils.deleteCardNumber(t);

// Test UI shows "Unsupported card" error has gone
await t.expect(cardPage.numErrorText.withExactText('').exists).ok();
await t.expect(cardPage.numErrorText.exists).notOk();
});
Loading

0 comments on commit 28ae874

Please sign in to comment.