@testgorilla/tgo-immersive-test 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +46 -0
- package/README.md +89 -0
- package/jest.config.ts +28 -0
- package/ng-package.json +16 -0
- package/package.json +25 -0
- package/project.json +37 -0
- package/src/assets/i18n/en.json +18 -0
- package/src/index.ts +4 -0
- package/src/lib/components/immersive-test/immersive-test.component.html +100 -0
- package/src/lib/components/immersive-test/immersive-test.component.scss +247 -0
- package/src/lib/components/immersive-test/immersive-test.component.spec.ts +581 -0
- package/src/lib/components/immersive-test/immersive-test.component.ts +279 -0
- package/src/lib/components/index.ts +6 -0
- package/src/lib/components/review-instructions-dialog/index.ts +1 -0
- package/src/lib/components/review-instructions-dialog/review-instructions-dialog.component.html +39 -0
- package/src/lib/components/review-instructions-dialog/review-instructions-dialog.component.scss +160 -0
- package/src/lib/components/review-instructions-dialog/review-instructions-dialog.component.spec.ts +81 -0
- package/src/lib/components/review-instructions-dialog/review-instructions-dialog.component.ts +80 -0
- package/src/lib/components/ringing-phone-animation/index.ts +1 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.component.html +16 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.component.scss +79 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.component.spec.ts +95 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.component.ts +54 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.sound.ts +2 -0
- package/src/lib/components/video-countdown/index.ts +1 -0
- package/src/lib/components/video-countdown/video-countdown.component.html +10 -0
- package/src/lib/components/video-countdown/video-countdown.component.scss +16 -0
- package/src/lib/components/video-countdown/video-countdown.component.spec.ts +59 -0
- package/src/lib/components/video-countdown/video-countdown.component.ts +102 -0
- package/src/lib/models/index.ts +9 -0
- package/src/lib/models/translations.ts +3 -0
- package/src/lib/services/index.ts +7 -0
- package/src/test-setup.ts +22 -0
- package/tsconfig.json +17 -0
- package/tsconfig.lib.json +15 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +13 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
.ringing-phone-animation-wrapper {
|
|
2
|
+
display: flex;
|
|
3
|
+
justify-content: center;
|
|
4
|
+
align-items: center;
|
|
5
|
+
|
|
6
|
+
.ringing-phone-animation-container {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
gap: 10px;
|
|
12
|
+
|
|
13
|
+
.ringing-effect {
|
|
14
|
+
display: flex;
|
|
15
|
+
gap: 15px;
|
|
16
|
+
opacity: 1;
|
|
17
|
+
animation: blink-wrapper 1.4s infinite;
|
|
18
|
+
animation-delay: 0.8s;
|
|
19
|
+
|
|
20
|
+
.ringing-line {
|
|
21
|
+
width: 6px;
|
|
22
|
+
height: 16px;
|
|
23
|
+
background-color: white;
|
|
24
|
+
opacity: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.first-line {
|
|
28
|
+
animation: blink-line 1.4s infinite;
|
|
29
|
+
animation-delay: 0.1s;
|
|
30
|
+
transform: rotate(-45deg);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.second-line {
|
|
34
|
+
animation: blink-line 1.4s infinite;
|
|
35
|
+
animation-delay: 0.3s;
|
|
36
|
+
transform: translate(0, -50%);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.third-line {
|
|
40
|
+
animation: blink-line 1.4s infinite;
|
|
41
|
+
animation-delay: 0.6s;
|
|
42
|
+
transform: rotate(45deg);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.answer-btn {
|
|
47
|
+
margin-top: 20px;
|
|
48
|
+
padding: 10px 20px;
|
|
49
|
+
background-color: white;
|
|
50
|
+
border: none;
|
|
51
|
+
border-radius: 20px;
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
font-size: 16px;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@keyframes blink-line {
|
|
59
|
+
0%,
|
|
60
|
+
49% {
|
|
61
|
+
opacity: 0;
|
|
62
|
+
}
|
|
63
|
+
50%,
|
|
64
|
+
100% {
|
|
65
|
+
opacity: 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@keyframes blink-wrapper {
|
|
70
|
+
0%,
|
|
71
|
+
49% {
|
|
72
|
+
opacity: 1;
|
|
73
|
+
}
|
|
74
|
+
50%,
|
|
75
|
+
100% {
|
|
76
|
+
opacity: 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { SimpleChanges } from '@angular/core';
|
|
3
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
4
|
+
import { RingingPhoneAnimationComponent } from './ringing-phone-animation.component';
|
|
5
|
+
import SpyInstance = jest.SpyInstance;
|
|
6
|
+
|
|
7
|
+
describe('RingingPhoneAnimationComponent', () => {
|
|
8
|
+
let component: RingingPhoneAnimationComponent;
|
|
9
|
+
let fixture: ComponentFixture<RingingPhoneAnimationComponent>;
|
|
10
|
+
|
|
11
|
+
let playSpy: SpyInstance;
|
|
12
|
+
let pauseSpy: SpyInstance;
|
|
13
|
+
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
await TestBed.configureTestingModule({
|
|
16
|
+
imports: [RingingPhoneAnimationComponent, CommonModule],
|
|
17
|
+
}).compileComponents();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
fixture = TestBed.createComponent(RingingPhoneAnimationComponent);
|
|
22
|
+
component = fixture.componentInstance;
|
|
23
|
+
|
|
24
|
+
playSpy = jest
|
|
25
|
+
.spyOn(HTMLAudioElement.prototype, 'play')
|
|
26
|
+
.mockImplementation(() => Promise.resolve());
|
|
27
|
+
pauseSpy = jest.spyOn(HTMLAudioElement.prototype, 'pause').mockImplementation(() => undefined);
|
|
28
|
+
|
|
29
|
+
fixture.detectChanges();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should create the component', () => {
|
|
33
|
+
expect(component).toBeTruthy();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('when initializing the component', () => {
|
|
37
|
+
it('should initialize with default values', () => {
|
|
38
|
+
expect(component.isRinging).toBe(true);
|
|
39
|
+
expect(component.ringingSignal()).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should update "ringingSignal" when "isRinging" changes', () => {
|
|
43
|
+
const changes: SimpleChanges = {
|
|
44
|
+
isRinging: {
|
|
45
|
+
previousValue: false,
|
|
46
|
+
currentValue: true,
|
|
47
|
+
firstChange: false,
|
|
48
|
+
isFirstChange: () => false,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
component.isRinging = false;
|
|
53
|
+
component.ngOnChanges(changes);
|
|
54
|
+
|
|
55
|
+
expect(component.ringingSignal()).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should call "playSpy" if "isRinging" changes to true', () => {
|
|
59
|
+
const changes: SimpleChanges = {
|
|
60
|
+
isRinging: {
|
|
61
|
+
previousValue: true,
|
|
62
|
+
currentValue: false,
|
|
63
|
+
firstChange: false,
|
|
64
|
+
isFirstChange: () => false,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
component.isRinging = true;
|
|
69
|
+
component.ngOnChanges(changes);
|
|
70
|
+
|
|
71
|
+
expect(component.ringingSignal()).toBe(true);
|
|
72
|
+
expect(component.audio.loop).toBe(true);
|
|
73
|
+
expect(component.audio.volume).toBe(0.15);
|
|
74
|
+
expect(playSpy).toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should call "pauseSpy" if isRinging changes to false', () => {
|
|
78
|
+
const changes: SimpleChanges = {
|
|
79
|
+
isRinging: {
|
|
80
|
+
previousValue: false,
|
|
81
|
+
currentValue: true,
|
|
82
|
+
firstChange: false,
|
|
83
|
+
isFirstChange: () => false,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
component.isRinging = false;
|
|
88
|
+
component.ngOnChanges(changes);
|
|
89
|
+
|
|
90
|
+
expect(component.ringingSignal()).toBe(false);
|
|
91
|
+
expect(component.audio.currentTime).toBe(0);
|
|
92
|
+
expect(pauseSpy).toHaveBeenCalled();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
WritableSignal,
|
|
4
|
+
signal,
|
|
5
|
+
Input,
|
|
6
|
+
SimpleChanges,
|
|
7
|
+
OnChanges,
|
|
8
|
+
OnDestroy,
|
|
9
|
+
} from '@angular/core';
|
|
10
|
+
import { CommonModule } from '@angular/common';
|
|
11
|
+
import { ringingPhoneSound } from './ringing-phone-animation.sound';
|
|
12
|
+
|
|
13
|
+
@Component({
|
|
14
|
+
selector: 'tgo-ringing-phone-animation',
|
|
15
|
+
templateUrl: './ringing-phone-animation.component.html',
|
|
16
|
+
styleUrls: ['./ringing-phone-animation.component.scss'],
|
|
17
|
+
standalone: true,
|
|
18
|
+
imports: [CommonModule],
|
|
19
|
+
})
|
|
20
|
+
export class RingingPhoneAnimationComponent implements OnChanges, OnDestroy {
|
|
21
|
+
@Input() isRinging = true;
|
|
22
|
+
|
|
23
|
+
ringingSignal: WritableSignal<boolean> = signal(this.isRinging);
|
|
24
|
+
audio: HTMLAudioElement = new Audio(ringingPhoneSound);
|
|
25
|
+
|
|
26
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
27
|
+
if (changes['isRinging']) {
|
|
28
|
+
this.ringingSignal.set(this.isRinging);
|
|
29
|
+
|
|
30
|
+
if (this.isRinging) {
|
|
31
|
+
this.startRinging();
|
|
32
|
+
} else {
|
|
33
|
+
this.stopRinging();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
ngOnDestroy(): void {
|
|
39
|
+
this.stopRinging();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private stopRinging(): void {
|
|
43
|
+
this.ringingSignal.set(false);
|
|
44
|
+
this.audio.pause();
|
|
45
|
+
this.audio.currentTime = 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private startRinging(): void {
|
|
49
|
+
this.audio.loop = true;
|
|
50
|
+
this.audio.volume = 0.15;
|
|
51
|
+
void this.audio.play();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export const ringingPhoneSound = `data:audio/mp3;base64,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`;
|
|
2
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './video-countdown.component';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.video-countdown {
|
|
2
|
+
display: flex;
|
|
3
|
+
justify-content: center;
|
|
4
|
+
|
|
5
|
+
&-element {
|
|
6
|
+
position: absolute;
|
|
7
|
+
font-size: 80px;
|
|
8
|
+
font-weight: 700;
|
|
9
|
+
line-height: 1;
|
|
10
|
+
-webkit-user-select: none;
|
|
11
|
+
user-select: none;
|
|
12
|
+
opacity: 1;
|
|
13
|
+
backface-visibility: hidden;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
2
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
3
|
+
import { VideoCountdownComponent } from './video-countdown.component';
|
|
4
|
+
|
|
5
|
+
describe('VideoCountdownComponent', () => {
|
|
6
|
+
let component: VideoCountdownComponent;
|
|
7
|
+
let fixture: ComponentFixture<VideoCountdownComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [VideoCountdownComponent, NoopAnimationsModule],
|
|
12
|
+
}).compileComponents();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
fixture = TestBed.createComponent(VideoCountdownComponent);
|
|
17
|
+
component = fixture.componentInstance;
|
|
18
|
+
|
|
19
|
+
fixture.detectChanges();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should create', () => {
|
|
23
|
+
expect(component).toBeTruthy();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should start counting on component init', () => {
|
|
27
|
+
const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
28
|
+
component.ngOnInit();
|
|
29
|
+
expect(setTimeoutSpy).toHaveBeenCalled();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should set isActive to true on start', () => {
|
|
33
|
+
component.start();
|
|
34
|
+
expect(component.isActive).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should count down from startFrom to endAt', (done) => {
|
|
38
|
+
component.startFrom = 3;
|
|
39
|
+
component.endAt = 0;
|
|
40
|
+
component.interval = 100;
|
|
41
|
+
const finishSpy = jest.spyOn(component.finish, 'emit');
|
|
42
|
+
|
|
43
|
+
component.start();
|
|
44
|
+
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
expect(component.displayNum.value).toBe(0);
|
|
47
|
+
expect(finishSpy).toHaveBeenCalled();
|
|
48
|
+
expect(component.isActive).toBe(false);
|
|
49
|
+
done();
|
|
50
|
+
}, 1000);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should clean up on ngOnDestroy', () => {
|
|
54
|
+
const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
|
|
55
|
+
component.ngOnDestroy();
|
|
56
|
+
expect(clearTimeoutSpy).toHaveBeenCalledWith(component.timeoutId);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { animate, style, transition, trigger } from '@angular/animations';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import {
|
|
4
|
+
ChangeDetectionStrategy,
|
|
5
|
+
ChangeDetectorRef,
|
|
6
|
+
Component,
|
|
7
|
+
EventEmitter,
|
|
8
|
+
Input,
|
|
9
|
+
OnDestroy,
|
|
10
|
+
OnInit,
|
|
11
|
+
Output,
|
|
12
|
+
} from '@angular/core';
|
|
13
|
+
|
|
14
|
+
@Component({
|
|
15
|
+
selector: 'tgo-video-countdown',
|
|
16
|
+
templateUrl: './video-countdown.component.html',
|
|
17
|
+
styleUrls: ['./video-countdown.component.scss'],
|
|
18
|
+
animations: [
|
|
19
|
+
trigger('zoomInOut', [
|
|
20
|
+
transition(':enter', [
|
|
21
|
+
style({ transform: 'scale(0)', opacity: 0 }),
|
|
22
|
+
animate('300ms ease-out', style({ transform: 'scale(1.2)', opacity: 1 })),
|
|
23
|
+
animate('100ms ease-in', style({ transform: 'scale(1)' })),
|
|
24
|
+
]),
|
|
25
|
+
transition(':leave', [
|
|
26
|
+
style({ opacity: 1 }),
|
|
27
|
+
animate('100ms ease-out', style({ transform: 'scale(1.2)', opacity: 0.5 })),
|
|
28
|
+
animate('200ms ease-in', style({ transform: 'scale(0)', opacity: 0 })),
|
|
29
|
+
]),
|
|
30
|
+
]),
|
|
31
|
+
],
|
|
32
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
33
|
+
standalone: true,
|
|
34
|
+
imports: [CommonModule],
|
|
35
|
+
})
|
|
36
|
+
export class VideoCountdownComponent implements OnInit, OnDestroy {
|
|
37
|
+
@Input() startFrom = 5;
|
|
38
|
+
@Input() endAt = 0;
|
|
39
|
+
@Input() interval = 1325;
|
|
40
|
+
@Output() finish: EventEmitter<void> = new EventEmitter<void>();
|
|
41
|
+
|
|
42
|
+
isActive = false;
|
|
43
|
+
displayNum: { key: number; value: number } = {
|
|
44
|
+
key: this.startFrom,
|
|
45
|
+
value: this.startFrom,
|
|
46
|
+
};
|
|
47
|
+
timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
48
|
+
|
|
49
|
+
constructor(private cdr: ChangeDetectorRef) {}
|
|
50
|
+
|
|
51
|
+
ngOnInit(): void {
|
|
52
|
+
this.start();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
start() {
|
|
56
|
+
this.isActive = true;
|
|
57
|
+
this.displayNum = { key: this.startFrom, value: this.startFrom };
|
|
58
|
+
this.scheduleAnimation();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
ngOnDestroy(): void {
|
|
62
|
+
this.isActive = false;
|
|
63
|
+
if (this.timeoutId) {
|
|
64
|
+
clearTimeout(this.timeoutId);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private scheduleAnimation() {
|
|
69
|
+
this.timeoutId = setTimeout(() => {
|
|
70
|
+
this.anim();
|
|
71
|
+
}, this.interval);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private anim() {
|
|
75
|
+
if (!this.isActive) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.updateNumber();
|
|
80
|
+
this.cdr.markForCheck();
|
|
81
|
+
|
|
82
|
+
if (this.displayNum && this.displayNum.value === this.endAt) {
|
|
83
|
+
this.finish.emit();
|
|
84
|
+
this.isActive = false;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.scheduleAnimation();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private updateNumber() {
|
|
92
|
+
if (this.displayNum && this.displayNum.value === this.endAt) {
|
|
93
|
+
this.displayNum = { key: this.startFrom, value: this.startFrom - 1 };
|
|
94
|
+
} else {
|
|
95
|
+
this.displayNum = {
|
|
96
|
+
key: this.displayNum.value - 1,
|
|
97
|
+
value: this.displayNum.value - 1,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import 'jest-preset-angular/setup-jest';
|
|
2
|
+
|
|
3
|
+
// Mock HTMLMediaElement.play() for jsdom
|
|
4
|
+
Object.defineProperty(HTMLMediaElement.prototype, 'play', {
|
|
5
|
+
writable: true,
|
|
6
|
+
value: jest.fn().mockResolvedValue(undefined),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// Mock @vimeo/player
|
|
10
|
+
jest.mock('@vimeo/player', () => {
|
|
11
|
+
return {
|
|
12
|
+
default: jest.fn().mockImplementation(() => ({
|
|
13
|
+
on: jest.fn(),
|
|
14
|
+
off: jest.fn(),
|
|
15
|
+
loadVideo: jest.fn(),
|
|
16
|
+
play: jest.fn(),
|
|
17
|
+
pause: jest.fn(),
|
|
18
|
+
destroy: jest.fn(),
|
|
19
|
+
})),
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"declarationMap": true,
|
|
7
|
+
"inlineSources": true,
|
|
8
|
+
"types": [
|
|
9
|
+
"node",
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"],
|
|
13
|
+
"include": ["src/**/*.ts"]
|
|
14
|
+
}
|
|
15
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"target": "es2016",
|
|
7
|
+
"types": ["jest", "node"]
|
|
8
|
+
},
|
|
9
|
+
"files": ["src/test-setup.ts"],
|
|
10
|
+
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"],
|
|
11
|
+
"exclude": ["src/lib/models/test.ts"]
|
|
12
|
+
}
|
|
13
|
+
|