ERROR: 'Error during cleanup of component', Object{component: FilterComponent

ERROR: 'Error during cleanup of component', Object{component: FilterComponent

我正在对 FilterComponent 进行测试,但每次我 运行 命令时都会收到此错误:

错误

'Error during cleanup of component', Object{component: FilterComponent{lang:
 Language{cookie: ..., get: ..., getLocale: ..., dict: ...}, api: Api{http:
 ..., router: ..., cookie: ..., send: ..., setUrl: ...}, filterService: 
FilterService{update: ..., filter: ...}, searchbar: SearchbarService{}, 
localStorageCache: LocalStorageCache{api: ...}, cookie: CookieService{document: 
..., optionsProvider: ..., cookieWriterService: ..., options: ...}, formatter: 
NgbDateISOParserFormatter{}, calendar: NgbCalendarGregorian{}, onFilter: 
EventEmitter{_isScalar: ..., observers: ..., closed: ..., isStopped: ..., hasError:
 ..., thrownError: ..., __isAsync: ...}, category: '', search: Subject{_isScalar: 
..., observers: ..., closed: ..., isStopped: ..., hasError: ..., thrownError: ...}, 
selectedElems: [], hoveredDate: null, filterDef: Object{tweets: ..., news: ..., events:
 ..., dashboard: ..., dashboardHashtags: ..., people: ..., reports: ..., editorials: 
..., verdicts: ..., organizations: ..., results: ..., _dashboard: ...}, componentDict: 
Object{author: ..., people: ..., organizations: ..., tag: ..., tactics: ..., court: 
..., pillars: ..., hashtags: ..., magistrates: ..., website: ...}, yearRange: [..., ...],
 getPillars: () => { ... }, dateFromNgbDate: ngbDate => { ... }, isEmpty: () => { ... }, 
getElements: () => { ... }, addItem: item => { ... }, compareIds: (el1, el2) => { ... },
 clearItem: item => { ... }, fillMultiSelect: () => { ... }, relevantChange: $ev => { 
... }, user: Object{id: ..., username: ..., name: ..., role: ..., id_client: ..., 
language: ..., filter: ..., services: ...}, __ngContext__: [..., ..., ..., ..., ..., 
..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., 
..., ..., ..., ..., ..., ..., ..., ..., ...], filterObj: Object{date_range: ..., 
relevant: ...}}, stacktrace: TypeError: Cannot read properties of undefined (reading 'unsubscribe')
TypeError: Cannot read properties of undefined (reading 'unsubscribe')

我已经通过导入组件使用的所有内容配置了规范文件。

filterComponent

import { Component, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
import { Language } from '../../services/language.service';
import { Observable, concat, of, Subject, Subscription } from 'rxjs';
import { distinctUntilChanged, switchMap, filter } from 'rxjs/operators';
import { CookieService } from 'ngx-cookie';

import { Api } from '../../services/api.service';
import { FilterService } from '../../services/filter.service';
import { LocalStorageCache } from '../../services/local-storage-cache.service';
import * as _ from 'lodash';
import { SearchbarService } from '../../services/searchbar.service';
import { NgbDate, NgbCalendar, NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap';

export interface MultiSelectItem {
    id: number,
    name: string,
    type: string
}

@Component({
    selector: 'is-filter',
    templateUrl: './filter.component.html',
    styleUrls: ['./filter.component.scss']
})
export class FilterComponent implements OnInit, OnDestroy {
    @Input() type: string;
    @Input() filterObj: any;

    @Output() onFilter: EventEmitter<any> = new EventEmitter<any>();

    category: string = '';
    //Escucha cambios en el campo de texto
    search: Subject<string> = new Subject<string>();
    selectedElems: MultiSelectItem[] = [];
    hoveredDate: NgbDate | null = null;


    filterDef: object = {
        dashboard: ['date_range', 'pillars'],
    }

    componentDict = {
        author: { component: 'Agents' },
    }
    user: any;
    yearRange = [2020, 2021];

    constructor(
        public lang: Language,
        private api: Api,
        public filterService: FilterService,
        public searchbar: SearchbarService,
        public localStorageCache: LocalStorageCache,
        private cookie: CookieService,
        public formatter: NgbDateParserFormatter,
        private calendar: NgbCalendar
    ) {
        this.user = JSON.parse(this.cookie.get('user'));
    }

    updateFilterSubscription: Subscription;
    filterSubscription: Subscription;

    ngOnDestroy() {
        this.updateFilterSubscription.unsubscribe();
        this.filterSubscription.unsubscribe();
    }

    ngOnInit() {
        this.clearFilter();
        this.filter();
        switch (this.type) {
            case 'male':
                this.category = 'mens';
                break;
            case 'female':
                this.category = 'woman';
                break;
        }
    }

    pillars: MultiSelectItem[];
    getPillars = async (): Promise<void> => {

        if (localStorage.getItem('pillars')) {
            this.pillars = JSON.parse(localStorage.getItem('pillars'));
            return;
        }

        let items = await this.api.send('Categories', 'get', filter ? { filter: filter } : {}).then((res: { data: any[], count: number }) => {
            return res.data.map(el => {
                return { id: el.id, name: el.name, type: 'Categories' }
            });
        });
        localStorage.setItem('pillars', JSON.stringify(items));

        this.pillars = items;
    }

    private ngbDateFromDate(date: Date) {
        return NgbDate.from({ year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() });
    }

    private dateFromNgbDate = (ngbDate: { year: number, month: number, day: number }) => new Date(ngbDate.year, ngbDate.month - 1, ngbDate.day);

    clearFilter() {
        this.filterObj = {};
        this.selectedElems = [];
        _.each(this.filterDef[this.type], filter => {
            switch (filter) {
                case 'date_range':
                    let startDate = new Date();
                    let endDate = new Date();
                    startDate.setDate(startDate.getDate() - 14);
                    endDate.setHours(23);
                    endDate.setMinutes(59, 59);
                    this.filterObj[filter] = {
                        start_date: this.ngbDateFromDate(startDate),
                        end_date: this.ngbDateFromDate(endDate)
                    }
                    break;

                case 'relevant':
                    this.filterObj[filter] = true;
                    break;
                case 'pillars':
                    this.category = 'pillars';
                    break;

                case 'country':
                default:
                    break;
            }

        })
        if (typeof this.user.filter == 'string') {
            try {
                this.filterObj = { ...this.filterObj, ...JSON.parse(this.user.filter) };
            } catch {
                console.error("malformed_user_filter", this.user.filter);
            }
        }

    }

    filter() {

        if (Object.keys(this.filterObj).indexOf('pillars') !== -1) {
            if (typeof this.filterObj['pillars'] !== "object") {
                this.onFilter.emit({ ...this.filterObj, pillars: this.pillars.filter(el => el.id == this.filterObj['pillars']) });
            } else {
                this.onFilter.emit({ ...this.filterObj });
            }
        } else {
            this.onFilter.emit({ ...this.filterObj });
        }
    }

    isEmpty = () => Object.keys(this.filterObj).filter(key => ['date_range', 'relevant'].indexOf(key) === -1).length === 0;


    getElements = async (): Promise<MultiSelectItem[]> => {
        let component = this.componentDict[this.category].component;
        let filter = this.componentDict[this.category].filter;
        let category = this.category == 'author' ? 'agent' : this.category;
        if (localStorage.getItem(category)) return JSON.parse(localStorage.getItem(category))

        let items = await this.api.send(component, 'get', filter ? { filter: filter } : {}).then((res: { data: any[], count: number }) => {
            return res.data.map(el => {
                return { id: el.id, name: el.name, type: component }
            });
        });
        localStorage.setItem(category, JSON.stringify(items));

        return items;
    }

    multiselectEls = concat(
        of([]),
        this.search.pipe(
            distinctUntilChanged(),
            filter(() => true),
            switchMap((term: string) => {
                return this.localStorageCache.get(this.category).then(elements => elements.filter(el => el['name'] && term ? el['name'].toLowerCase().indexOf(term.toLowerCase()) !== -1 : false))
            }),
        )
    ) as Observable<MultiSelectItem[]>;
    validateInput(currentValue: NgbDate | null, input: string): NgbDate | null {
        const parsed = this.formatter.parse(input);
        return parsed && this.calendar.isValid(NgbDate.from(parsed)) ? NgbDate.from(parsed) : currentValue;
    }

    onDateSelection(date: NgbDate) {

        let dateRange = this.filterObj.date_range;

        if (this.filterObj.relevant === false) {
            this.filterObj.date_range.start_date = date;
            this.filterObj.date_range.end_date = date;
            return;
        }

        if (!dateRange.start_date && !dateRange.end_date) {
            this.filterObj.date_range.start_date = date;
        }

        if (!!dateRange.start_date && (!!dateRange.end_date || date.before(dateRange.start_date))) {
            this.filterObj.date_range = {
                start_date: date,
                end_date: null
            }
            return;
        }

        if (!!dateRange.start_date && (date.after(dateRange.start_date) || date.equals(dateRange.start_date))) {
            this.filterObj.date_range.end_date = date;
            let offset = !this.filterObj.relevant && typeof this.filterObj.relevant !== 'undefined' ? 0 : 31;

            let d = this.dateFromNgbDate(date);
            d.setDate(d.getDate() - offset);
            let offsetDate = this.ngbDateFromDate(d);
            dateRange.start_date = offsetDate.after(dateRange.start_date) ? offsetDate : dateRange.start_date;
            return;
        }

    }
}

我在 ts 文件中导入并配置了所有必要的东西,但是每次我尝试启动 ng 测试命令时配置都失败。

规格:

import { FilterComponent } from "./filter.component";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from "@angular/core";
import { RouterTestingModule } from '@angular/router/testing';
import { CookieModule, CookieService } from 'ngx-cookie';
import { Api } from '../../services/api.service';
import { MultiSelectItem } from './filter.component';
import { NgbDate } from '@ng-bootstrap/ng-bootstrap';
import { FilterService } from '../../services/filter.service';

var user = {
  "id": 1,
  "username": "test",
}

var item:MultiSelectItem = {
  id: 1,
  name: 'item1',
  type: 'text'
};

describe('filter Component', () => {
  let component: FilterComponent;
  let fixture: ComponentFixture<FilterComponent>;
  let cookieService: CookieService;
  let apiService: Api;
  let filterService: FilterService;

  beforeEach(() => {
    TestBed.configureTestingModule({
        imports: [
            HttpClientTestingModule,
            RouterTestingModule,
            CookieModule.forRoot()
        ],
        declarations: [
            FilterComponent,
        ],
        providers: [
          Api,
          CookieService,
          FilterService
        ],
        schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
    }).compileComponents();
  });

  beforeEach( () => {
    cookieService = TestBed.inject(CookieService);
    cookieService.put('user', JSON.stringify(user));
    fixture = TestBed.createComponent(FilterComponent);
    component = fixture.componentInstance;
    apiService = TestBed.inject(Api);
    filterService = TestBed.inject(FilterService);
    component.ngOnInit();
  });

  afterEach(() => {
    component = null;
    fixture = null;
    cookieService.removeAll();
    apiService = null;
    filterService = null;
    cookieService = null;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  describe('ngOnInit()', () => {
    it('Should call clearFilter()', () => {
      let spy1 = spyOn(component, 'clearFilter');
      component.ngOnInit();
      expect(spy1).toHaveBeenCalled();
    });
    it('Should call filter()', () => {
      let spy1 = spyOn(component, 'filter');
      component.ngOnInit();
      expect(spy1).toHaveBeenCalled();
    });
    it('Should category to be equal magistrates', () => {
      component.type = 'verdicts';
      component.ngOnInit();
      expect(component.category).toEqual('magistrates');
    });
    it('Should category to be equal hashtags', () => {
      component.type = 'dashboardHashtags';
      component.ngOnInit();
      expect(component.category).toEqual('hashtags');
    });
    it('Should category to be equal people', () => {
      component.type = '';
      component.ngOnInit();
      expect(component.category).toEqual('people');
    });
    it('Should call clearFilter() from ngOnInit next subscribe', () => {
      let spy1 = spyOn(component, 'clearFilter');
      component.ngOnInit();
      expect(spy1).toHaveBeenCalled();
    });
  });
});

在您的测试中,您的订阅变量永远不会被分配,这会在 ngOnDestroy 函数中抛出您的错误。 要解决此问题,您可以像这样立即定义您的订阅:

updateFilterSubscription = new Subscription();

然后使用

添加您的订阅
this.updateFilterSubscription.add(<your_subscription>);

或者在您的 ngOnDestroy 函数中,您可以检查订阅是否存在,例如? 运算符,或者 if 如果您喜欢这种方式,然后像这样取消订阅

this.updateFilterSubscription?.unsubscribe();