From 22e1025e85991cdd6fa6cc1a46a8dd6736897858 Mon Sep 17 00:00:00 2001
From: Tifenn Guillas <tifenn.guillas@gmail.com>
Date: Tue, 2 Nov 2021 17:46:00 +0100
Subject: [PATCH] WIP: tests on cone search

---
 .../cone-search/ra.component.spec.ts          | 111 ++++++++++++++++++
 .../components/cone-search/ra.component.ts    |  38 ++++--
 .../cone-search/radius.component.spec.ts      |  22 ++++
 .../cone-search/radius.component.ts           |   8 +-
 .../cone-search/resolver.component.spec.ts    |  75 ++++++++++++
 .../cone-search/resolver.component.ts         |  17 ++-
 .../datatable/datatable.component.ts          |  25 ++--
 7 files changed, 268 insertions(+), 28 deletions(-)
 create mode 100644 client/src/app/instance/shared-search/components/cone-search/ra.component.spec.ts
 create mode 100644 client/src/app/instance/shared-search/components/cone-search/radius.component.spec.ts
 create mode 100644 client/src/app/instance/shared-search/components/cone-search/resolver.component.spec.ts

diff --git a/client/src/app/instance/shared-search/components/cone-search/ra.component.spec.ts b/client/src/app/instance/shared-search/components/cone-search/ra.component.spec.ts
new file mode 100644
index 00000000..efb1dc3e
--- /dev/null
+++ b/client/src/app/instance/shared-search/components/cone-search/ra.component.spec.ts
@@ -0,0 +1,111 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import { Component, ViewChild } from '@angular/core';
+import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+
+import { RaComponent } from './ra.component';
+import { ConeSearch, Resolver } from '../../../store/models';
+import { nanValidator, rangeValidator } from '../../validators';
+import { of } from 'rxjs';
+
+describe('[Instance][SharedSearch][Components][ConeSearch] RaComponent', () => {
+    let form = new FormGroup({
+        ra: new FormControl('', [Validators.required, nanValidator, rangeValidator(0, 360, 'RA')]),
+        ra_hms: new FormGroup({
+            h: new FormControl('', [Validators.required, nanValidator, rangeValidator(0, 24, 'Hours')]),
+            m: new FormControl('', [Validators.required, nanValidator, rangeValidator(0, 60, 'Minutes')]),
+            s: new FormControl('', [Validators.required, nanValidator, rangeValidator(0, 60, 'Seconds')])
+        })
+    });
+    @Component({
+        selector: `app-host`,
+        template: `
+            <app-ra
+                    [form]="form" 
+                    [unit]="unit" 
+                    [resolver]="resolver">
+            </app-ra>`
+    })
+    class TestHostComponent {
+        @ViewChild(RaComponent, { static: false })
+        public testedComponent: RaComponent;
+        public form: FormGroup = form;
+        public unit: string = 'degree';
+        public resolver: Resolver = undefined;
+    }
+
+    let testHostComponent: TestHostComponent;
+    let testHostFixture: ComponentFixture<TestHostComponent>;
+    let testedComponent: RaComponent;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                TestHostComponent,
+                RaComponent
+            ],
+            imports: [ReactiveFormsModule]
+        });
+        testHostFixture = TestBed.createComponent(TestHostComponent);
+        testHostComponent = testHostFixture.componentInstance;
+        testHostFixture.detectChanges();
+        testedComponent = testHostComponent.testedComponent;
+    });
+
+    it('should create the component', () => {
+        expect(testedComponent).toBeTruthy();
+    });
+
+    it('#ngOnInit() should disable ra_hms control and convert degrees to HMS', () => {
+        expect(testedComponent.form.controls.ra_hms.disabled).toBeTruthy();
+    });
+
+    it('should call ngOnChanges and apply changes', () => {
+        testedComponent.raControlSubscription = of().subscribe();
+        testedComponent.raHmsFormSubscription = of().subscribe();
+        const spyRa = jest.spyOn(testedComponent.raControlSubscription, 'unsubscribe');
+        const spyRaHms = jest.spyOn(testedComponent.raHmsFormSubscription, 'unsubscribe');
+        const spy = jest.spyOn(testedComponent, 'ngOnChanges');
+        testHostComponent.unit = 'degree';
+        testHostFixture.detectChanges();
+        // expect(spy).toHaveBeenCalledTimes(1);
+        expect(testedComponent.form.controls.ra_hms.disabled).toBeTruthy();
+        expect(testedComponent.form.controls.ra.enabled).toBeTruthy();
+        // expect(spyRaHms).toHaveBeenCalledTimes(1);
+        testHostComponent.unit = 'hms';
+        testHostFixture.detectChanges();
+        // expect(spy).toHaveBeenCalledTimes(2);
+        expect(testedComponent.form.controls.ra_hms.enabled).toBeTruthy();
+        expect(testedComponent.form.controls.ra.disabled).toBeTruthy();
+        // expect(spyRa).toHaveBeenCalledTimes(1);
+    });
+
+    it('#getRaHmsForm() should return RA HMS form control', () => {
+        const form: FormGroup = testedComponent.getRaHmsForm();
+        expect(Object.keys(form.controls).length).toEqual(3);
+        expect(Object.keys(form.controls)).toContain('h');
+        expect(Object.keys(form.controls)).toContain('m');
+        expect(Object.keys(form.controls)).toContain('s');
+    });
+
+    it('#deg2HMS(value) convert RA from degree to HH:MM:SS', () => {
+        testedComponent.deg2HMS(78.2);
+        expect(testedComponent.getRaHmsForm().controls.h.value).toBe(5);
+        expect(testedComponent.getRaHmsForm().controls.m.value).toBe(12);
+        expect(parseFloat(testedComponent.getRaHmsForm().controls.s.value)).toBe(48);
+    });
+
+    it('#HMS2Deg(hms) convert RA from HH:MM:SS to degree', () => {
+        testedComponent.HMS2Deg({ h: 5, m: 12, s: 48 });
+        expect(testedComponent.form.controls.ra.value).toBe(78.2);
+    });
+
+    it('#ngOnDestroy() should unsubscribe from raControlSubscription and raHmsFormSubscription', () => {
+        testedComponent.raControlSubscription = of().subscribe();
+        testedComponent.raHmsFormSubscription = of().subscribe();
+        const spyRa = jest.spyOn(testedComponent.raControlSubscription, 'unsubscribe');
+        const spyRaHms = jest.spyOn(testedComponent.raHmsFormSubscription, 'unsubscribe');
+        testedComponent.ngOnDestroy();
+        expect(spyRa).toHaveBeenCalledTimes(1);
+        expect(spyRaHms).toHaveBeenCalledTimes(1);
+    });
+});
diff --git a/client/src/app/instance/shared-search/components/cone-search/ra.component.ts b/client/src/app/instance/shared-search/components/cone-search/ra.component.ts
index aab36bf7..7a7a6365 100644
--- a/client/src/app/instance/shared-search/components/cone-search/ra.component.ts
+++ b/client/src/app/instance/shared-search/components/cone-search/ra.component.ts
@@ -15,16 +15,20 @@ import { debounceTime } from 'rxjs/operators';
 
 import { Resolver } from 'src/app/instance/store/models';
 
+/**
+ * @class
+ * @classdesc RA component.
+ *
+ * @implements OnInit
+ * @implements OnChanges
+ * @implements OnDestroy
+ */
 @Component({
     selector: 'app-ra',
     templateUrl: 'ra.component.html',
     styleUrls: ['input-group.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
-/**
- * @class
- * @classdesc RA component.
- */
 export class RaComponent implements OnInit, OnDestroy, OnChanges  {
     @Input() form: FormGroup;
     @Input() unit: string;
@@ -33,7 +37,7 @@ export class RaComponent implements OnInit, OnDestroy, OnChanges  {
     public raControlSubscription: Subscription;
     public raHmsFormSubscription: Subscription;
 
-    ngOnInit() {
+    ngOnInit(): void {
         this.form.controls.ra_hms.disable();
         this.raControlSubscription = this.form.controls.ra.valueChanges.pipe(debounceTime(250))
             .subscribe(deg => this.deg2HMS(deg));
@@ -62,11 +66,20 @@ export class RaComponent implements OnInit, OnDestroy, OnChanges  {
         }
     }
 
-    getRaHmsForm() {
-        const raHmsForm = this.form.controls.ra_hms as FormGroup;
-        return raHmsForm;
+    /**
+     * Returns RA form group.
+     *
+     * @return FormGroup
+     */
+    getRaHmsForm(): FormGroup {
+        return this.form.controls.ra_hms as FormGroup;
     }
 
+    /**
+     * Converts RA hour minute second from degree and sets RA HMS fields.
+     *
+     * @param  {number} deg - The degree value.
+     */
     deg2HMS(deg: number): void {
         let tmp = deg / 15;
         const hh = Math.trunc(tmp);
@@ -80,12 +93,17 @@ export class RaComponent implements OnInit, OnDestroy, OnChanges  {
         raHmsForm.controls.s.setValue(ss);
     }
 
-    HMS2Deg(hms: {h: number, m: number, s: number }): void {
+    /**
+     * Sets RA degree from hour minute second and sets RA degree field.
+     *
+     * @param {h: number, m: number, s: number} hms - Coordinates in HMS.
+     */
+    HMS2Deg(hms: { h: number, m: number, s: number }): void {
         const deg = +(((((hms.s / 60) + hms.m) / 60) + hms.h) * 15).toFixed(8);
         this.form.controls.ra.setValue(deg);
     }
 
-    ngOnDestroy() {
+    ngOnDestroy(): void {
         if (this.raControlSubscription) this.raControlSubscription.unsubscribe();
         if (this.raHmsFormSubscription) this.raHmsFormSubscription.unsubscribe();
     }
diff --git a/client/src/app/instance/shared-search/components/cone-search/radius.component.spec.ts b/client/src/app/instance/shared-search/components/cone-search/radius.component.spec.ts
new file mode 100644
index 00000000..1af9f997
--- /dev/null
+++ b/client/src/app/instance/shared-search/components/cone-search/radius.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+
+import { RadiusComponent } from './radius.component';
+
+describe('[Instance][SharedSearch][Components][ConeSearch] RadiusComponent', () => {
+    let component: RadiusComponent;
+    let fixture: ComponentFixture<RadiusComponent>;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [RadiusComponent],
+            imports: [ReactiveFormsModule]
+        });
+        fixture = TestBed.createComponent(RadiusComponent);
+        component = fixture.componentInstance;
+    });
+
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/client/src/app/instance/shared-search/components/cone-search/radius.component.ts b/client/src/app/instance/shared-search/components/cone-search/radius.component.ts
index fde6ceb2..21114acb 100644
--- a/client/src/app/instance/shared-search/components/cone-search/radius.component.ts
+++ b/client/src/app/instance/shared-search/components/cone-search/radius.component.ts
@@ -10,16 +10,16 @@
 import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
 import { FormGroup } from '@angular/forms';
 
+/**
+ * @class
+ * @classdesc Radius component.
+ */
 @Component({
     selector: 'app-radius',
     templateUrl: 'radius.component.html',
     styleUrls: ['radius.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
-/**
- * @class
- * @classdesc Radius component.
- */
 export class RadiusComponent {
     @Input() form: FormGroup;
 }
diff --git a/client/src/app/instance/shared-search/components/cone-search/resolver.component.spec.ts b/client/src/app/instance/shared-search/components/cone-search/resolver.component.spec.ts
new file mode 100644
index 00000000..8c62843f
--- /dev/null
+++ b/client/src/app/instance/shared-search/components/cone-search/resolver.component.spec.ts
@@ -0,0 +1,75 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { Component, ViewChild } from '@angular/core';
+import { ReactiveFormsModule } from '@angular/forms';
+
+import { ResolverComponent } from './resolver.component';
+import { ConeSearch, Resolver } from '../../../store/models';
+
+describe('[Instance][SharedSearch][Components][ConeSearch] ResolverComponent', () => {
+    @Component({
+        selector: `app-host`,
+        template: `
+            <app-resolver
+                [coneSearch]="coneSearch"
+                [resolver]="resolver"
+                [resolverIsLoading]="resolverIsLoading"
+                [resolverIsLoaded]="resolverIsLoaded">
+            </app-resolver>`
+    })
+    class TestHostComponent {
+        @ViewChild(ResolverComponent, { static: false })
+        public testedComponent: ResolverComponent;
+        public coneSearch: ConeSearch = undefined;
+        public resolver: Resolver = undefined;
+        public resolverIsLoading: boolean = false;
+        public resolverIsLoaded: boolean = false;
+    }
+
+    let testHostComponent: TestHostComponent;
+    let testHostFixture: ComponentFixture<TestHostComponent>;
+    let testedComponent: ResolverComponent;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                TestHostComponent,
+                ResolverComponent
+            ],
+            imports: [ReactiveFormsModule]
+        });
+        testHostFixture = TestBed.createComponent(TestHostComponent);
+        testHostComponent = testHostFixture.componentInstance;
+        testHostFixture.detectChanges();
+        testedComponent = testHostComponent.testedComponent;
+    });
+
+    it('should create the component', () => {
+        expect(testedComponent).toBeTruthy();
+    });
+
+    it('#ngOnInit() should disable form if cone search already defined', () => {
+        expect(testedComponent.form.enabled).toBeTruthy();
+        testedComponent.coneSearch = { ra: 1, dec: 2, radius: 3 };
+        testedComponent.ngOnInit();
+        expect(testedComponent.form.disabled).toBeTruthy();
+    });
+
+    it('should call ngOnChanges and apply changes', () => {
+        const spy = jest.spyOn(testedComponent, 'ngOnChanges');
+        testHostComponent.coneSearch = null;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.enabled).toBeTruthy();
+        testHostComponent.coneSearch = { ra: 1, dec: 2, radius: 3 };
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.disabled).toBeTruthy();
+        expect(spy).toHaveBeenCalledTimes(2);
+    });
+
+    it('#submit() should raise retrieveCoordinates event', () => {
+        testedComponent.form.controls.name.setValue('myObjectName');
+        const spy = jest.spyOn(testedComponent.retrieveCoordinates, 'emit');
+        testedComponent.submit();
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith('myObjectName');
+    });
+});
diff --git a/client/src/app/instance/shared-search/components/cone-search/resolver.component.ts b/client/src/app/instance/shared-search/components/cone-search/resolver.component.ts
index 18c09444..2d2434f4 100644
--- a/client/src/app/instance/shared-search/components/cone-search/resolver.component.ts
+++ b/client/src/app/instance/shared-search/components/cone-search/resolver.component.ts
@@ -12,15 +12,15 @@ import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { ConeSearch, Resolver } from 'src/app/instance/store/models';
 
+/**
+ * @class
+ * @classdesc Resolver component.
+ */
 @Component({
     selector: 'app-resolver',
     templateUrl: 'resolver.component.html',
     changeDetection: ChangeDetectionStrategy.OnPush
 })
-/**
- * @class
- * @classdesc Resolver component.
- */
 export class ResolverComponent implements OnInit, OnChanges {
     @Input() coneSearch: ConeSearch;
     @Input() resolver: Resolver;
@@ -32,7 +32,7 @@ export class ResolverComponent implements OnInit, OnChanges {
         name: new FormControl('', [Validators.required])
     });
 
-    ngOnInit() {
+    ngOnInit(): void {
         if (this.coneSearch) {
             this.form.disable();
         }
@@ -48,7 +48,12 @@ export class ResolverComponent implements OnInit, OnChanges {
         }
     }
 
-    submit() {
+    /**
+     * Emits event to retrieve coordinates.
+     *
+     * @fires EventEmitter<string>
+     */
+    submit(): void {
         this.retrieveCoordinates.emit(this.form.controls.name.value);
     }
 }
diff --git a/client/src/app/instance/shared-search/components/datatable/datatable.component.ts b/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
index b9887163..afd7d5e9 100644
--- a/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
+++ b/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
@@ -67,25 +67,34 @@ export class DatatableComponent implements OnInit {
      *
      * @param  {Attribute} attribute - The attribute.
      *
-     * @return DetailRendererConfig | LinkRendererConfig | DownloadRendererConfig | ImageRendererConfig | RendererConfig | null
      */
-    getRendererConfig(attribute: Attribute): DetailRendererConfig | LinkRendererConfig | DownloadRendererConfig | ImageRendererConfig | RendererConfig | null {
+    // getRendererConfig(attribute: Attribute): DetailRendererConfig | LinkRendererConfig | DownloadRendererConfig | ImageRendererConfig | RendererConfig | null {
+    getRendererConfig(attribute: Attribute) {
+        let config = null;
         switch(attribute.renderer) {
             case 'detail':
-                return attribute.renderer_config as DetailRendererConfig;
+                config = attribute.renderer_config as DetailRendererConfig;
+                break;
             case 'link':
-                return attribute.renderer_config as LinkRendererConfig;
+                config = attribute.renderer_config as LinkRendererConfig;
+                break;
             case 'download':
-                return attribute.renderer_config as DownloadRendererConfig;
+                config = attribute.renderer_config as DownloadRendererConfig;
+                break;
             case 'image':
-                return attribute.renderer_config as ImageRendererConfig;
+                config = attribute.renderer_config as ImageRendererConfig;
+                break;
             case 'json':
-                return attribute.renderer_config as RendererConfig;
+                config = attribute.renderer_config as RendererConfig;
+                break;
             default:
-                return null;
+                config = null;
         }
+        return config;
+
     }
 
+
     /**
      * Returns output list from attribute list.
      *
-- 
GitLab