From: Stephan Müller Date: Thu, 2 Aug 2018 10:50:24 +0000 (+0200) Subject: mgr/dashboard: Add a unit test form helper class X-Git-Tag: v14.1.0~1047^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=0532fce13e86c0fb512571b88d03eb15dd64f7db;p=ceph.git mgr/dashboard: Add a unit test form helper class This class helps testing forms, most methods were outsourced from the pool form test suite. Fixes: https://tracker.ceph.com/issues/36467 Signed-off-by: Stephan Müller --- diff --git a/src/pybind/mgr/dashboard/HACKING.rst b/src/pybind/mgr/dashboard/HACKING.rst index 83f7ff21092..0e7983928c3 100644 --- a/src/pybind/mgr/dashboard/HACKING.rst +++ b/src/pybind/mgr/dashboard/HACKING.rst @@ -79,6 +79,34 @@ We added 2 npm scripts to help run these tools: - ``npm run lint``, will check frontend files against all linters - ``npm run fix``, will try to fix all the detected linting errors +Writing Unit Tests +~~~~~~~~~~~~~~~~~~ + +To write unit tests most efficient we have a small collection of tools, +we use within test suites. + +Those tools can be found under +``src/pybind/mgr/dashboard/frontend/src/testing/``, especially take +a look at ``unit-test-helper.ts``. + +There you will be able to find: + +``configureTestBed`` that replaces the initial ``TestBed`` +methods. It takes the same arguments as ``TestBed.configureTestingModule``. +Using it will run your tests a lot faster in development, as it doesn't +recreate everything from scratch on every test. To use the default behaviour +pass ``true`` as the second argument. + +``PermissionHelper`` to help determine if +the correct actions are shown based on the current permissions and selection +in a list. + +``FormHelper`` which makes testing a form a lot easier +with a few simple methods. It allows you to set a control or multiple +controls, expect if a control is valid or has an error or just do both with +one method. Additional you can expect a template element or multiple elements +to be visible in the rendered template. + Running Unit Tests ~~~~~~~~~~~~~~~~~~ diff --git a/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts b/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts index 8e1cc2b461f..bd396d34462 100644 --- a/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts +++ b/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts @@ -1,8 +1,11 @@ -import { async, TestBed } from '@angular/core/testing'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { AbstractControl } from '@angular/forms'; +import { By } from '@angular/platform-browser'; import * as _ from 'lodash'; import { TableActionsComponent } from '../app/shared/datatable/table-actions/table-actions.component'; +import { CdFormGroup } from '../app/shared/forms/cd-form-group'; import { Permission } from '../app/shared/models/permissions'; import { _DEV_ } from '../unit-test-configuration'; @@ -89,3 +92,90 @@ export class PermissionHelper { this.tableActions.selection.update(); } } + +export class FormHelper { + form: CdFormGroup; + + constructor(form: CdFormGroup) { + this.form = form; + } + + /** + * Changes multiple values in multiple controls + */ + setMultipleValues(values: { [controlName: string]: any }, markAsDirty?: boolean) { + Object.keys(values).forEach((key) => { + this.setValue(key, values[key], markAsDirty); + }); + } + + /** + * Changes the value of a control + */ + setValue(control: AbstractControl | string, value: any, markAsDirty?: boolean): AbstractControl { + control = this.getControl(control); + if (markAsDirty) { + control.markAsDirty(); + } + control.setValue(value); + return control; + } + + private getControl(control: AbstractControl | string): AbstractControl { + if (typeof control === 'string') { + return this.form.get(control); + } + return control; + } + + /** + * Change the value of the control and expect the control to be valid afterwards. + */ + expectValidChange(control: AbstractControl | string, value: any, markAsDirty?: boolean) { + this.expectValid(this.setValue(control, value, markAsDirty)); + } + + /** + * Expect that the given control is valid. + */ + expectValid(control: AbstractControl | string) { + // 'isValid' would be false for disabled controls + expect(this.getControl(control).errors).toBe(null); + } + + /** + * Change the value of the control and expect a specific error. + */ + expectErrorChange( + control: AbstractControl | string, + value: any, + error: string, + markAsDirty?: boolean + ) { + this.expectError(this.setValue(control, value, markAsDirty), error); + } + + /** + * Expect a specific error for the given control. + */ + expectError(control: AbstractControl | string, error: string) { + expect(this.getControl(control).hasError(error)).toBeTruthy(); + } + + /** + * Expect a list of id elements to be visible or not. + */ + expectIdElementsVisible(fixture: ComponentFixture, ids: string[], visibility: boolean) { + fixture.detectChanges(); + ids.forEach((css) => { + this.expectElementVisible(fixture, `#${css}`, visibility); + }); + } + + /** + * Expect a specific element in fixture to be visible or not. + */ + expectElementVisible(fixture: ComponentFixture, css: string, visibility: boolean) { + expect(Boolean(fixture.debugElement.query(By.css(css)))).toBe(visibility); + } +}