From 751282a1bb748083b0f57b5de396541cfac6b84a Mon Sep 17 00:00:00 2001
From: Tifenn Guillas <tifenn.guillas@lam.fr>
Date: Thu, 30 Sep 2021 15:57:07 +0200
Subject: [PATCH] Tests on search containers => DONE

---
 .../abstract-search.component.spec.ts         |  61 ++++++++
 .../containers/abstract-search.component.ts   |  15 +-
 .../search/containers/criteria.component.html |  10 +-
 .../containers/criteria.component.spec.ts     | 125 +++++++++++++++
 .../search/containers/criteria.component.ts   |  25 ++-
 .../search/containers/dataset.component.html  |   4 +-
 .../containers/dataset.component.spec.ts      | 142 +++++++++++-------
 .../search/containers/dataset.component.ts    |   4 +-
 .../containers/result.component.spec.ts       |   2 +-
 9 files changed, 314 insertions(+), 74 deletions(-)
 create mode 100644 client/src/app/instance/search/containers/abstract-search.component.spec.ts
 create mode 100644 client/src/app/instance/search/containers/criteria.component.spec.ts

diff --git a/client/src/app/instance/search/containers/abstract-search.component.spec.ts b/client/src/app/instance/search/containers/abstract-search.component.spec.ts
new file mode 100644
index 00000000..60b38e48
--- /dev/null
+++ b/client/src/app/instance/search/containers/abstract-search.component.spec.ts
@@ -0,0 +1,61 @@
+import { Component } from '@angular/core';
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { MockStore, provideMockStore } from '@ngrx/store/testing';
+import { of } from 'rxjs';
+
+import { AbstractSearchComponent } from './abstract-search.component';
+import * as searchActions from '../../store/actions/search.actions';
+
+describe('[Instance][Search][Container] AbstractSearchComponent', () => {
+    @Component({
+        selector: 'app-fake',
+        template: ''
+    })
+    class MyFakeComponent extends AbstractSearchComponent {
+        ngOnInit() {
+            super.ngOnInit();
+        }
+
+        ngOnDestroy() {
+            super.ngOnDestroy();
+        }
+    }
+
+    let component: MyFakeComponent;
+    let fixture: ComponentFixture<MyFakeComponent>;
+    let store: MockStore;
+
+    beforeEach(waitForAsync(() => {
+        TestBed.configureTestingModule({
+            declarations: [MyFakeComponent],
+            providers: [provideMockStore({ })]
+        });
+        fixture = TestBed.createComponent(MyFakeComponent);
+        component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
+    }));
+
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+
+    it('should execute ngOnInit lifecycle', (done) => {
+        component.attributeListIsLoaded = of(true);
+        const spy = jest.spyOn(store, 'dispatch');
+        component.ngOnInit();
+        Promise.resolve(null).then(function() {
+            expect(spy).toHaveBeenCalledTimes(2);
+            expect(spy).toHaveBeenCalledWith(searchActions.initSearch());
+            expect(spy).toHaveBeenCalledWith(searchActions.loadDefaultFormParameters());
+            done();
+        });
+    });
+
+    it('#ngOnDestroy() should unsubscribe from attributeListIsLoadedSubscription', () => {
+        component.attributeListIsLoadedSubscription = of().subscribe();
+        const spy = jest.spyOn(component.attributeListIsLoadedSubscription, 'unsubscribe');
+        component.ngOnDestroy();
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+});
diff --git a/client/src/app/instance/search/containers/abstract-search.component.ts b/client/src/app/instance/search/containers/abstract-search.component.ts
index 54e40851..afe55c5d 100644
--- a/client/src/app/instance/search/containers/abstract-search.component.ts
+++ b/client/src/app/instance/search/containers/abstract-search.component.ts
@@ -16,6 +16,14 @@ import * as searchActions from '../../store/actions/search.actions';
 import * as searchSelector from '../../store/selectors/search.selector';
 import * as coneSearchSelector from '../../store/selectors/cone-search.selector';
 
+/**
+ * @abstract
+ * @class
+ * @classdesc Abstract search container.
+ *
+ * @implements OnInit
+ * @implements OnDestroy
+ */
 @Directive()
 export abstract class AbstractSearchComponent implements OnInit, OnDestroy {
     public datasetSelected: Observable<string>;
@@ -41,8 +49,7 @@ export abstract class AbstractSearchComponent implements OnInit, OnDestroy {
     public outputList: Observable<number[]>;
     public queryParams: Observable<SearchQueryParams>;
     public coneSearch: Observable<ConeSearch>;
-
-    private attributeListIsLoadedSubscription: Subscription;
+    public attributeListIsLoadedSubscription: Subscription;
 
     constructor(protected store: Store<{ }>) {
         this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute);
@@ -70,7 +77,7 @@ export abstract class AbstractSearchComponent implements OnInit, OnDestroy {
         this.coneSearch = this.store.select(coneSearchSelector.selectConeSearch);
     }
 
-    ngOnInit() {
+    ngOnInit(): void {
         // Create a micro task that is processed after the current synchronous code
         // This micro task prevent the expression has changed after view init error
         Promise.resolve(null).then(() => this.store.dispatch(searchActions.initSearch()));
@@ -81,7 +88,7 @@ export abstract class AbstractSearchComponent implements OnInit, OnDestroy {
         });
     }
 
-    ngOnDestroy() {
+    ngOnDestroy(): void {
         if (this.attributeListIsLoadedSubscription) this.attributeListIsLoadedSubscription.unsubscribe();
     }
 }
diff --git a/client/src/app/instance/search/containers/criteria.component.html b/client/src/app/instance/search/containers/criteria.component.html
index c2beb345..0afddb0a 100644
--- a/client/src/app/instance/search/containers/criteria.component.html
+++ b/client/src/app/instance/search/containers/criteria.component.html
@@ -18,11 +18,11 @@
             (deleteConeSearch)="deleteConeSearch()"
             (retrieveCoordinates)="retrieveCoordinates($event)">
         </app-cone-search-tab>
-        <app-criteria-tabs 
+        <app-criteria-tabs
             [attributeList]="attributeList | async | sortByCriteriaDisplay"
             [criteriaFamilyList]="criteriaFamilyList | async"
             [criteriaList]="criteriaList | async"
-            (addCriterion)="addCriterion($event)" 
+            (addCriterion)="addCriterion($event)"
             (deleteCriterion)="deleteCriterion($event)">
         </app-criteria-tabs>
     </div>
@@ -32,7 +32,7 @@
             [currentStep]="currentStep | async"
             [datasetSelected]="datasetSelected | async"
             [datasetList]="datasetList | async"
-            [attributeList]="attributeList | async | SortByOutputDisplayPipe"
+            [attributeList]="attributeList | async | sortByCriteriaDisplay"
             [criteriaFamilyList]="criteriaFamilyList | async"
             [outputFamilyList]="outputFamilyList | async"
             [outputCategoryList]="outputCategoryList | async"
@@ -45,8 +45,8 @@
 </div>
 <div class="row mt-5 justify-content-between">
     <div class="col">
-        <a routerLink="/instance/{{ instanceSelected | async }}/search/dataset/{{ datasetSelected | async }}" 
-            [queryParams]="queryParams | async" 
+        <a routerLink="/instance/{{ instanceSelected | async }}/search/dataset/{{ datasetSelected | async }}"
+            [queryParams]="queryParams | async"
             class="btn btn-outline-secondary">
             <span class="fas fa-arrow-left"></span> Dataset
         </a>
diff --git a/client/src/app/instance/search/containers/criteria.component.spec.ts b/client/src/app/instance/search/containers/criteria.component.spec.ts
new file mode 100644
index 00000000..a1c1e2eb
--- /dev/null
+++ b/client/src/app/instance/search/containers/criteria.component.spec.ts
@@ -0,0 +1,125 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import { Component, Input } from '@angular/core';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { MockStore, provideMockStore } from '@ngrx/store/testing';
+
+import { CriteriaComponent } from './criteria.component';
+import { Attribute, CriteriaFamily, Dataset, OutputCategory, OutputFamily } from '../../../metamodel/models';
+import { ConeSearch, Criterion, Resolver, SearchQueryParams } from '../../store/models';
+import { SortByCriteriaDisplayPipe } from '../pipes/sort-by-criteria-display.pipe';
+import * as searchActions from '../../store/actions/search.actions';
+import { AbstractSearchComponent } from './abstract-search.component';
+import * as coneSearchActions from '../../store/actions/cone-search.actions';
+
+describe('[Instance][Search][Container] CriteriaComponent', () => {
+    @Component({ selector: 'app-spinner', template: '' })
+    class SpinnerStubComponent { }
+
+    @Component({ selector: 'app-cone-search-tab', template: '' })
+    class ConeSearchStubComponent {
+        @Input() datasetSelected: string;
+        @Input() datasetList: Dataset[];
+        @Input() coneSearch: ConeSearch;
+        @Input() resolver: Resolver;
+        @Input() resolverIsLoading: boolean;
+        @Input() resolverIsLoaded: boolean;
+    }
+
+    @Component({ selector: 'app-criteria-tabs', template: '' })
+    class CriteriaTabsStubComponent {
+        @Input() attributeList: Attribute[];
+        @Input() criteriaFamilyList: CriteriaFamily[];
+        @Input() criteriaList: Criterion[];
+    }
+
+    @Component({ selector: 'app-summary', template: '' })
+    class SummaryStubComponent {
+        @Input() currentStep: string;
+        @Input() datasetSelected: string;
+        @Input() datasetList: Dataset[];
+        @Input() attributeList: Attribute[];
+        @Input() criteriaFamilyList: CriteriaFamily[];
+        @Input() outputFamilyList: OutputFamily[];
+        @Input() outputCategoryList: OutputCategory[];
+        @Input() criteriaList: Criterion[];
+        @Input() outputList: number[];
+        @Input() queryParams: SearchQueryParams;
+        @Input() coneSearch: ConeSearch;
+    }
+
+    let component: CriteriaComponent;
+    let fixture: ComponentFixture<CriteriaComponent>;
+    let store: MockStore;
+
+    beforeEach(waitForAsync(() => {
+        TestBed.configureTestingModule({
+            imports: [RouterTestingModule],
+            declarations: [
+                CriteriaComponent,
+                SpinnerStubComponent,
+                ConeSearchStubComponent,
+                CriteriaTabsStubComponent,
+                SummaryStubComponent,
+                SortByCriteriaDisplayPipe
+            ],
+            providers: [provideMockStore({ })]
+        });
+        fixture = TestBed.createComponent(CriteriaComponent);
+        component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
+    }));
+
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+
+    it('should execute ngOnInit lifecycle', (done) => {
+        const spy = jest.spyOn(store, 'dispatch');
+        jest.spyOn(AbstractSearchComponent.prototype, 'ngOnInit').mockReturnThis();
+        component.ngOnInit();
+        Promise.resolve(null).then(function() {
+            expect(spy).toHaveBeenCalledTimes(2);
+            expect(spy).toHaveBeenCalledWith(searchActions.changeStep({ step: 'criteria' }));
+            expect(spy).toHaveBeenCalledWith(searchActions.checkCriteria());
+            done();
+        });
+    });
+
+    it('#addCriterion() should dispatch addCriterion action', () => {
+        const criterion: Criterion = { id: 1, type: 'field' };
+        const spy = jest.spyOn(store, 'dispatch');
+        component.addCriterion(criterion);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(searchActions.addCriterion({ criterion }));
+    });
+
+    it('#deleteCriterion() should dispatch deleteCriterion action', () => {
+        const spy = jest.spyOn(store, 'dispatch');
+        component.deleteCriterion(1);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(searchActions.deleteCriterion({ idCriterion: 1 }));
+    });
+
+    it('#addConeSearch() should dispatch addConeSearch action', () => {
+        const coneSearch: ConeSearch = { ra: 1, dec: 2, radius: 3 };
+        const spy = jest.spyOn(store, 'dispatch');
+        component.addConeSearch(coneSearch);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(coneSearchActions.addConeSearch({ coneSearch }));
+    });
+
+    it('#deleteConeSearch() should dispatch deleteConeSearch action', () => {
+        const spy = jest.spyOn(store, 'dispatch');
+        component.deleteConeSearch();
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(coneSearchActions.deleteConeSearch());
+    });
+
+    it('#retrieveCoordinates() should dispatch deleteConeSearch action', () => {
+        const spy = jest.spyOn(store, 'dispatch');
+        component.retrieveCoordinates('myObject');
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(coneSearchActions.retrieveCoordinates({ name: 'myObject' }));
+    });
+});
diff --git a/client/src/app/instance/search/containers/criteria.component.ts b/client/src/app/instance/search/containers/criteria.component.ts
index b03d239d..2530489e 100644
--- a/client/src/app/instance/search/containers/criteria.component.ts
+++ b/client/src/app/instance/search/containers/criteria.component.ts
@@ -18,14 +18,14 @@ import * as searchActions from '../../store/actions/search.actions';
 import * as coneSearchActions from '../../store/actions/cone-search.actions';
 import * as coneSearchSelector from '../../store/selectors/cone-search.selector';
 
-@Component({
-    selector: 'app-criteria',
-    templateUrl: 'criteria.component.html'
-})
 /**
  * @class
  * @classdesc Search criteria container.
  */
+@Component({
+    selector: 'app-criteria',
+    templateUrl: 'criteria.component.html'
+})
 export class CriteriaComponent extends AbstractSearchComponent {
     public resolver: Observable<Resolver>;
     public resolverIsLoading: Observable<boolean>;
@@ -38,7 +38,9 @@ export class CriteriaComponent extends AbstractSearchComponent {
         this.resolverIsLoaded = this.store.select(coneSearchSelector.selectResolverIsLoaded);
     }
 
-    ngOnInit() {
+    ngOnInit(): void {
+        // Create a micro task that is processed after the current synchronous code
+        // This micro task prevent the expression has changed after view init error
         Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'criteria' })));
         Promise.resolve(null).then(() => this.store.dispatch(searchActions.checkCriteria()));
         super.ngOnInit();
@@ -62,14 +64,27 @@ export class CriteriaComponent extends AbstractSearchComponent {
         this.store.dispatch(searchActions.deleteCriterion({ idCriterion }));
     }
 
+    /**
+     * Dispatches action to add cone search.
+     *
+     * @param  {ConeSearch} coneSearch - The cone search.
+     */
     addConeSearch(coneSearch: ConeSearch): void {
         this.store.dispatch(coneSearchActions.addConeSearch({ coneSearch }));
     }
 
+    /**
+     * Dispatches action to remove the cone search.
+     */
     deleteConeSearch(): void {
         this.store.dispatch(coneSearchActions.deleteConeSearch());
     }
 
+    /**
+     * Dispatches action to retrieve object coordinates.
+     *
+     * @param  {string} name - The object name.
+     */
     retrieveCoordinates(name: string): void {
         this.store.dispatch(coneSearchActions.retrieveCoordinates({ name }));
     }
diff --git a/client/src/app/instance/search/containers/dataset.component.html b/client/src/app/instance/search/containers/dataset.component.html
index 032a3fce..c1a85d26 100644
--- a/client/src/app/instance/search/containers/dataset.component.html
+++ b/client/src/app/instance/search/containers/dataset.component.html
@@ -4,7 +4,7 @@
 </app-spinner>
 
 <div *ngIf="(datasetFamilyListIsLoaded | async)
-    && (datasetListIsLoaded | async) 
+    && (datasetListIsLoaded | async)
     && (surveyListIsLoaded | async)" class="row mt-4">
     <ng-container *ngIf="(datasetList | async).length === 0">
         <div class="col-12 lead text-center">
@@ -30,7 +30,7 @@
                 [currentStep]="currentStep | async"
                 [datasetSelected]="datasetSelected | async"
                 [datasetList]="datasetList | async"
-                [attributeList]="attributeList | async | SortByOutputDisplayPipe"
+                [attributeList]="attributeList | async | sortByOutputDisplay"
                 [criteriaFamilyList]="criteriaFamilyList | async"
                 [outputFamilyList]="outputFamilyList | async"
                 [outputCategoryList]="outputCategoryList | async"
diff --git a/client/src/app/instance/search/containers/dataset.component.spec.ts b/client/src/app/instance/search/containers/dataset.component.spec.ts
index aba154ca..0bd43368 100644
--- a/client/src/app/instance/search/containers/dataset.component.spec.ts
+++ b/client/src/app/instance/search/containers/dataset.component.spec.ts
@@ -1,65 +1,95 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import { Component, Input } from '@angular/core';
+import { RouterTestingModule } from '@angular/router/testing';
 
-import { Component} from '@angular/core';
+import { MockStore, provideMockStore } from '@ngrx/store/testing';
+import { of } from 'rxjs';
 
-import { Store } from '@ngrx/store';
-import { Observable, Subscription } from 'rxjs';
-
-import { AbstractSearchComponent } from './abstract-search.component';
-import { Survey, DatasetFamily } from 'src/app/metamodel/models';
+import { DatasetComponent } from './dataset.component';
+import {
+    Attribute,
+    CriteriaFamily,
+    Dataset,
+    DatasetFamily,
+    OutputCategory,
+    OutputFamily,
+    Survey
+} from '../../../metamodel/models';
+import { ConeSearch, Criterion, SearchQueryParams } from '../../store/models';
+import { SortByOutputDisplayPipe } from '../pipes/sort-by-output-display.pipe';
 import * as searchActions from '../../store/actions/search.actions';
-import * as authSelector from 'src/app/auth/auth.selector';
-import * as datasetFamilySelector from 'src/app/metamodel/selectors/dataset-family.selector';
-import * as surveySelector from 'src/app/metamodel/selectors/survey.selector';
+import { AbstractSearchComponent } from './abstract-search.component';
 
-/**
- * @class
- * @classdesc Search dataset container.
- */
-@Component({
-    selector: 'app-dataset',
-    templateUrl: 'dataset.component.html'
-})
-export class DatasetComponent extends AbstractSearchComponent {
-    public isAuthenticated: Observable<boolean>;
-    public datasetFamilyListIsLoading: Observable<boolean>;
-    public datasetFamilyListIsLoaded: Observable<boolean>;
-    public datasetFamilyList: Observable<DatasetFamily[]>;
-    public surveyListIsLoading: Observable<boolean>;
-    public surveyListIsLoaded: Observable<boolean>;
-    public surveyList: Observable<Survey[]>;
-    public datasetSelectedSubscription: Subscription;
+describe('[Instance][Search][Container] DatasetComponent', () => {
+    @Component({ selector: 'app-spinner', template: '' })
+    class SpinnerStubComponent { }
 
-    constructor(protected store: Store<{ }>) {
-        super(store);
-        this.isAuthenticated = store.select(authSelector.selectIsAuthenticated);
-        this.datasetFamilyListIsLoading = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoading);
-        this.datasetFamilyListIsLoaded = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoaded);
-        this.datasetFamilyList = store.select(datasetFamilySelector.selectAllDatasetFamilies);
-        this.surveyListIsLoading = store.select(surveySelector.selectSurveyListIsLoading);
-        this.surveyListIsLoaded = store.select(surveySelector.selectSurveyListIsLoaded);
-        this.surveyList = store.select(surveySelector.selectAllSurveys);
+    @Component({ selector: 'app-dataset-tabs', template: '' })
+    class DatasetTabsStubComponent {
+        @Input() surveyList: Survey[];
+        @Input() datasetList: Dataset[];
+        @Input() datasetFamilyList: DatasetFamily[];
+        @Input() instanceSelected: string;
+        @Input() datasetSelected: string;
     }
 
-    ngOnInit(): void {
-        Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'dataset' })));
-        this.datasetSelectedSubscription = this.datasetSelected.subscribe(datasetSelected => {
-            if (datasetSelected) {
-                Promise.resolve(null).then(() => this.store.dispatch(searchActions.initSearch()));
-            }
-        })
-        super.ngOnInit();
+    @Component({ selector: 'app-summary', template: '' })
+    class SummaryStubComponent {
+        @Input() currentStep: string;
+        @Input() datasetSelected: string;
+        @Input() datasetList: Dataset[];
+        @Input() attributeList: Attribute[];
+        @Input() criteriaFamilyList: CriteriaFamily[];
+        @Input() outputFamilyList: OutputFamily[];
+        @Input() outputCategoryList: OutputCategory[];
+        @Input() criteriaList: Criterion[];
+        @Input() outputList: number[];
+        @Input() queryParams: SearchQueryParams;
+        @Input() coneSearch: ConeSearch;
     }
 
-    ngOnDestroy(): void {
-        this.datasetSelectedSubscription.unsubscribe();
-        super.ngOnDestroy();
-    }
-}
+    let component: DatasetComponent;
+    let fixture: ComponentFixture<DatasetComponent>;
+    let store: MockStore;
+
+    beforeEach(waitForAsync(() => {
+        TestBed.configureTestingModule({
+            imports: [RouterTestingModule],
+            declarations: [
+                DatasetComponent,
+                SpinnerStubComponent,
+                DatasetTabsStubComponent,
+                SummaryStubComponent,
+                SortByOutputDisplayPipe
+            ],
+            providers: [provideMockStore({ })]
+        });
+        fixture = TestBed.createComponent(DatasetComponent);
+        component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
+    }));
+
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+
+    it('should execute ngOnInit lifecycle', (done) => {
+        component.datasetSelected = of('myDataset');
+        const spy = jest.spyOn(store, 'dispatch');
+        jest.spyOn(AbstractSearchComponent.prototype, 'ngOnInit').mockReturnThis();
+        component.ngOnInit();
+        Promise.resolve(null).then(function() {
+            expect(spy).toHaveBeenCalledTimes(2);
+            expect(spy).toHaveBeenCalledWith(searchActions.changeStep({ step: 'dataset' }));
+            expect(spy).toHaveBeenCalledWith(searchActions.initSearch());
+            done();
+        });
+    });
+
+    it('#ngOnDestroy() should unsubscribe from datasetSelectedSubscription', () => {
+        component.datasetSelectedSubscription = of().subscribe();
+        const spy = jest.spyOn(component.datasetSelectedSubscription, 'unsubscribe');
+        component.ngOnDestroy();
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+});
diff --git a/client/src/app/instance/search/containers/dataset.component.ts b/client/src/app/instance/search/containers/dataset.component.ts
index aba154ca..cbd886fd 100644
--- a/client/src/app/instance/search/containers/dataset.component.ts
+++ b/client/src/app/instance/search/containers/dataset.component.ts
@@ -49,6 +49,8 @@ export class DatasetComponent extends AbstractSearchComponent {
     }
 
     ngOnInit(): void {
+        // Create a micro task that is processed after the current synchronous code
+        // This micro task prevent the expression has changed after view init error
         Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'dataset' })));
         this.datasetSelectedSubscription = this.datasetSelected.subscribe(datasetSelected => {
             if (datasetSelected) {
@@ -59,7 +61,7 @@ export class DatasetComponent extends AbstractSearchComponent {
     }
 
     ngOnDestroy(): void {
-        this.datasetSelectedSubscription.unsubscribe();
+        if (this.datasetSelectedSubscription) this.datasetSelectedSubscription.unsubscribe();
         super.ngOnDestroy();
     }
 }
diff --git a/client/src/app/instance/search/containers/result.component.spec.ts b/client/src/app/instance/search/containers/result.component.spec.ts
index c6c157af..16462a1f 100644
--- a/client/src/app/instance/search/containers/result.component.spec.ts
+++ b/client/src/app/instance/search/containers/result.component.spec.ts
@@ -161,7 +161,7 @@ describe('[Instance][Search][Container] ResultComponent', () => {
         expect(spy).toHaveBeenCalledWith(searchActions.deleteSelectedData({ id: 1 }));
     });
 
-    it('#ngOnDestroy() should dispatch destroyResults action, unsubscribe from pristineSubscription and call #ngOnDestroy() on extended class', () => {
+    it('#ngOnDestroy() should dispatch destroyResults action and unsubscribe from pristineSubscription', () => {
         component.pristineSubscription = of().subscribe();
         const unsubscribeSpy = jest.spyOn(component.pristineSubscription, 'unsubscribe');
         const dispatchSpy = jest.spyOn(store, 'dispatch');
-- 
GitLab