From 2dc4ae57dc05b671cc8c1b428b4c70544ac5c267 Mon Sep 17 00:00:00 2001 From: Sean Perkins Date: Mon, 8 May 2023 23:09:50 -0400 Subject: [PATCH] test: test input masking --- core/src/components/input/input.tsx | 31 ++++++---- .../src/components/input/test/mask/index.html | 31 +++++++++- .../components/input/test/mask/input.e2e.ts | 44 ++++++++++++++ .../input/test/mask/mask-fixture.ts | 57 +++++++++++++++++++ 4 files changed, 152 insertions(+), 11 deletions(-) create mode 100644 core/src/components/input/test/mask/input.e2e.ts create mode 100644 core/src/components/input/test/mask/mask-fixture.ts diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 7b928a0d38..12d2b46839 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -374,6 +374,14 @@ export class Input implements ComponentInterface { this.emitStyle(); } + @Watch('mask') + protected maskChanged() { + if (this.maskController) { + this.maskController.destroy(); + } + this.initInputMask(); + } + componentWillLoad() { const { el } = this; @@ -400,18 +408,9 @@ export class Input implements ComponentInterface { } componentDidLoad() { - const { mask, nativeInput } = this; - this.originalIonInput = this.ionInput; - if (mask !== undefined && nativeInput) { - const formattedMask = formatMask(mask); - if (formattedMask) { - this.maskController = new MaskController(nativeInput, { - mask: formattedMask, - }); - } - } + this.initInputMask(); } disconnectedCallback() { @@ -484,6 +483,18 @@ export class Input implements ComponentInterface { return clearOnEdit === undefined ? type === 'password' : clearOnEdit; } + private initInputMask() { + const { mask, nativeInput } = this; + if (mask !== undefined && nativeInput) { + const formattedMask = formatMask(mask); + if (formattedMask) { + this.maskController = new MaskController(nativeInput, { + mask: formattedMask, + }); + } + } + } + private getValue(): string { return typeof this.value === 'number' ? this.value.toString() : (this.value || '').toString(); } diff --git a/core/src/components/input/test/mask/index.html b/core/src/components/input/test/mask/index.html index 7a8354dcae..47026ac93e 100644 --- a/core/src/components/input/test/mask/index.html +++ b/core/src/components/input/test/mask/index.html @@ -19,6 +19,7 @@ grid-row-gap: 20px; grid-column-gap: 20px; } + h2 { font-size: 12px; font-weight: normal; @@ -27,6 +28,7 @@ margin-top: 10px; } + @media screen and (max-width: 800px) { .grid { grid-template-columns: 1fr; @@ -40,7 +42,7 @@ - Input - Item + Input - Mask @@ -51,6 +53,17 @@ +
+ + + Dynamic Mask + + + + + + +
@@ -76,6 +89,22 @@ /\d/, /\d/, ]; + + inputPhoneUS.addEventListener('ionInput', (ev) => { + console.log('** ionInput event **', ev.detail); + }); + + inputPhoneUS.addEventListener('ionChange', (ev) => { + console.log('** ionChange event **', ev.detail); + }); + + const dynamicMaskInput = document.querySelector('#dynamicMaskInput'); + dynamicMaskInput.addEventListener('ionChange', (ev) => { + const mask = ev.detail.value; + const exampleInput = document.querySelector('#dynamicExample'); + console.log('setting the mask to: ', mask); + exampleInput.mask = mask; + }); diff --git a/core/src/components/input/test/mask/input.e2e.ts b/core/src/components/input/test/mask/input.e2e.ts new file mode 100644 index 0000000000..d0edbb3885 --- /dev/null +++ b/core/src/components/input/test/mask/input.e2e.ts @@ -0,0 +1,44 @@ +import { configs } from '@utils/test/playwright'; + +import { test } from './mask-fixture'; + +configs({ + modes: ['md'], + directions: ['ltr'], +}).forEach(({ title, config }) => { + test.describe(title('input: mask'), () => { + test('should mask the input', async ({ maskPage }) => { + // US Phone number + await maskPage.init(config, [ + '+', + '1', + ' ', + '(', + /\d/, + /\d/, + /\d/, + ')', + ' ', + /\d/, + /\d/, + /\d/, + '-', + /\d/, + /\d/, + /\d/, + /\d/, + ]); + + await maskPage.typeAndBlur('5555555555'); + await maskPage.expectValue('+1 (555) 555-5555'); + }); + + test('should mask the input with a string', async ({ maskPage }) => { + // Only allow lowercase letters + await maskPage.init(config, '[a-z]$'); + + await maskPage.typeAndBlur('5abc123d'); + await maskPage.expectValue('abcd'); + }); + }); +}); diff --git a/core/src/components/input/test/mask/mask-fixture.ts b/core/src/components/input/test/mask/mask-fixture.ts new file mode 100644 index 0000000000..f207db632f --- /dev/null +++ b/core/src/components/input/test/mask/mask-fixture.ts @@ -0,0 +1,57 @@ +import { expect } from '@playwright/test'; +import type { E2ELocator, E2EPage, TestConfig } from '@utils/test/playwright'; +import { test as base } from '@utils/test/playwright'; +import type { MaskExpression } from 'src/interface'; + +const stringifyMask = (mask: MaskExpression | string): string => { + if (typeof mask === 'string') { + return `'${mask}'`; + } + if (Array.isArray(mask)) { + return `[${mask.map(stringifyMask).join(', ')}]`; + } + return mask.toString(); +}; + +class MaskPage { + ionInput!: E2ELocator; + nativeInput!: E2ELocator; + + constructor(private page: E2EPage) {} + + async init(config: TestConfig, mask: MaskExpression | string) { + await this.page.setContent( + ` + + `, + config + ); + + this.ionInput = this.page.locator('ion-input'); + this.nativeInput = this.page.locator('ion-input input'); + } + + async typeAndBlur(value: string) { + await this.ionInput.click(); + + await this.ionInput.type(value, { delay: 50 }); + await this.ionInput.blur(); + } + + async expectValue(value: string) { + expect(await this.ionInput.evaluate((node: HTMLIonInputElement) => node.value)).toBe(value); + } +} + +type MaskFixtures = { + maskPage: MaskPage; +}; + +export const test = base.extend({ + maskPage: async ({ page }, use) => { + await use(new MaskPage(page)); + }, +});