From 0bf5c93064885ed394f526d14ad23c3f8beb9e8e Mon Sep 17 00:00:00 2001 From: Andrii Kamaldinov <129040945+andriikamaldinov1@users.noreply.github.com> Date: Tue, 5 Nov 2024 18:25:59 +0200 Subject: [PATCH] fix(ref: no-ref): fix issues (#1447) * fix(ref: no-ref): fix issues * fix(ref: no-ref): fix issues * fix(ref: no-ref): fix issues * fix(ref: no-ref): fix issues * fix(ref: no-ref): fix issues * fix(ref: no-ref): fix issues * fix(ref: no-ref): fix issues * fix(ref: no-ref): fix issues * fix(ref: no-ref): fix issues * fix(ref: no-ref): fix issues --- .github/workflows/publish.yml | 4 +- .github/workflows/quality-check.yml | 2 +- CHANGELOG.md | 10 + package.json | 2 +- projects/ngx-mask-lib/package.json | 2 +- .../src/lib/ngx-mask-expression.enum.ts | 1 + .../src/lib/ngx-mask.directive.ts | 18 +- .../ngx-mask-lib/src/lib/ngx-mask.service.ts | 12 +- .../ngx-mask-lib/src/test/basic-logic.spec.ts | 52 ++ .../ngx-mask-lib/src/test/cursor.cy-spec.ts | 193 -------- .../ngx-mask-lib/src/test/delete.cy-spec.ts | 263 ---------- .../test/keep-character-position.cy-spec.ts | 43 ++ .../src/test/separator.cy-spec.ts | 460 ++++++++++++++++++ .../ngx-mask-lib/src/test/separator.spec.ts | 38 ++ .../ngx-mask-lib/src/test/validation.spec.ts | 59 +++ src/assets/content/common-cases.ts | 17 + 16 files changed, 712 insertions(+), 464 deletions(-) create mode 100644 projects/ngx-mask-lib/src/test/separator.cy-spec.ts diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0add2295..6a223369 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,6 +8,8 @@ on: jobs: build: runs-on: ubuntu-latest + outputs: + version: ${{ steps.get_version.outputs.version }} steps: - uses: actions/checkout@v4 with: @@ -34,7 +36,7 @@ jobs: id: get_version run: | VERSION=$(node -p "require('./dist/ngx-mask-lib/package.json').version") - echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT slack_notification: needs: diff --git a/.github/workflows/quality-check.yml b/.github/workflows/quality-check.yml index 97b5d7bc..f423ddfe 100644 --- a/.github/workflows/quality-check.yml +++ b/.github/workflows/quality-check.yml @@ -21,4 +21,4 @@ jobs: - name: Check quality run: | bun i - bash .github/workflows/scripts/quality.sh \ No newline at end of file + bash .github/workflows/scripts/quality.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 6667c655..0a18fc93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 18.0.3(2024-11-05) + +### Fix + +- Fix ([#1372](https://github.com/JsDaddy/ngx-mask/issues/1372)) +- Fix ([#1441](https://github.com/JsDaddy/ngx-mask/issues/1441)) +- Fix ([#1442](https://github.com/JsDaddy/ngx-mask/issues/1442)) +- Fix ([#1440](https://github.com/JsDaddy/ngx-mask/issues/1440)) +- Fix ([#1409](https://github.com/JsDaddy/ngx-mask/issues/1409)) + # 18.0.2(2024-11-01) ### Fix diff --git a/package.json b/package.json index 6b9529d7..8a20bd64 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ngx-mask", - "version": "18.0.2", + "version": "18.0.3", "description": "Awesome ngx mask", "license": "MIT", "engines": { diff --git a/projects/ngx-mask-lib/package.json b/projects/ngx-mask-lib/package.json index a018296e..fcdab315 100644 --- a/projects/ngx-mask-lib/package.json +++ b/projects/ngx-mask-lib/package.json @@ -1,6 +1,6 @@ { "name": "ngx-mask", - "version": "18.0.2", + "version": "18.0.3", "description": "awesome ngx mask", "keywords": [ "ng2-mask", diff --git a/projects/ngx-mask-lib/src/lib/ngx-mask-expression.enum.ts b/projects/ngx-mask-lib/src/lib/ngx-mask-expression.enum.ts index c9c2916b..3aadf5ab 100644 --- a/projects/ngx-mask-lib/src/lib/ngx-mask-expression.enum.ts +++ b/projects/ngx-mask-lib/src/lib/ngx-mask-expression.enum.ts @@ -12,6 +12,7 @@ export const enum MaskExpression { HOURS_HOUR = 'Hh', SECONDS = 's0', HOURS_MINUTES_SECONDS = 'Hh:m0:s0', + EMAIL_MASK = 'A*@A*.A*', HOURS_MINUTES = 'Hh:m0', MINUTES_SECONDS = 'm0:s0', DAYS_MONTHS_YEARS = 'd0/M0/0000', diff --git a/projects/ngx-mask-lib/src/lib/ngx-mask.directive.ts b/projects/ngx-mask-lib/src/lib/ngx-mask.directive.ts index 4c8b77ac..4ab0e2a0 100644 --- a/projects/ngx-mask-lib/src/lib/ngx-mask.directive.ts +++ b/projects/ngx-mask-lib/src/lib/ngx-mask.directive.ts @@ -280,7 +280,15 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida if (timeMasks.includes(this._maskValue)) { return this._validateTime(processedValue); } + if (this._maskValue === MaskExpression.EMAIL_MASK) { + const emailPattern = /^[^@]+@[^@]+\.[^@]+$/; + if (!emailPattern.test(processedValue) && processedValue) { + return this._createValidationError(processedValue); + } else { + return null; + } + } if (processedValue && processedValue.length >= 1) { let counterOfOpt = 0; @@ -461,7 +469,11 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida const selStart = Number(this._maskService.selStart) - prefixLength; const selEnd = Number(this._maskService.selEnd) - prefixLength; - if (this._code === MaskExpression.BACKSPACE) { + const backspaceOrDelete = + this._code === MaskExpression.BACKSPACE || + this._code === MaskExpression.DELETE; + + if (backspaceOrDelete) { if (!selectRangeBackspace) { if (this._maskService.selStart === prefixLength) { this._maskService.actualValue = `${this.prefix}${this._maskService.maskIsShown.slice(0, selEnd)}${this._inputValue.split(this.prefix).join('')}`; @@ -505,8 +517,9 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida this._maskService.actualValue = `${part1}${this._maskService.placeHolderCharacter}${part2}`; } } + position = this._code === MaskExpression.DELETE ? position + 1 : position; } - if (this._code !== MaskExpression.BACKSPACE) { + if (!backspaceOrDelete) { if (!checkSymbols && !checkSpecialCharacter && selectRangeBackspace) { position = Number(el.selectionStart) - 1; } else if ( @@ -996,6 +1009,7 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida if (typeof this.inputTransformFn !== 'function') { this._maskService.writingValue = true; } + this._maskService.formElementProperty = [ 'value', this._maskService.applyMask(inputValue, this._maskService.maskExpression), diff --git a/projects/ngx-mask-lib/src/lib/ngx-mask.service.ts b/projects/ngx-mask-lib/src/lib/ngx-mask.service.ts index 777e43bb..1de853eb 100644 --- a/projects/ngx-mask-lib/src/lib/ngx-mask.service.ts +++ b/projects/ngx-mask-lib/src/lib/ngx-mask.service.ts @@ -206,6 +206,7 @@ export class NgxMaskService extends NgxMaskApplierService { this._emitValue = this._previousValue !== this._currentValue || this.maskChanged || + this.writingValue || (this._previousValue === this._currentValue && justPasted); } @@ -215,6 +216,7 @@ export class NgxMaskService extends NgxMaskApplierService { ? requestAnimationFrame(() => this.formControlResult(result)) : this.formControlResult(result) : ''; + if (!this.showMaskTyped || (this.showMaskTyped && this.hiddenInput)) { if (this.hiddenInput) { if (backspaced) { @@ -530,6 +532,10 @@ export class NgxMaskService extends NgxMaskApplierService { * @param inputValue the current form input value */ private formControlResult(inputValue: string): void { + if (this.writingValue && !inputValue) { + this.onChange(''); + return; + } if (this.writingValue || (!this.triggerOnMaskChange && this.maskChanged)) { // eslint-disable-next-line no-unused-expressions,@typescript-eslint/no-unused-expressions this.triggerOnMaskChange && this.maskChanged @@ -583,9 +589,11 @@ export class NgxMaskService extends NgxMaskApplierService { ) { return value; } - if (String(value).length > 16 && this.separatorLimit.length > 14) { + + if (String(value).length > 14 && this.maskExpression.startsWith(MaskExpression.SEPARATOR)) { return String(value); } + const num = Number(value); if (this.maskExpression.startsWith(MaskExpression.SEPARATOR) && Number.isNaN(num)) { const val = String(value).replace(',', '.'); @@ -686,7 +694,7 @@ export class NgxMaskService extends NgxMaskApplierService { if (processedResult === this.decimalMarker) { return null; } - if (this.separatorLimit.length > 14) { + if (separatorValue.length > 14) { return String(separatorValue); } return this._checkPrecision(this.maskExpression, separatorValue); diff --git a/projects/ngx-mask-lib/src/test/basic-logic.spec.ts b/projects/ngx-mask-lib/src/test/basic-logic.spec.ts index b072e0af..24cc97f6 100644 --- a/projects/ngx-mask-lib/src/test/basic-logic.spec.ts +++ b/projects/ngx-mask-lib/src/test/basic-logic.spec.ts @@ -969,4 +969,56 @@ describe('Directive: Mask', () => { expect(component.form.dirty).toBe(false); }); + + it('mask sepator.2 after setValue should be dont dirty', () => { + component.mask = 'separator.0'; + component.form.setValue('2002'); + + expect(component.form.dirty).toBe(false); + }); + + it('should return empty string in formControl mask SSS-SSS-SSS', () => { + component.mask = 'SSS-SSS-SSS'; + component.form.setValue('978-1-93624-386-0'); + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + expect(inputTarget.value).toBe(''); + }); + + it('should return empty string in formControl mask AAA-AAA-AAA', () => { + component.mask = 'AAA-AAA-AAA'; + component.form.setValue('978-123-936'); + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + expect(inputTarget.value).toBe(''); + }); + + it('should return empty string in formControl mask (000) 000-000', () => { + component.mask = '(000) 000-000'; + component.form.setValue('978-123-936'); + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + expect(inputTarget.value).toBe(''); + }); + + it('should return empty string in formControl mask (000) 000-000 with prefix +7', () => { + component.mask = '(000) 000-000'; + component.prefix = '+7 '; + component.form.setValue('978-123-936'); + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + expect(inputTarget.value).toBe(''); + }); }); diff --git a/projects/ngx-mask-lib/src/test/cursor.cy-spec.ts b/projects/ngx-mask-lib/src/test/cursor.cy-spec.ts index e433c013..a83b43b1 100644 --- a/projects/ngx-mask-lib/src/test/cursor.cy-spec.ts +++ b/projects/ngx-mask-lib/src/test/cursor.cy-spec.ts @@ -176,108 +176,6 @@ describe('Test Date Hh:m0', () => { .clear(); }); - it('Mask separator.2 check cursor with value 100.0', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - decimalMarker: '.', - thousandSeparator: ',', - }, - imports: [CypressTestMaskModule], - }); - cy.get('#masked') - .type('1000') - .type('{leftArrow}') - .type('.') - .should('have.value', '100.0') - .should('have.prop', 'selectionStart', 4); - }); - - it('Mask separator.2 check cursor with value 1.00', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - decimalMarker: '.', - thousandSeparator: ',', - }, - imports: [CypressTestMaskModule], - }); - cy.get('#masked') - .type('1000') - .type('{leftArrow}'.repeat(3)) - .type('.') - .should('have.value', '1.00') - .should('have.prop', 'selectionStart', 2); - }); - - it('Mask separator.2 check cursor with value 123456789.20', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - decimalMarker: '.', - thousandSeparator: ',', - }, - imports: [CypressTestMaskModule], - }); - cy.get('#masked') - .type('123456789.20') - .type('{leftArrow}'.repeat(4)) - .type('.') - .should('have.value', '12,345,678.9') - .should('have.prop', 'selectionStart', 11); - }); - - it('Mask separator.2 check cursor with value 100.0', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - decimalMarker: ',', - thousandSeparator: '.', - }, - imports: [CypressTestMaskModule], - }); - cy.get('#masked') - .type('1000') - .type('{leftArrow}') - .type(',') - .should('have.value', '100,0') - .should('have.prop', 'selectionStart', 4); - }); - - it('Mask separator.2 check cursor with value 1.00', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - decimalMarker: ',', - thousandSeparator: '.', - }, - imports: [CypressTestMaskModule], - }); - cy.get('#masked') - .type('1000') - .type('{leftArrow}'.repeat(3)) - .type(',') - .should('have.value', '1,00') - .should('have.prop', 'selectionStart', 2); - }); - - it('Mask separator.2 check cursor with value 123456789.20', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - decimalMarker: ',', - thousandSeparator: '.', - }, - imports: [CypressTestMaskModule], - }); - cy.get('#masked') - .type('123456789,20') - .type('{leftArrow}'.repeat(4)) - .type(',') - .should('have.value', '12.345.678,9') - .should('have.prop', 'selectionStart', 11); - }); - it('Mask d0/M0/0000 should set cursor on right position', () => { cy.mount(CypressTestMaskComponent, { componentProperties: { @@ -381,95 +279,4 @@ describe('Test Date Hh:m0', () => { cy.get('#masked').type('111').should('have.value', '(11) 1'); cy.get('#masked').type('{backspace}').should('have.prop', 'selectionStart', 4); }); - - it('when decimalMarker doenst set should have right position cursor thousandSeparator = .', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - thousandSeparator: '.', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('12345678,00') - - .should('have.value', '12.345.678,00') - .type('{leftArrow}'.repeat(3)) - .type('{backspace}'.repeat(3)) - .should('have.value', '12.345,00'); - }); - - it('when decimalMarker doenst set should have right position cursor thousandSeparator = ,', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - thousandSeparator: ',', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('12345678.00') - - .should('have.value', '12,345,678.00') - .type('{leftArrow}'.repeat(3)) - .type('{backspace}'.repeat(3)) - .should('have.value', '12,345.00'); - }); - - it('should place cursor after backspace with separatorLimit = 10 in correct position', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - separatorLimit: '10', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('12.10') - .should('have.value', '12.10') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '12') - .should('have.prop', 'selectionStart', 2); - }); - - it('should place cursor after backspace with separatorLimit = 100 in correct position', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - separatorLimit: '100', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('123.10') - .should('have.value', '123.10') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '123') - .should('have.prop', 'selectionStart', 3); - }); - - it('should place cursor after backspace with separatorLimit = 1000 in correct position', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - thousandSeparator: ',', - separatorLimit: '1000', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('1234.10') - .should('have.value', '1,234.10') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '1,234') - .should('have.prop', 'selectionStart', 5); - }); }); diff --git a/projects/ngx-mask-lib/src/test/delete.cy-spec.ts b/projects/ngx-mask-lib/src/test/delete.cy-spec.ts index c711933f..2b3a12b1 100644 --- a/projects/ngx-mask-lib/src/test/delete.cy-spec.ts +++ b/projects/ngx-mask-lib/src/test/delete.cy-spec.ts @@ -289,42 +289,6 @@ describe('Directive: Mask (Delete)', () => { .should('have.value', '+32 __ ___ __ __'); }); - it('should backspace with separator and prefix', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - thousandSeparator: ',', - prefix: '$ ', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('1234567890') - .should('have.value', '$ 1,234,567,890') - .type('{leftArrow}'.repeat(3)) - .type('{backspace}') - .should('have.prop', 'selectionStart', 11); - }); - - it('should backspace with separator and prefix', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - thousandSeparator: '.', - prefix: '$ ', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('1234567890') - .should('have.value', '$ 1.234.567.890') - .type('{leftArrow}'.repeat(3)) - .type('{backspace}') - .should('have.prop', 'selectionStart', 11); - }); - it('should backspace with showMaskTyped and leadZeroDateTime', () => { cy.mount(CypressTestMaskComponent, { componentProperties: { @@ -376,233 +340,6 @@ describe('Directive: Mask (Delete)', () => { .should('have.value', '__:__:____'); }); - it('should correct work after backspace separator.6 decimalMarker . thousandSeparator ,', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.6', - decimalMarker: '.', - thousandSeparator: ',', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('0.000001') - .should('have.value', '0.000001') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '0.00001') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '0.0001') - .type('{backspace}') - .should('have.value', '1'); - }); - - it('should correct work after backspace separator.2 decimalMarker . thousandSeparator ,', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - decimalMarker: '.', - thousandSeparator: ',', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('0.01') - .should('have.value', '0.01') - .type('{leftArrow}') - .type('{backspace}') - .should('have.value', '0.1') - .type('{leftArrow}') - .type('{backspace}') - .should('have.value', '1'); - }); - - it('should correct work after backspace separator.2 decimalMarker . thousandSeparator , allowNegative', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - decimalMarker: '.', - thousandSeparator: ',', - allowNegativeNumbers: true, - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('-0.01') - .should('have.value', '-0.01') - .type('{leftArrow}') - .type('{backspace}') - .should('have.value', '-0.1') - .type('{leftArrow}') - .type('{backspace}') - .should('have.value', '-1'); - }); - - it('should correct work after backspace separator.3 decimalMarker . thousandSeparator , allowNegative', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.3', - decimalMarker: '.', - thousandSeparator: ',', - allowNegativeNumbers: true, - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('-0.014') - .should('have.value', '-0.014') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '-0.14') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '-14'); - }); - - it('should correct work after backspace separator.3 leadZero allowNegative', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.3', - allowNegativeNumbers: true, - leadZero: true, - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('-0.1') - .should('have.value', '-0.1') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '-1') - .type('{backspace}') - .should('have.value', '-'); - }); - - it('should correct work after backspace separator', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('0.33') - .should('have.value', '0.33') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '33'); - }); - - it('should correct work after backspace separator leadZero', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator', - leadZero: true, - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('0.33') - .should('have.value', '0.33') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '33'); - }); - - it('should correct work after backspace separator allowNegativeNumbers', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator', - allowNegativeNumbers: true, - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('-0.33') - .should('have.value', '-0.33') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '-33'); - }); - - it('should correct work after backspace separator leadZero', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator', - leadZero: true, - allowNegativeNumbers: true, - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('-0.33') - .should('have.value', '-0.33') - .type('{leftArrow}'.repeat(2)) - .type('{backspace}') - .should('have.value', '-33'); - }); - - it('should correct work after backspace separator.2 when first digit .', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - thousandSeparator: '.', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('50004') - .should('have.value', '50.004') - .type('{leftArrow}'.repeat(5)) - .type('{backspace}') - .should('have.value', '4'); - }); - - it('should correct work after backspace separator.2 when first digit ,', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - thousandSeparator: ',', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('50004') - .should('have.value', '50,004') - .type('{leftArrow}'.repeat(5)) - .type('{backspace}') - .should('have.value', '4'); - }); - - it('should correct work after backspace separator.2 when first digit whitespace', () => { - cy.mount(CypressTestMaskComponent, { - componentProperties: { - mask: 'separator.2', - thousandSeparator: ' ', - }, - imports: [CypressTestMaskModule], - }); - - cy.get('#masked') - .type('50004') - .should('have.value', '50 004') - .type('{leftArrow}'.repeat(5)) - .type('{backspace}') - .should('have.value', '4'); - }); - it('should backspace with mask Hh:m0', () => { cy.mount(CypressTestMaskComponent, { componentProperties: { diff --git a/projects/ngx-mask-lib/src/test/keep-character-position.cy-spec.ts b/projects/ngx-mask-lib/src/test/keep-character-position.cy-spec.ts index 00448085..bfba7384 100644 --- a/projects/ngx-mask-lib/src/test/keep-character-position.cy-spec.ts +++ b/projects/ngx-mask-lib/src/test/keep-character-position.cy-spec.ts @@ -207,4 +207,47 @@ describe('Directive: Mask (Delete)', () => { .type('{backspace}'.repeat(2)) .should('have.value', '12/__/567 test'); }); + + it('should delete character from del', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: '000-000-000', + keepCharacterPositions: true, + showMaskTyped: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('123456789') + .type('{leftArrow}'.repeat(11)) + .type('{del}'.repeat(11)) + .should('have.value', '___-___-___'); + + cy.get('#masked').clear(); + cy.get('#masked') + .type('123456789') + .type('{leftArrow}'.repeat(4)) + .type('{del}') + .should('have.value', '123-456-789') + .should('have.prop', 'selectionStart', 8); + }); + + it('should delete character from del', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: '0000 0000 0000 0000', + keepCharacterPositions: true, + showMaskTyped: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('1234567891011121') + .type('{leftArrow}'.repeat(5)) + .type('{del}') + .should('have.value', '1234 5678 9101 1121') + .should('have.prop', 'selectionStart', 15); + }); }); diff --git a/projects/ngx-mask-lib/src/test/separator.cy-spec.ts b/projects/ngx-mask-lib/src/test/separator.cy-spec.ts new file mode 100644 index 00000000..d7176ed8 --- /dev/null +++ b/projects/ngx-mask-lib/src/test/separator.cy-spec.ts @@ -0,0 +1,460 @@ +import { CypressTestMaskModule } from './utils/cypress-test.module'; +import { CypressTestMaskComponent } from './utils/cypress-test-component.component'; + +describe('Test Date Hh:m0', () => { + it('Mask separator.2 check cursor with value 100.0', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + decimalMarker: '.', + thousandSeparator: ',', + }, + imports: [CypressTestMaskModule], + }); + cy.get('#masked') + .type('1000') + .type('{leftArrow}') + .type('.') + .should('have.value', '100.0') + .should('have.prop', 'selectionStart', 4); + }); + + it('Mask separator.2 check cursor with value 1.00', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + decimalMarker: '.', + thousandSeparator: ',', + }, + imports: [CypressTestMaskModule], + }); + cy.get('#masked') + .type('1000') + .type('{leftArrow}'.repeat(3)) + .type('.') + .should('have.value', '1.00') + .should('have.prop', 'selectionStart', 2); + }); + + it('Mask separator.2 check cursor with value 123456789.20', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + decimalMarker: '.', + thousandSeparator: ',', + }, + imports: [CypressTestMaskModule], + }); + cy.get('#masked') + .type('123456789.20') + .type('{leftArrow}'.repeat(4)) + .type('.') + .should('have.value', '12,345,678.9') + .should('have.prop', 'selectionStart', 11); + }); + + it('Mask separator.2 check cursor with value 100.0', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + decimalMarker: ',', + thousandSeparator: '.', + }, + imports: [CypressTestMaskModule], + }); + cy.get('#masked') + .type('1000') + .type('{leftArrow}') + .type(',') + .should('have.value', '100,0') + .should('have.prop', 'selectionStart', 4); + }); + + it('Mask separator.2 check cursor with value 1.00', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + decimalMarker: ',', + thousandSeparator: '.', + }, + imports: [CypressTestMaskModule], + }); + cy.get('#masked') + .type('1000') + .type('{leftArrow}'.repeat(3)) + .type(',') + .should('have.value', '1,00') + .should('have.prop', 'selectionStart', 2); + }); + + it('Mask separator.2 check cursor with value 123456789.20', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + decimalMarker: ',', + thousandSeparator: '.', + }, + imports: [CypressTestMaskModule], + }); + cy.get('#masked') + .type('123456789,20') + .type('{leftArrow}'.repeat(4)) + .type(',') + .should('have.value', '12.345.678,9') + .should('have.prop', 'selectionStart', 11); + }); + + it('when decimalMarker doenst set should have right position cursor thousandSeparator = .', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + thousandSeparator: '.', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('12345678,00') + + .should('have.value', '12.345.678,00') + .type('{leftArrow}'.repeat(3)) + .type('{backspace}'.repeat(3)) + .should('have.value', '12.345,00'); + }); + + it('when decimalMarker doenst set should have right position cursor thousandSeparator = ,', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + thousandSeparator: ',', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('12345678.00') + + .should('have.value', '12,345,678.00') + .type('{leftArrow}'.repeat(3)) + .type('{backspace}'.repeat(3)) + .should('have.value', '12,345.00'); + }); + + it('should place cursor after backspace with separatorLimit = 10 in correct position', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + separatorLimit: '10', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('12.10') + .should('have.value', '12.10') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '12') + .should('have.prop', 'selectionStart', 2); + }); + + it('should place cursor after backspace with separatorLimit = 100 in correct position', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + separatorLimit: '100', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('123.10') + .should('have.value', '123.10') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '123') + .should('have.prop', 'selectionStart', 3); + }); + + it('should place cursor after backspace with separatorLimit = 1000 in correct position', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + thousandSeparator: ',', + separatorLimit: '1000', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('1234.10') + .should('have.value', '1,234.10') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '1,234') + .should('have.prop', 'selectionStart', 5); + }); + + it('should backspace with separator and prefix', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + thousandSeparator: ',', + prefix: '$ ', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('1234567890') + .should('have.value', '$ 1,234,567,890') + .type('{leftArrow}'.repeat(3)) + .type('{backspace}') + .should('have.prop', 'selectionStart', 11); + }); + + it('should backspace with separator and prefix', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + thousandSeparator: '.', + prefix: '$ ', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('1234567890') + .should('have.value', '$ 1.234.567.890') + .type('{leftArrow}'.repeat(3)) + .type('{backspace}') + .should('have.prop', 'selectionStart', 11); + }); + + it('should correct work after backspace separator.6 decimalMarker . thousandSeparator ,', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.6', + decimalMarker: '.', + thousandSeparator: ',', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('0.000001') + .should('have.value', '0.000001') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '0.00001') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '0.0001') + .type('{backspace}') + .should('have.value', '1'); + }); + + it('should correct work after backspace separator.2 decimalMarker . thousandSeparator ,', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + decimalMarker: '.', + thousandSeparator: ',', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('0.01') + .should('have.value', '0.01') + .type('{leftArrow}') + .type('{backspace}') + .should('have.value', '0.1') + .type('{leftArrow}') + .type('{backspace}') + .should('have.value', '1'); + }); + + it('should correct work after backspace separator.2 decimalMarker . thousandSeparator , allowNegative', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + decimalMarker: '.', + thousandSeparator: ',', + allowNegativeNumbers: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('-0.01') + .should('have.value', '-0.01') + .type('{leftArrow}') + .type('{backspace}') + .should('have.value', '-0.1') + .type('{leftArrow}') + .type('{backspace}') + .should('have.value', '-1'); + }); + + it('should correct work after backspace separator.3 decimalMarker . thousandSeparator , allowNegative', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.3', + decimalMarker: '.', + thousandSeparator: ',', + allowNegativeNumbers: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('-0.014') + .should('have.value', '-0.014') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '-0.14') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '-14'); + }); + + it('should correct work after backspace separator.3 leadZero allowNegative', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.3', + allowNegativeNumbers: true, + leadZero: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('-0.1') + .should('have.value', '-0.1') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '-1') + .type('{backspace}') + .should('have.value', '-'); + }); + + it('should correct work after backspace separator', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('0.33') + .should('have.value', '0.33') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '33'); + }); + + it('should correct work after backspace separator leadZero', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator', + leadZero: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('0.33') + .should('have.value', '0.33') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '33'); + }); + + it('should correct work after backspace separator allowNegativeNumbers', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator', + allowNegativeNumbers: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('-0.33') + .should('have.value', '-0.33') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '-33'); + }); + + it('should correct work after backspace separator leadZero', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator', + leadZero: true, + allowNegativeNumbers: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('-0.33') + .should('have.value', '-0.33') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '-33'); + }); + + it('should correct work after backspace separator.2 when first digit .', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + thousandSeparator: '.', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('50004') + .should('have.value', '50.004') + .type('{leftArrow}'.repeat(5)) + .type('{backspace}') + .should('have.value', '4'); + }); + + it('should correct work after backspace separator.2 when first digit ,', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + thousandSeparator: ',', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('50004') + .should('have.value', '50,004') + .type('{leftArrow}'.repeat(5)) + .type('{backspace}') + .should('have.value', '4'); + }); + + it('should correct work after backspace separator.2 when first digit whitespace', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: 'separator.2', + thousandSeparator: ' ', + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('50004') + .should('have.value', '50 004') + .type('{leftArrow}'.repeat(5)) + .type('{backspace}') + .should('have.value', '4'); + }); +}); diff --git a/projects/ngx-mask-lib/src/test/separator.spec.ts b/projects/ngx-mask-lib/src/test/separator.spec.ts index 970bd8dd..93529b7a 100644 --- a/projects/ngx-mask-lib/src/test/separator.spec.ts +++ b/projects/ngx-mask-lib/src/test/separator.spec.ts @@ -1835,4 +1835,42 @@ describe('Separator: Mask', () => { fixture.detectChanges(); expect(inputTarget.value).toBe('10.1000000000'); })); + + it('should support big numbers with separator', () => { + component.mask = 'separator'; + + equal('12345678910111215', '12 345 678 910 111 215', fixture); + expect(component.form.value).toBe('12345678910111215'); + equal('12345678910111215.9999', '12 345 678 910 111 215.9999', fixture); + expect(component.form.value).toBe('12345678910111215.9999'); + }); + + it('should support big numbers with separator 2', () => { + component.mask = 'separator.2'; + + equal('12345678910111215', '12 345 678 910 111 215', fixture); + expect(component.form.value).toBe('12345678910111215'); + equal('12345678910111215.9999', '12 345 678 910 111 215.99', fixture); + expect(component.form.value).toBe('12345678910111215.99'); + }); + + it('should support big numbers with separator 2 thousand =.', () => { + component.mask = 'separator.2'; + component.thousandSeparator = '.'; + + equal('12345678910111215', '12.345.678.910.111.215', fixture); + expect(component.form.value).toBe('12345678910111215'); + equal('12345678910111215,99', '12.345.678.910.111.215,99', fixture); + expect(component.form.value).toBe('12345678910111215.99'); + }); + + it('should support big numbers with separator 2 thousand =,', () => { + component.mask = 'separator.2'; + component.thousandSeparator = ','; + + equal('12345678910111215', '12,345,678,910,111,215', fixture); + expect(component.form.value).toBe('12345678910111215'); + equal('12345678910111215.9999', '12,345,678,910,111,215.99', fixture); + expect(component.form.value).toBe('12345678910111215.99'); + }); }); diff --git a/projects/ngx-mask-lib/src/test/validation.spec.ts b/projects/ngx-mask-lib/src/test/validation.spec.ts index cc65a35e..6ce0fdd9 100644 --- a/projects/ngx-mask-lib/src/test/validation.spec.ts +++ b/projects/ngx-mask-lib/src/test/validation.spec.ts @@ -87,6 +87,23 @@ export class TestValidatorDropSpecialCharacters { public dropSpecialCharacters = [' ']; } +@Component({ + selector: 'jsdaddy-open-source-test', + template: ` + + `, +}) +// eslint-disable-next-line @angular-eslint/component-class-suffix +export class TestValidatorEmailMask { + public form: FormControl = new FormControl('', Validators.required); + public mask = 'A*@A*.A*'; + public dropSpecialCharacters = false; +} + describe('Directive: Mask (Validation)', () => { describe('Global validation true, validation attribute on input not specified', () => { let fixture: ComponentFixture; @@ -422,4 +439,46 @@ describe('Directive: Mask (Validation)', () => { expect(component.form.valid).toBe(true); }); }); + + describe('Global validation true, email mask', () => { + let fixture: ComponentFixture; + let component: TestValidatorEmailMask; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestValidatorEmailMask], + imports: [ReactiveFormsModule, NgxMaskDirective], + providers: [provideNgxMask({ validation: true })], + }); + fixture = TestBed.createComponent(TestValidatorEmailMask); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('email Mask should validated correct', () => { + component.mask = 'A*@A*.A*'; + component.dropSpecialCharacters = false; + + equal('validate', 'validate', fixture); + expect(component.form.valid).toBe(false); + + equal('validate@', 'validate@', fixture); + expect(component.form.valid).toBe(false); + + equal('validate@some', 'validate@some', fixture); + expect(component.form.valid).toBe(false); + + equal('validate@some.', 'validate@some.', fixture); + expect(component.form.valid).toBe(false); + + equal('validate@some.e', 'validate@some.e', fixture); + expect(component.form.valid).toBe(true); + + equal('validate@some.eu', 'validate@some.eu', fixture); + expect(component.form.valid).toBe(true); + + equal('validate@some.com', 'validate@some.com', fixture); + expect(component.form.valid).toBe(true); + }); + }); }); diff --git a/src/assets/content/common-cases.ts b/src/assets/content/common-cases.ts index 70d807f5..b775d193 100644 --- a/src/assets/content/common-cases.ts +++ b/src/assets/content/common-cases.ts @@ -63,6 +63,13 @@ export const ComDocs: ComDoc[] = [ id: 8, anchor: 'email-mask', }, + { + header: 'Email mask with validation', + text: '', + code: ``, + id: 8, + anchor: 'email-mask', + }, { header: 'Allow negative numbers to mask', text: 'You can allow negative numbers', @@ -142,8 +149,18 @@ export const ComExamples: TExample[] = [ { _placeholder: 'Valid email', _mask: 'A*@A*.SSS', + _validation: true, + _dropSpecialCharacters: false, control: { form: new UntypedFormControl(''), model: '' }, }, + { + _placeholder: 'Valid email', + _validation: true, + _dropSpecialCharacters: false, + _mask: 'A*@A*.A*', + control: { form: new UntypedFormControl(''), model: '' }, + }, + { _placeholder: 'allowNegativeNumbers mask', _allowNegativeNumbers: true,