Skip to content

Commit 3417a64

Browse files
V2 (#1)
* feat: emit focus event before checking "blur" emission * feat: misc enhancements
1 parent 00953c7 commit 3417a64

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+49787
-19544
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: on-pull-request
22

33
on:
4+
pull_request:
45
push:
56
branches:
67
- 'master'
@@ -19,11 +20,11 @@ jobs:
1920
- name: Install dependencies
2021
run: npm ci
2122
- name: Check Formatting
22-
run: npm run format:check
23-
- name: Integration Test
23+
run: git fetch origin master && npm run format:check -- --base=origin/master
24+
- name: Integration Test (Jest)
2425
run: npm run test
25-
- name: Test Package
26-
run: npm run test-package
26+
- name: Integration Test (Karma)
27+
run: npx nx run integration:test-karma
2728
- name: Build App
2829
run: npm run build -- --prod
2930
- name: Build Package

.vscode/extensions.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
2-
"recommendations": [
3-
"angular.ng-template",
4-
"nrwl.angular-console",
5-
"esbenp.prettier-vscode",
6-
"firsttris.vscode-jest-runner",
7-
"dbaeumer.vscode-eslint"
8-
]
2+
"recommendations": [
3+
"angular.ng-template",
4+
"nrwl.angular-console",
5+
"esbenp.prettier-vscode",
6+
"firsttris.vscode-jest-runner",
7+
"dbaeumer.vscode-eslint"
8+
]
99
}

README.md

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,19 @@
66
<img src="https://img.shields.io/npm/v/ngx-cva-test-suite.svg?logo=npm&logoColor=fff&label=NPM+package&color=limegreen" alt="NPM package" />
77
</a>
88

9-
`ngx-cva-test-suite` provides extensive set of test cases, ensuring your custom controls behave as intended.
9+
`ngx-cva-test-suite` provides an extensive set of test cases, ensuring your custom controls behave as intended. Package is designed and tested to work properly with both **Jest** and **Jasmine** test runners.
1010

1111
It provides various configurations, that allows even the most non-standard components to be properly tested.
1212

13+
Among the main features:
14+
15+
- ensures the correct amount of calls for the `onChange` function _(incorrect usage may result in extra emissions of `valueChanges` of formControl)_
16+
- ensures correct triggering of `onTouched` function _(is needed for `touched` state of the control and `updateOn: 'blur'` [strategy](https://angular.io/api/forms/AbstractControl#updateOn) to function properly)_
17+
- ensures that no extra emissions are present when control is disabled
18+
- checks for control to be resettable using `AbstractControl.reset()`
19+
20+
In the repository you can also [find few simple CVA components](apps/integration/src/app/controls), that are configured properly along with `ngx-cva-test-suite` setup for them.
21+
1322
## Installation
1423

1524
```
@@ -37,6 +46,53 @@ runValueAccessorTests({
3746
});
3847
```
3948

49+
## Using host template
50+
51+
This type of configuration might become handy, if your CVA component relies on projected content or specific layout to function correctly. A good example of such would be a select component, that gets it's options as projected content.
52+
53+
```typescript
54+
import { runValueAccessorTests } from 'ngx-cva-test-suite';
55+
import { Component, ViewChild } from '@angular/core';
56+
57+
import { CustomCheckboxControlValueAccessor } from './support/standard-value-accessors-directives';
58+
59+
@Component({
60+
template: `
61+
<app-select>
62+
<app-select-option [value]="1">Opt 1</app-select-option>
63+
<app-select-option [value]="2">Opt 2</app-select-option>
64+
<app-select-option [value]="3">Opt 3</app-select-option>
65+
</app-select>
66+
`,
67+
})
68+
export class SelectWrapperComponent {
69+
@ViewChild(AppSelectComponent) ctrl: AppSelectComponent;
70+
}
71+
72+
runValueAccessorTests<AppSelectComponent, SelectWrapperComponent>({
73+
// <= if host template is used, it should be marked explicitly as a type
74+
component: AppSelectComponent, // <= using actual AppSelectComponent as a test target
75+
testModuleMetadata: {
76+
declarations: [SelectWrapperComponent],
77+
imports: [AppSelectModule], // <= importing the module for app-select
78+
},
79+
hostTemplate: {
80+
// specify that "AppSelectComponent" should not be tested directly
81+
hostComponent: SelectWrapperComponent,
82+
// specify the way to access "AppSelectComponent" from the host template
83+
getTestingComponent: (fixture) => fixture.componentInstance.ctrl,
84+
},
85+
supportsOnBlur: false,
86+
internalValueChangeSetter: (fixture, value) => {
87+
// "setValue" is a function that is being called
88+
// when user selects any "app-select-option"
89+
fixture.componentInstance.ctrl.setValue(value, true);
90+
},
91+
getComponentValue: (fixture) => fixture.componentInstance.ctrl.value,
92+
getValues: () => [1, 2, 3], // <= setting the same values as select options in host template
93+
});
94+
```
95+
4096
## Config
4197

4298
### interface CVATestConfig<T extends CVAComponentType, H = T>
@@ -164,6 +220,14 @@ If set to true, `ControlValueAccessor.setDisabledState()` function will not be c
164220
165221
Test suite will automatically detect whether it's Jest or Jasmine environment. If needed, this can be overriden
166222
223+
#### excludeSteps?: CVATestSteps[];
224+
225+
List of steps to be excluded from execution. Cannot be specified along with `includeSteps`
226+
227+
#### includeSteps?: CVATestSteps[];
228+
229+
List of steps to be included in execution. Cannot be specified along with `excludeSteps`
230+
167231
### interface HostTemplate<T, H>
168232
169233
#### hostComponent: Type<any>
@@ -174,19 +238,21 @@ Wrapper, that hosts testing component. For example, to test `app-select-componen
174238
@Component({
175239
selector: 'app-test-component-wrapper',
176240
template: `
177-
<app-select label="Label Value">
178-
<app-select-item [value]="1" label="Opt 1"></app-select-item>
179-
<app-select-item [value]="2" label="Opt 2"></app-select-item>
180-
<app-select-item [value]="3" label="Opt 3"></app-select-item>
241+
<app-select label="Label Value" #ctrl>
242+
<app-select-option [value]="1" label="Opt 1"></app-select-option>
243+
<app-select-option [value]="2" label="Opt 2"></app-select-option>
244+
<app-select-option [value]="3" label="Opt 3"></app-select-option>
181245
</app-select>
182246
`,
183247
})
184-
class TestWrapperComponent {}
248+
class TestWrapperComponent {
249+
@ViewChild('ctrl') ctrl: AppSelectComponent;
250+
}
185251
```
186252
187253
#### getTestingComponent: (fixture: ComponentFixture<H>) => T
188254
189255
Getter for the actual component that is being tested
190256
191257
Using the hostComponent above, the following function should be used:
192-
`(fixture) => fixture.debugElement.children[0].componentInstance;`
258+
`(fixture) => fixture.componentInstance.ctrl;`

angular.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@
8585
"jestConfig": "apps/integration/jest.config.js",
8686
"passWithNoTests": true
8787
}
88+
},
89+
"test-karma": {
90+
"builder": "@angular-devkit/build-angular:karma",
91+
"options": {
92+
"main": "apps/integration/src/karma-test.ts",
93+
"tsConfig": "apps/integration/tsconfig.spec-karma.json",
94+
"karmaConfig": "apps/integration/karma.conf.js",
95+
"polyfills": "apps/integration/src/polyfills.ts"
96+
}
8897
}
8998
}
9099
},
Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
2-
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
3-
"ignorePatterns": ["!**/*"],
4-
"overrides": [
5-
{
6-
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7-
"rules": {}
8-
},
9-
{
10-
"files": ["src/plugins/index.js"],
11-
"rules": {
12-
"@typescript-eslint/no-var-requires": "off",
13-
"no-undef": "off"
14-
}
15-
}
16-
]
2+
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7+
"rules": {}
8+
},
9+
{
10+
"files": ["src/plugins/index.js"],
11+
"rules": {
12+
"@typescript-eslint/no-var-requires": "off",
13+
"no-undef": "off"
14+
}
15+
}
16+
]
1717
}

apps/integration-e2e/cypress.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
2-
"fileServerFolder": ".",
3-
"fixturesFolder": "./src/fixtures",
4-
"integrationFolder": "./src/integration",
5-
"modifyObstructiveCode": false,
6-
"supportFile": "./src/support/index.ts",
7-
"pluginsFile": false,
8-
"video": true,
9-
"videosFolder": "../../dist/cypress/apps/integration-e2e/videos",
10-
"screenshotsFolder": "../../dist/cypress/apps/integration-e2e/screenshots",
11-
"chromeWebSecurity": false
2+
"fileServerFolder": ".",
3+
"fixturesFolder": "./src/fixtures",
4+
"integrationFolder": "./src/integration",
5+
"modifyObstructiveCode": false,
6+
"supportFile": "./src/support/index.ts",
7+
"pluginsFile": false,
8+
"video": true,
9+
"videosFolder": "../../dist/cypress/apps/integration-e2e/videos",
10+
"screenshotsFolder": "../../dist/cypress/apps/integration-e2e/screenshots",
11+
"chromeWebSecurity": false
1212
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"name": "Using fixtures to represent data",
3-
"email": "[email protected]"
2+
"name": "Using fixtures to represent data",
3+
"email": "[email protected]"
44
}

apps/integration-e2e/tsconfig.json

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
{
2-
"extends": "../../tsconfig.base.json",
3-
"compilerOptions": {
4-
"sourceMap": false,
5-
"outDir": "../../dist/out-tsc",
6-
"allowJs": true,
7-
"types": ["cypress", "node"],
8-
"forceConsistentCasingInFileNames": true,
9-
"strict": true,
10-
"noImplicitReturns": true,
11-
"noFallthroughCasesInSwitch": true
12-
},
13-
"include": ["src/**/*.ts", "src/**/*.js"],
14-
"angularCompilerOptions": {
15-
"strictInjectionParameters": true,
16-
"strictInputAccessModifiers": true,
17-
"strictTemplates": true
18-
}
2+
"extends": "../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"sourceMap": false,
5+
"outDir": "../../dist/out-tsc",
6+
"allowJs": true,
7+
"types": ["cypress", "node"],
8+
"forceConsistentCasingInFileNames": true,
9+
"strict": true,
10+
"noImplicitReturns": true,
11+
"noFallthroughCasesInSwitch": true
12+
},
13+
"include": ["src/**/*.ts", "src/**/*.js"],
14+
"angularCompilerOptions": {
15+
"strictInjectionParameters": true,
16+
"strictInputAccessModifiers": true,
17+
"strictTemplates": true
18+
}
1919
}

apps/integration/.eslintrc.json

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
11
{
2-
"extends": ["../../.eslintrc.json"],
3-
"ignorePatterns": ["!**/*"],
4-
"overrides": [
5-
{
6-
"files": ["*.ts"],
7-
"extends": ["plugin:@nrwl/nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
8-
"rules": {
9-
"@angular-eslint/directive-selector": [
10-
"error",
11-
{
12-
"type": "attribute",
13-
"prefix": "lib",
14-
"style": "camelCase"
15-
}
16-
],
17-
"@angular-eslint/component-selector": [
18-
"error",
19-
{
20-
"type": "element",
21-
"prefix": "lib",
22-
"style": "kebab-case"
23-
}
24-
]
25-
}
26-
},
27-
{
28-
"files": ["*.html"],
29-
"extends": ["plugin:@nrwl/nx/angular-template"],
30-
"rules": {}
31-
}
32-
]
2+
"extends": ["../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts"],
7+
"extends": ["plugin:@nrwl/nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
8+
"rules": {
9+
"@typescript-eslint/no-non-null-assertion": "off",
10+
"@angular-eslint/directive-selector": [
11+
"error",
12+
{
13+
"type": "attribute",
14+
"prefix": "lib",
15+
"style": "camelCase"
16+
}
17+
],
18+
"@angular-eslint/component-selector": [
19+
"error",
20+
{
21+
"type": "element",
22+
"prefix": "lib",
23+
"style": "kebab-case"
24+
}
25+
]
26+
}
27+
},
28+
{
29+
"files": ["*.html"],
30+
"extends": ["plugin:@nrwl/nx/angular-template"],
31+
"rules": {}
32+
}
33+
]
3334
}

apps/integration/karma.conf.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Karma configuration file, see link for more information
2+
// https://karma-runner.github.io/1.0/config/configuration-file.html
3+
4+
const { join } = require('path');
5+
const getBaseKarmaConfig = require('../../karma.conf');
6+
7+
module.exports = function (config) {
8+
const baseConfig = getBaseKarmaConfig();
9+
config.set({
10+
...baseConfig,
11+
coverageIstanbulReporter: {
12+
...baseConfig.coverageIstanbulReporter,
13+
dir: join(__dirname, '../../coverage/apps/docs/'),
14+
},
15+
});
16+
};

0 commit comments

Comments
 (0)