https://github.com/dmitry-stepanenko/ngx-cva-test-suite
https://github.com/dmitry-stepanenko/ngx-cva-test-suite
angular cva typescript
Last synced: 3 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/dmitry-stepanenko/ngx-cva-test-suite
- Owner: dmitry-stepanenko
- License: mit
- Created: 2021-09-16T15:58:57.000Z (about 4 years ago)
- Default Branch: master
- Last Pushed: 2023-04-07T18:09:10.000Z (over 2 years ago)
- Last Synced: 2025-04-18T17:50:33.283Z (6 months ago)
- Topics: angular, cva, typescript
- Language: TypeScript
- Homepage:
- Size: 673 KB
- Stars: 7
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ngx-cva-test-suite
## Standardise your custom UI form components with ControlValueAccessor Test Suite
`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.
It provides various configurations, that allows even the most non-standard components to be properly tested.
Among the main features:
- ensures the correct amount of calls for the `onChange` function _(incorrect usage may result in extra emissions of `valueChanges` of formControl)_
- 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)_
- ensures that no extra emissions are present when control is disabled
- checks for control to be resettable using `AbstractControl.reset()`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.
## Installation
```
npm i ngx-cva-test-suite --save-dev
```## Simple Usage
See [config](#config) below for the details on each property.
```typescript
import { runValueAccessorTests } from `ngx-cva-test-suite`;
import { ComboboxComponent } from './combobox.component';runValueAccessorTests({
/** Component, that is being tested */
component: ComboboxComponent,
/**
* All the metadata required for this test to run.
* Under the hood calls TestBed.configureTestingModule with provided config.
*/
testModuleMetadata: {
declarations: [ComboboxComponent],
},
/** Whether component is able to track "onBlur" events separately */
supportsOnBlur: true,
/**
* CSS selector for the element, that should dispatch `blur` event.
* Required and used only if `supportsOnBlur` is set to true.
*/
nativeControlSelector: 'input.combobox-input',
/**
* Tests the correctness of an approach that is used to set value in the component,
* when the change is internal. It's optional and can be omitted by passing "null"
*/
internalValueChangeSetter: (fixture, value) => {
fixture.componentInstance.setValue(value, true);
},
/** Function to get the value of a component in a runtime. */
getComponentValue: (fixture) => fixture.componentInstance.value,
});
```## Using host template
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.
```typescript
import { runValueAccessorTests } from 'ngx-cva-test-suite';
import { Component, ViewChild } from '@angular/core';import { CustomCheckboxControlValueAccessor } from './support/standard-value-accessors-directives';
@Component({
template: `
Opt 1
Opt 2
Opt 3
`,
})
export class SelectWrapperComponent {
@ViewChild(AppSelectComponent) ctrl: AppSelectComponent;
}runValueAccessorTests({
// <= if host template is used, it should be marked explicitly as a type
component: AppSelectComponent, // <= using actual AppSelectComponent as a test target
testModuleMetadata: {
declarations: [SelectWrapperComponent],
imports: [AppSelectModule], // <= importing the module for app-select
},
hostTemplate: {
// specify that "AppSelectComponent" should not be tested directly
hostComponent: SelectWrapperComponent,
// specify the way to access "AppSelectComponent" from the host template
getTestingComponent: (fixture) => fixture.componentInstance.ctrl,
},
supportsOnBlur: false,
internalValueChangeSetter: (fixture, value) => {
// "setValue" is a function that is being called
// when user selects any "app-select-option"
fixture.componentInstance.ctrl.setValue(value, true);
},
getComponentValue: (fixture) => fixture.componentInstance.ctrl.value,
getValues: () => [1, 2, 3], // <= setting the same values as select options in host template
});
```## Config
### interface CVATestConfig
#### testModuleMetadata: TestModuleMetadata
All the metadata required for this test to run. Under the hood calls `TestBed.configureTestingModule` with provided config
#### name?: string
Is used in a root `describe` statement of the test suite.
Should be the name of the component, that is being tested. By default will use the name of testing component.
#### hostTemplate?: HostTemplate
Allows to define custom wrapper template for this set of tests. Is useful if CVA cannot be tested in complete isolation.
See details [below](#interface-hosttemplatet-h)
#### component: Type
Component, that is being tested
#### getComponentValue: (fixture: ComponentFixture) => any
Function to get the value of a component in a runtime. Is used to ensure component applies provided value correctly.
Set to `null` if you want to skip this check.Example usage:
```typescript
// supposed your component has "getValue()" method
getComponentValue: (fixture) => fixture.componentInstance.getValue();
```#### supportsOnBlur: boolean
This is related to the ability to track blur events in order to set `emitOn: 'blur'` when used in reactive form.
If set to true, component will be tested to not call `onTouched` event when value changed.
Instead of this, it will be expected to trigger this function
by html blur event using native control (see `nativeControlSelector`in this config).#### nativeControlSelector?: string
CSS selector for the element, that should dispatch `blur` event. Required and used only if `supportsOnBlur` is set to true.
Provided selected will be used to programmatically dispatch `blur` event.Example:
```html
```
For the CVA with HTML as above the following should be provided:
```typescript
nativeControlSelector: 'input.combobox-input';
```#### internalValueChangeSetter: (fixture: ComponentFixture, value: any) => void
Tests the approach that is used to set value in the component, when the change is internal
(e.g. by clicking on an option of the select or typing in the input field).
When value is set, "onChange" (and "onTouched" depending on the "blur" behavior) methods are expected to be invokedSet to `null` if you want to skip this check.
Example:
if in your component you have something like this in html```html
```
then provide here
```typescript
internalValueChangeSetter: (fixture, value) => {
fixture.componentInstance.onSearchChange(value);
};
```#### additionalSetup?: (fixture: ComponentFixture, done: () => void) => void
Gives an ability to add any additional logic for the setup process.
It will be called before running each test.Make sure you are calling `done` callback after setup is finished.
#### customDelay?: number
After setting the value, each test waits for the given amount of time before going further with checks. Defaults to 100ms
#### getValues?: () => [any, any, any]
Customizer for plain values. By default will use ['a', 'b', 'c']
This test suite applies up to 3 different values on the component. If strings are not supported in your component,
replace it with whatever is needed. It's recommended to **NOT** use consecutive same values (like ~`[1, 1, 1]`~)Example:
```
getValues: () => [1, 2, 3]
// or
getValues: () => [true, false, true]
```#### resetCustomValue?: { value: any }
Component will be tested for correct behavior, when `FormControl`'s `reset()` method is called.
After simulating this call, it will check if value of the component is reset internally.If your component is not supposed to work with `null` as a value, specify what you're expecting to have as a value internally
#### disabledStateNotSupported?: boolean
Set this to true, if component cannot be disabled.
If set to true, `ControlValueAccessor.setDisabledState()` function will not be checked for existance and correct behavior.
#### testRunnerType?: TestRunnerType
Test suite will automatically detect whether it's Jest or Jasmine environment. If needed, this can be overriden
#### excludeSteps?: CVATestSteps[];
List of steps to be excluded from execution. Cannot be specified along with `includeSteps`
#### includeSteps?: CVATestSteps[];
List of steps to be included in execution. Cannot be specified along with `excludeSteps`
### interface HostTemplate
#### hostComponent: Type
Wrapper, that hosts testing component. For example, to test `app-select-component` the following wrapper is used
```typescript
@Component({
selector: 'app-test-component-wrapper',
template: `
`,
})
class TestWrapperComponent {
@ViewChild('ctrl') ctrl: AppSelectComponent;
}
```#### getTestingComponent: (fixture: ComponentFixture) => T
Getter for the actual component that is being tested
Using the hostComponent above, the following function should be used:
`(fixture) => fixture.componentInstance.ctrl;`