Skip to content

Commit

Permalink
remove setTimeout from code, add exportAs for ccDirective, tests
Browse files Browse the repository at this point in the history
  • Loading branch information
timofei-iatsenko committed Mar 31, 2020
1 parent bac2a70 commit 28fb7ca
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 196 deletions.
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,24 @@ export class AppModule {
}
```

**Credit Card Formater**
**Credit Card Formatter**
* add `ccNumber` directive:
```html
<input id="cc-number" type="tel" autocomplete="cc-number" ccNumber>
```
* this will also apply a class name based off the card `.visa`, `.amex`, etc. See the array of card types in `credit-card.ts` for all available types

**Expiration Date Formater**
* You can get parsed card type by using export api:

```html
<input type="tel" ccNumber #ccNumber="ccNumber">
<span class="scheme">{{ccNumber.resolvedScheme$ | async}}</span>
```

`resolvedScheme$` will be populated with `visa`, `amex`, etc.


**Expiration Date Formatter**
Will support format of MM/YY or MM/YYYY
* add `ccExp` directive:
```html
Expand Down Expand Up @@ -96,7 +106,7 @@ export class AppComponent implements OnInit {

# Inspiration

Based on Stripe's [jquery.payment](https://github.com/stripe/jquery.payment) plugin but adapted for use by Angular2
Based on Stripe's [jquery.payment](https://github.com/stripe/jquery.payment) plugin but adapted for use by Angular

# License

Expand Down
Original file line number Diff line number Diff line change
@@ -1,139 +1,170 @@
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import {CreditCardFormatDirective} from './credit-card-format.directive';
import { CreditCardFormatDirective } from './credit-card-format.directive';
import { Component, DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';

@Component({
template: `<input type="tel" ccNumber>`,
})
class TestCreditCardFormatComponent {
const KEY_MAP = {
ONE: 49, // input `1`
BACKSPACE: 8,
};

function createKeyEvent(keyCode: number) {
return {keyCode, which: keyCode, preventDefault: jest.fn()};
}

function triggerKeyEvent(input: DebugElement, eventName: string, keyCode: number) {
input.triggerEventHandler(eventName, createKeyEvent(keyCode));
}


describe('Directive: CreditCardFormat', () => {
let component: TestCreditCardFormatComponent;
let fixture: ComponentFixture<TestCreditCardFormatComponent>;
let inputEl: DebugElement;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestCreditCardFormatComponent, CreditCardFormatDirective],
describe('general cases', () => {
@Component({
template: `<input type="tel" ccNumber>`,
})
class TestCreditCardFormatComponent {}

let fixture: ComponentFixture<TestCreditCardFormatComponent>;
let inputEl: DebugElement;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestCreditCardFormatComponent, CreditCardFormatDirective],
});
fixture = TestBed.createComponent(TestCreditCardFormatComponent);
inputEl = fixture.debugElement.query(By.css('input'));
});
fixture = TestBed.createComponent(TestCreditCardFormatComponent);
component = fixture.componentInstance;
inputEl = fixture.debugElement.query(By.css('input'));
});

it('formats card number tick by tick', fakeAsync(() => {
it('formats card number tick by tick', fakeAsync(() => {

inputEl.nativeElement.value = '4111 1111';
inputEl.triggerEventHandler('keydown', {keyCode: 49, which: 49});
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111');
inputEl.nativeElement.value = '4111 1111';
triggerKeyEvent(inputEl, 'keydown', KEY_MAP.ONE);

inputEl.triggerEventHandler('keypress', {keyCode: 49, which: 49});
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111');
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111');

// the value is changed here by the browser as default behavior
inputEl.nativeElement.value = '4111 11111';
triggerKeyEvent(inputEl, 'keypress', KEY_MAP.ONE);
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111');

inputEl.nativeElement.focus();
inputEl.triggerEventHandler('input', null);
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
// the value is changed here by the browser as default behavior
inputEl.nativeElement.value = '4111 11111';

inputEl.triggerEventHandler('keyup', {keyCode: 49, which: 49});
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
inputEl.nativeElement.focus();
inputEl.triggerEventHandler('input', null);
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
triggerKeyEvent(inputEl, 'keyup', KEY_MAP.ONE);

}));
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
}));

it('formats card number one tick', fakeAsync(() => {
it('formats card number one tick', fakeAsync(() => {

inputEl.nativeElement.value = '4111 1111';
inputEl.triggerEventHandler('keydown', {keyCode: 49, which: 49});
fixture.detectChanges();
expect(inputEl.nativeElement.value).toBe('4111 1111');
inputEl.nativeElement.value = '4111 1111';
inputEl.triggerEventHandler('keydown', {keyCode: KEY_MAP.ONE, which: KEY_MAP.ONE});
fixture.detectChanges();
expect(inputEl.nativeElement.value).toBe('4111 1111');

inputEl.triggerEventHandler('keypress', {keyCode: 49, which: 49});
fixture.detectChanges();
expect(inputEl.nativeElement.value).toBe('4111 1111');
inputEl.triggerEventHandler('keypress', {keyCode: KEY_MAP.ONE, which: KEY_MAP.ONE});
fixture.detectChanges();
expect(inputEl.nativeElement.value).toBe('4111 1111');

// the value is changed here by the browser as default behavior
inputEl.nativeElement.value = '4111 11111';
// the value is changed here by the browser as default behavior
inputEl.nativeElement.value = '4111 11111';

inputEl.triggerEventHandler('input', null);
fixture.detectChanges();
expect(inputEl.nativeElement.value).toBe('4111 11111');
inputEl.triggerEventHandler('input', null);
fixture.detectChanges();
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
}));

inputEl.nativeElement.focus();
inputEl.triggerEventHandler('keyup', {keyCode: 49, which: 49});
fixture.detectChanges();
expect(inputEl.nativeElement.value).toBe('4111 11111');
it('deletes from middle of value', fakeAsync(() => {

tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
inputEl.nativeElement.value = '4111 1111 111';
inputEl.nativeElement.selectionStart = 5;
inputEl.nativeElement.selectionEnd = 5;
inputEl.nativeElement.focus();

}));
const event = createKeyEvent(KEY_MAP.BACKSPACE);

it('deletes from middle of value', fakeAsync(() => {
inputEl.triggerEventHandler('keydown', event);
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111 11');
expect(inputEl.nativeElement.selectionStart).toBe(3);
expect(inputEl.nativeElement.selectionEnd).toBe(3);
expect(event.preventDefault).toBeCalled();

inputEl.nativeElement.value = '4111 1111 111';
inputEl.nativeElement.selectionStart = 5;
inputEl.nativeElement.selectionEnd = 5;
inputEl.nativeElement.focus();
}));

let defPrevented = false;
it('deletes from beginning of value', fakeAsync(() => {

inputEl.triggerEventHandler('keydown', {keyCode: 8, which: 8, preventDefault() { defPrevented = true; }});
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111 11');
expect(inputEl.nativeElement.selectionStart).toBe(3);
expect(inputEl.nativeElement.selectionEnd).toBe(3);
expect(defPrevented).toBeTruthy();
inputEl.nativeElement.value = '5 411 1111';
inputEl.nativeElement.selectionStart = 2;
inputEl.nativeElement.selectionEnd = 2;
inputEl.nativeElement.focus();

}));
const event = createKeyEvent(KEY_MAP.BACKSPACE);

it('deletes from beginning of value', fakeAsync(() => {
inputEl.triggerEventHandler('keydown', event);
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 111');
expect(inputEl.nativeElement.selectionStart).toBe(0);
expect(inputEl.nativeElement.selectionEnd).toBe(0);
expect(event.preventDefault).toBeCalled();

inputEl.nativeElement.value = '5 411 1111';
inputEl.nativeElement.selectionStart = 2;
inputEl.nativeElement.selectionEnd = 2;
inputEl.nativeElement.focus();
}));

let defPrevented = false;
it('does not modify deleting from end of value', fakeAsync(() => {

inputEl.triggerEventHandler('keydown', {keyCode: 8, which: 8, preventDefault() { defPrevented = true; }});
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 111');
expect(inputEl.nativeElement.selectionStart).toBe(0);
expect(inputEl.nativeElement.selectionEnd).toBe(0);
expect(defPrevented).toBeTruthy();
inputEl.nativeElement.value = '4111 1111 111';
inputEl.nativeElement.selectionStart = 13;
inputEl.nativeElement.selectionEnd = 13;
inputEl.nativeElement.focus();

}));
const event = createKeyEvent(KEY_MAP.BACKSPACE);
inputEl.triggerEventHandler('keydown', event);
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111 111');
expect(inputEl.nativeElement.selectionStart).toBe(13);
expect(inputEl.nativeElement.selectionEnd).toBe(13);
expect(event.preventDefault).not.toBeCalled();

it('does not modify deleting from end of value', fakeAsync(() => {

inputEl.nativeElement.value = '4111 1111 111';
inputEl.nativeElement.selectionStart = 13;
inputEl.nativeElement.selectionEnd = 13;
inputEl.nativeElement.focus();
}));
});

let defPrevented = false;
describe('exportAs cases', () => {
@Component({
template: `<input type="tel" ccNumber #ccNumber="ccNumber">
<span class="scheme">{{ccNumber.resolvedScheme$ | async}}</span>`,
})
class TestCreditCardFormatComponent {}

let fixture: ComponentFixture<TestCreditCardFormatComponent>;
let inputEl: DebugElement;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestCreditCardFormatComponent, CreditCardFormatDirective],
});
fixture = TestBed.createComponent(TestCreditCardFormatComponent);
inputEl = fixture.debugElement.query(By.css('input'));
});

inputEl.triggerEventHandler('keydown', {keyCode: 8, which: 8, preventDefault() { defPrevented = true; }});
fixture.detectChanges();
tick(10);
expect(inputEl.nativeElement.value).toBe('4111 1111 111');
expect(inputEl.nativeElement.selectionStart).toBe(13);
expect(inputEl.nativeElement.selectionEnd).toBe(13);
expect(defPrevented).toBeFalsy();
it('should provide resolved scheme via exportAs', () => {
(inputEl.nativeElement as HTMLInputElement).value = '4111111111111111';
inputEl.triggerEventHandler('input', null);
fixture.detectChanges();

}));
const span: HTMLSpanElement = fixture.debugElement.query(By.css('.scheme')).nativeElement;
expect(span.textContent).toBe('visa');
});
});
});
Loading

0 comments on commit 28fb7ca

Please sign in to comment.