带有子组件的angular2测试组件

问题描述 投票:1回答:1

我正在建立一个网站来维护一些设备。有一种反应形式来创建新设备,因为我需要在多个页面上,我使用设备FormGroup创建了一个专用组件。这样我就可以以其他形式重用它。该设备还有一个访问控制列表(acl),其中包含确定谁可以访问设备以及谁不能访问设备的规则。此acl还用于位置和用户。所以我制作了一个acl-form组件,以便能够重用这个表单。此acl表单填充formArray。

acl-form有2个额外的函数,prepareACL和sync。第一个函数用于为访问控制条目创建FormControl,并使用数据填充它们。第二个函数用于进行一些数据操作,因为表单中的数据不能按原样发送到服务器。

这一切都很好,但现在我想为表单编写一些测试。测试acl-form本身没问题,因为所有的依赖都可以很容易地被模拟。另一方面,设备形式要困难得多。当我编写测试时,如果我模拟了设备形式的所有依赖关系(正常)和acl-form的依赖关系,我只能让它工作。但由于acl-form已经自行测试,我不想打扰它。

我的问题是,如何模拟acl-form,所以我不需要提供所有依赖项,我如何监视prepareACL-function和sync-function,以确保它们被调用?

这是我的代码:

ACL-form.component.ts:

import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormArray, Validators } from '@angular/forms';

import { UserService } from '../../../services/user.service';

@Component({
    selector: 'acl-form',
    templateUrl: './acl-form.component.html'
})
export class ACLFormComponent implements OnInit {
    @Input()
    public group: FormArray;
    public users: Object[];

    public formErrors = {};

    build() {
        let group = this.fb.array([]);
        return group;
    }

    constructor(private fb: FormBuilder, private userService: UserService) { }

    ngOnInit() {
        this.userService.getUsers().subscribe(users => {
            this.users = users;
        });
    }

    buildACE() {
        let group = this.fb.group({
            guid: ['', []],
            user: ['', [Validators.required]],
            permission: ['', []],
            read: [{ value: '', disabled: true }, []],
            write: [{ value: '', disabled: true }, []],
            execute: [{ value: '', disabled: true }, []]
        });

        group.controls['user'].valueChanges.subscribe(guid => {
            if (guid && guid !== '') {
                group.controls['read'].enable();
                group.controls['write'].enable();
                group.controls['execute'].enable();
            } else {
                group.controls['read'].disable();
                group.controls['write'].disable();
                group.controls['execute'].disable();
            }
        });
        return group;
    }

    createACEs(count: Number) {
        const control = this.group;
        while (control.length) {
            control.removeAt(0);
        }
        for (let i = 0; i < count; i++) {
            control.push(this.buildACE());
        }
    }

    addACE() {
        this.group.push(this.buildACE());
    }

    removeACE(i: number) {
        this.group.removeAt(i);
    }

    encodePermission(ace) {
        let read = ace.read;
        let write = ace.write;
        let execute = ace.execute;

        let permission = 0;
        permission += read ? 4 : 0;
        permission += write ? 2 : 0;
        permission += execute ? 1 : 0;
        ace.permission = permission;
    }

    decodePermission(ace) {
        let permission = ace.permission;
        ace.read = (permission & 4) > 0;
        ace.write = (permission & 2) > 0;
        ace.execute = (permission & 1) > 0;
    }

    encodePermissions() {
        let acl = this.group.value;
        for (let i = 0; i < acl.length; i++) {
            this.encodePermission(acl[i]);
            // remove secondary fields to`enter code here` prevent api necking about it
            delete acl[i].read;
            delete acl[i].write;
            delete acl[i].execute;
            // remove guid to prevent api necking about it
            if (acl[i].guid === '') {
                delete acl[i].guid;
            }
        }
    }

    decodePermissions(acl) {
        for (let i = 0; i < acl.length; i++) {
            this.decodePermission(acl[i]);
        }
    }

    prepareACL(acl) {
        this.createACEs(acl.length);
        this.decodePermissions(acl);
    }

    // assign removedACE to the entity
    handleRemovedACE(entity) {
        let acl = this.group.value;
        if (entity.acl) {
            // remove acl
            acl = acl.filter(x => x.permission > 0);
            entity.removedACE = [...entity.acl].filter(x => acl.find(y => y.guid === x['guid']) === undefined)
                .map(x => x['guid']);

        } else {
            console.error('no acl entry found');
        }
        entity.acl = acl;
    }

    sync(entity) {
        this.encodePermissions();
        this.handleRemovedACE(entity);
    }
}

device.component.ts:

import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

import { EnvironmentService } from '../../../services/environment.service';
import { ResidenceService } from '../../../services/residence.service';
import { DeviceService } from '../../../services/device.service';
import { FormUtility } from '../../utility/form-utility';
import { macValidator } from '../../validators/global.validators';

import { ACLFormComponent } from '../acl-form/acl-form.component';
import { NetworkConfigFormComponent } from '../../components/network-config-form/network-config-form.component';

@Component({
    selector: 'device-form',
    templateUrl: './device-form.component.html'
})
export class DeviceFormComponent implements OnInit {

    @ViewChild(ACLFormComponent)
    public aclForm: ACLFormComponent;
    @ViewChild(NetworkConfigFormComponent)
    public networkConfigForm: NetworkConfigFormComponent;

    @Input()
    public group: FormGroup;
    public states: String[];
    public types: Object[];

    public environments: Object[];
    public residences: Object[];
    private residence: Object;
    public rooms: Object[];

    private onValueChanged: Function;
    public formErrors = {};

    private validationMessages = {
        mac: {
            required: 'mac address is required',
            mac: 'Ivalid mac address'
        }
    };

    constructor(
        private fb: FormBuilder,
        private deviceService: DeviceService,
        private environmentService: EnvironmentService,
        private residenceService: ResidenceService
    ) { }

    ngOnInit() {
        this.deviceService.getDeviceStates().subscribe(states => {
            this.states = states;
        });
        this.deviceService.getDeviceTypes().subscribe(types => {
            this.types = types;
        });
        this.environmentService.getEnvironmentList().subscribe(envs => {
            this.environments = envs;
        });
        this.onValueChanged = FormUtility.valueChangeGenerator(this.group, this.formErrors, this.validationMessages);
        this.group.valueChanges.subscribe(data => {
            this.onValueChanged(data);
        });

        this.group.controls['environment'].valueChanges.subscribe(data => {
            this.residenceService.getResidencesInEnvironmentList(data).subscribe(res => {
                this.group.controls['residence'].enable();
                this.residences = res;
                // add empty residence to make it possible only select environment
                let emptyRes = {name: '-', guid: ''};
                this.residences.push(emptyRes);
            });
        });
        this.group.controls['residence'].valueChanges.subscribe(data => {
            this.residenceService.getResidence(data).subscribe(res => {
                this.group.controls['room'].enable();
                this.residence = res;
                this.rooms = res.room;
                if (this.rooms) {
                    // add empty room to make it possible only select residence and environment
                    this.rooms.push({comment: '-', guid: ''});
                }
            });
        });
    }

    build() {
        return this.fb.group({
            mac: [
                '', [
                    Validators.required,
                    macValidator
                ]
            ],
            status: [
                '', []
            ],
            type: [
                '', []
            ],
            network: this.fb.group({}),
            environment: [
                '', [

                    Validators.required
                ],
            ],
            residence: [
                {
                    value: '',
                    disabled: true
                }, [
                ]
            ],
            room: [
                {
                    value: '',
                    disabled: true
                }, [
                ]
            ],
            acl: this.fb.group({})
        });
    }

    sync(device) {
        // encode befor getting group.value because encode is working on the formdata itself
        // encode permissions rwx => 1-7 and remove entries with permission === 0
        this.aclForm.sync(device);

        // handle parent_path
        let formdata = this.group.value;
        device.parent_path = ',' + formdata.environment + ',' + formdata.residence + ',' + formdata.room + ',';
    }

    patchValue(entity) {
        this.aclForm.prepareACL(entity.acl);
        // get parent_path objects
        if (entity.parent_path && entity.parent_path !== '') {
            let chunks = entity.parent_path.split(',');
            if (chunks.length === 5) {
                entity.environment = chunks[1];
                entity.residence = chunks[2];
                entity.room = chunks[3];
            }
        }
        this.group.patchValue(entity);
    }
}
angular2-forms angular2-components angular2-testing
1个回答
0
投票

我会使用ng-mocks的MockComponent

阅读我的示例代码显示了使用它的一种方法。您仍然需要导入主要组件,以便模拟器可以读取它的签名。

从自述文件: -


import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MockModule } from 'ng-mocks';
import { DependencyModule } from './dependency.module';
import { TestedComponent } from './tested.component';

describe('MockModule', () => {
  let fixture: ComponentFixture<TestedComponent>;
  let component: TestedComponent;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        TestedComponent,
      ],
      imports: [
        MockModule(DependencyModule),
      ],
    });

    fixture = TestBed.createComponent(TestedComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('renders nothing without any error', () => {
    expect(component).toBeTruthy();
  });
});

(这个问题可能是重复的;我似乎记得我在这里学过ng-mocks;但我现在没有看到另一个问题)

© www.soinside.com 2019 - 2024. All rights reserved.