Skip to content

Commit 28fb7ca

Browse files
remove setTimeout from code, add exportAs for ccDirective, tests
1 parent bac2a70 commit 28fb7ca

File tree

6 files changed

+210
-196
lines changed

6 files changed

+210
-196
lines changed

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,24 @@ export class AppModule {
3636
}
3737
```
3838

39-
**Credit Card Formater**
39+
**Credit Card Formatter**
4040
* add `ccNumber` directive:
4141
```html
4242
<input id="cc-number" type="tel" autocomplete="cc-number" ccNumber>
4343
```
4444
* 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
4545

46-
**Expiration Date Formater**
46+
* You can get parsed card type by using export api:
47+
48+
```html
49+
<input type="tel" ccNumber #ccNumber="ccNumber">
50+
<span class="scheme">{{ccNumber.resolvedScheme$ | async}}</span>
51+
```
52+
53+
`resolvedScheme$` will be populated with `visa`, `amex`, etc.
54+
55+
56+
**Expiration Date Formatter**
4757
Will support format of MM/YY or MM/YYYY
4858
* add `ccExp` directive:
4959
```html
@@ -96,7 +106,7 @@ export class AppComponent implements OnInit {
96106

97107
# Inspiration
98108

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

101111
# License
102112

Lines changed: 131 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,170 @@
11
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
2-
import {CreditCardFormatDirective} from './credit-card-format.directive';
2+
import { CreditCardFormatDirective } from './credit-card-format.directive';
33
import { Component, DebugElement } from '@angular/core';
44
import { By } from '@angular/platform-browser';
55

6-
@Component({
7-
template: `<input type="tel" ccNumber>`,
8-
})
9-
class TestCreditCardFormatComponent {
6+
const KEY_MAP = {
7+
ONE: 49, // input `1`
8+
BACKSPACE: 8,
9+
};
10+
11+
function createKeyEvent(keyCode: number) {
12+
return {keyCode, which: keyCode, preventDefault: jest.fn()};
13+
}
14+
15+
function triggerKeyEvent(input: DebugElement, eventName: string, keyCode: number) {
16+
input.triggerEventHandler(eventName, createKeyEvent(keyCode));
1017
}
1118

19+
1220
describe('Directive: CreditCardFormat', () => {
13-
let component: TestCreditCardFormatComponent;
14-
let fixture: ComponentFixture<TestCreditCardFormatComponent>;
15-
let inputEl: DebugElement;
1621

17-
beforeEach(() => {
18-
TestBed.configureTestingModule({
19-
declarations: [TestCreditCardFormatComponent, CreditCardFormatDirective],
22+
describe('general cases', () => {
23+
@Component({
24+
template: `<input type="tel" ccNumber>`,
25+
})
26+
class TestCreditCardFormatComponent {}
27+
28+
let fixture: ComponentFixture<TestCreditCardFormatComponent>;
29+
let inputEl: DebugElement;
30+
beforeEach(() => {
31+
TestBed.configureTestingModule({
32+
declarations: [TestCreditCardFormatComponent, CreditCardFormatDirective],
33+
});
34+
fixture = TestBed.createComponent(TestCreditCardFormatComponent);
35+
inputEl = fixture.debugElement.query(By.css('input'));
2036
});
21-
fixture = TestBed.createComponent(TestCreditCardFormatComponent);
22-
component = fixture.componentInstance;
23-
inputEl = fixture.debugElement.query(By.css('input'));
24-
});
2537

26-
it('formats card number tick by tick', fakeAsync(() => {
38+
it('formats card number tick by tick', fakeAsync(() => {
2739

28-
inputEl.nativeElement.value = '4111 1111';
29-
inputEl.triggerEventHandler('keydown', {keyCode: 49, which: 49});
30-
fixture.detectChanges();
31-
tick(10);
32-
expect(inputEl.nativeElement.value).toBe('4111 1111');
40+
inputEl.nativeElement.value = '4111 1111';
41+
triggerKeyEvent(inputEl, 'keydown', KEY_MAP.ONE);
3342

34-
inputEl.triggerEventHandler('keypress', {keyCode: 49, which: 49});
35-
fixture.detectChanges();
36-
tick(10);
37-
expect(inputEl.nativeElement.value).toBe('4111 1111');
43+
fixture.detectChanges();
44+
tick(10);
45+
expect(inputEl.nativeElement.value).toBe('4111 1111');
3846

39-
// the value is changed here by the browser as default behavior
40-
inputEl.nativeElement.value = '4111 11111';
47+
triggerKeyEvent(inputEl, 'keypress', KEY_MAP.ONE);
48+
fixture.detectChanges();
49+
tick(10);
50+
expect(inputEl.nativeElement.value).toBe('4111 1111');
4151

42-
inputEl.nativeElement.focus();
43-
inputEl.triggerEventHandler('input', null);
44-
fixture.detectChanges();
45-
tick(10);
46-
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
52+
// the value is changed here by the browser as default behavior
53+
inputEl.nativeElement.value = '4111 11111';
4754

48-
inputEl.triggerEventHandler('keyup', {keyCode: 49, which: 49});
49-
fixture.detectChanges();
50-
tick(10);
51-
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
55+
inputEl.nativeElement.focus();
56+
inputEl.triggerEventHandler('input', null);
57+
fixture.detectChanges();
58+
tick(10);
59+
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
60+
triggerKeyEvent(inputEl, 'keyup', KEY_MAP.ONE);
5261

53-
}));
62+
fixture.detectChanges();
63+
tick(10);
64+
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
65+
}));
5466

55-
it('formats card number one tick', fakeAsync(() => {
67+
it('formats card number one tick', fakeAsync(() => {
5668

57-
inputEl.nativeElement.value = '4111 1111';
58-
inputEl.triggerEventHandler('keydown', {keyCode: 49, which: 49});
59-
fixture.detectChanges();
60-
expect(inputEl.nativeElement.value).toBe('4111 1111');
69+
inputEl.nativeElement.value = '4111 1111';
70+
inputEl.triggerEventHandler('keydown', {keyCode: KEY_MAP.ONE, which: KEY_MAP.ONE});
71+
fixture.detectChanges();
72+
expect(inputEl.nativeElement.value).toBe('4111 1111');
6173

62-
inputEl.triggerEventHandler('keypress', {keyCode: 49, which: 49});
63-
fixture.detectChanges();
64-
expect(inputEl.nativeElement.value).toBe('4111 1111');
74+
inputEl.triggerEventHandler('keypress', {keyCode: KEY_MAP.ONE, which: KEY_MAP.ONE});
75+
fixture.detectChanges();
76+
expect(inputEl.nativeElement.value).toBe('4111 1111');
6577

66-
// the value is changed here by the browser as default behavior
67-
inputEl.nativeElement.value = '4111 11111';
78+
// the value is changed here by the browser as default behavior
79+
inputEl.nativeElement.value = '4111 11111';
6880

69-
inputEl.triggerEventHandler('input', null);
70-
fixture.detectChanges();
71-
expect(inputEl.nativeElement.value).toBe('4111 11111');
81+
inputEl.triggerEventHandler('input', null);
82+
fixture.detectChanges();
83+
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
84+
}));
7285

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

78-
tick(10);
79-
expect(inputEl.nativeElement.value).toBe('4111 1111 1');
88+
inputEl.nativeElement.value = '4111 1111 111';
89+
inputEl.nativeElement.selectionStart = 5;
90+
inputEl.nativeElement.selectionEnd = 5;
91+
inputEl.nativeElement.focus();
8092

81-
}));
93+
const event = createKeyEvent(KEY_MAP.BACKSPACE);
8294

83-
it('deletes from middle of value', fakeAsync(() => {
95+
inputEl.triggerEventHandler('keydown', event);
96+
fixture.detectChanges();
97+
tick(10);
98+
expect(inputEl.nativeElement.value).toBe('4111 1111 11');
99+
expect(inputEl.nativeElement.selectionStart).toBe(3);
100+
expect(inputEl.nativeElement.selectionEnd).toBe(3);
101+
expect(event.preventDefault).toBeCalled();
84102

85-
inputEl.nativeElement.value = '4111 1111 111';
86-
inputEl.nativeElement.selectionStart = 5;
87-
inputEl.nativeElement.selectionEnd = 5;
88-
inputEl.nativeElement.focus();
103+
}));
89104

90-
let defPrevented = false;
105+
it('deletes from beginning of value', fakeAsync(() => {
91106

92-
inputEl.triggerEventHandler('keydown', {keyCode: 8, which: 8, preventDefault() { defPrevented = true; }});
93-
fixture.detectChanges();
94-
tick(10);
95-
expect(inputEl.nativeElement.value).toBe('4111 1111 11');
96-
expect(inputEl.nativeElement.selectionStart).toBe(3);
97-
expect(inputEl.nativeElement.selectionEnd).toBe(3);
98-
expect(defPrevented).toBeTruthy();
107+
inputEl.nativeElement.value = '5 411 1111';
108+
inputEl.nativeElement.selectionStart = 2;
109+
inputEl.nativeElement.selectionEnd = 2;
110+
inputEl.nativeElement.focus();
99111

100-
}));
112+
const event = createKeyEvent(KEY_MAP.BACKSPACE);
101113

102-
it('deletes from beginning of value', fakeAsync(() => {
114+
inputEl.triggerEventHandler('keydown', event);
115+
fixture.detectChanges();
116+
tick(10);
117+
expect(inputEl.nativeElement.value).toBe('4111 111');
118+
expect(inputEl.nativeElement.selectionStart).toBe(0);
119+
expect(inputEl.nativeElement.selectionEnd).toBe(0);
120+
expect(event.preventDefault).toBeCalled();
103121

104-
inputEl.nativeElement.value = '5 411 1111';
105-
inputEl.nativeElement.selectionStart = 2;
106-
inputEl.nativeElement.selectionEnd = 2;
107-
inputEl.nativeElement.focus();
122+
}));
108123

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

111-
inputEl.triggerEventHandler('keydown', {keyCode: 8, which: 8, preventDefault() { defPrevented = true; }});
112-
fixture.detectChanges();
113-
tick(10);
114-
expect(inputEl.nativeElement.value).toBe('4111 111');
115-
expect(inputEl.nativeElement.selectionStart).toBe(0);
116-
expect(inputEl.nativeElement.selectionEnd).toBe(0);
117-
expect(defPrevented).toBeTruthy();
126+
inputEl.nativeElement.value = '4111 1111 111';
127+
inputEl.nativeElement.selectionStart = 13;
128+
inputEl.nativeElement.selectionEnd = 13;
129+
inputEl.nativeElement.focus();
118130

119-
}));
131+
const event = createKeyEvent(KEY_MAP.BACKSPACE);
132+
inputEl.triggerEventHandler('keydown', event);
133+
fixture.detectChanges();
134+
tick(10);
135+
expect(inputEl.nativeElement.value).toBe('4111 1111 111');
136+
expect(inputEl.nativeElement.selectionStart).toBe(13);
137+
expect(inputEl.nativeElement.selectionEnd).toBe(13);
138+
expect(event.preventDefault).not.toBeCalled();
120139

121-
it('does not modify deleting from end of value', fakeAsync(() => {
122-
123-
inputEl.nativeElement.value = '4111 1111 111';
124-
inputEl.nativeElement.selectionStart = 13;
125-
inputEl.nativeElement.selectionEnd = 13;
126-
inputEl.nativeElement.focus();
140+
}));
141+
});
127142

128-
let defPrevented = false;
143+
describe('exportAs cases', () => {
144+
@Component({
145+
template: `<input type="tel" ccNumber #ccNumber="ccNumber">
146+
<span class="scheme">{{ccNumber.resolvedScheme$ | async}}</span>`,
147+
})
148+
class TestCreditCardFormatComponent {}
149+
150+
let fixture: ComponentFixture<TestCreditCardFormatComponent>;
151+
let inputEl: DebugElement;
152+
153+
beforeEach(() => {
154+
TestBed.configureTestingModule({
155+
declarations: [TestCreditCardFormatComponent, CreditCardFormatDirective],
156+
});
157+
fixture = TestBed.createComponent(TestCreditCardFormatComponent);
158+
inputEl = fixture.debugElement.query(By.css('input'));
159+
});
129160

130-
inputEl.triggerEventHandler('keydown', {keyCode: 8, which: 8, preventDefault() { defPrevented = true; }});
131-
fixture.detectChanges();
132-
tick(10);
133-
expect(inputEl.nativeElement.value).toBe('4111 1111 111');
134-
expect(inputEl.nativeElement.selectionStart).toBe(13);
135-
expect(inputEl.nativeElement.selectionEnd).toBe(13);
136-
expect(defPrevented).toBeFalsy();
161+
it('should provide resolved scheme via exportAs', () => {
162+
(inputEl.nativeElement as HTMLInputElement).value = '4111111111111111';
163+
inputEl.triggerEventHandler('input', null);
164+
fixture.detectChanges();
137165

138-
}));
166+
const span: HTMLSpanElement = fixture.debugElement.query(By.css('.scheme')).nativeElement;
167+
expect(span.textContent).toBe('visa');
168+
});
169+
});
139170
});

0 commit comments

Comments
 (0)