Commit b863ad68 authored by Jérémy Destin's avatar Jérémy Destin
Browse files

Merge branch 'fix/card_and_form_tests' into 'master'

Fix card and form tests

See merge request urgi-is/gpds!20
parents df4189c5 7027aeb3
<ng-container *ngIf="(test && value === undefined) || (value)">
<ng-container *ngIf="shouldShow()">
<div class="row row-sep">
<div class="col-4 field my-2">
{{ label }}
......
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, TestBed } from '@angular/core/testing';
import { CardRowComponent } from './card-row.component';
import { ComponentTester, speculoosMatchers } from 'ngx-speculoos';
import { Component, ViewChild } from '@angular/core';
class CardRowComponentTester extends ComponentTester<CardRowComponent> {
constructor() {
super(CardRowComponent);
}
get rowDiv() {
return this.element('div.row');
}
get labelDiv() {
return this.element('div.field');
}
get valueDiv() {
return this.element('div.col');
}
}
/**
* Test gpds-card-row with a provided `ng-template`
*/
@Component({
selector: 'gpds-test',
template: `
<gpds-card-row>
<ng-template>
<strong>Value HTML template</strong>
</ng-template>
</gpds-card-row>`
})
class CardRowWithTemplateComponent {
@ViewChild(CardRowComponent) component: CardRowComponent;
}
describe('CardRowComponent', () => {
let component: CardRowComponent;
let fixture: ComponentFixture<CardRowComponent>;
beforeEach(() => jasmine.addMatchers(speculoosMatchers));
beforeEach(async(() => {
beforeEach(async(() =>
TestBed.configureTestingModule({
declarations: [CardRowComponent]
})
.compileComponents();
}));
declarations: [CardRowComponent, CardRowWithTemplateComponent]
}).compileComponents()
));
beforeEach(() => {
fixture = TestBed.createComponent(CardRowComponent);
component = fixture.componentInstance;
fixture.detectChanges();
it('should hide falsy value', () => {
const tester = new CardRowComponentTester();
tester.componentInstance.label = 'Label1';
tester.componentInstance.value = null;
tester.detectChanges();
expect(tester.rowDiv).toBeFalsy();
});
it('should show truthy value', () => {
const tester = new CardRowComponentTester();
tester.componentInstance.label = 'Label1';
tester.componentInstance.value = 'Value1';
tester.detectChanges();
expect(tester.rowDiv).toBeTruthy();
expect(tester.labelDiv).toContainText(tester.componentInstance.label);
expect(tester.valueDiv).toContainText(tester.componentInstance.value);
});
it('should hide falsy test', () => {
const tester = new CardRowComponentTester();
tester.componentInstance.label = 'Label1';
tester.componentInstance.value = 'Value1';
tester.componentInstance.test = false;
tester.detectChanges();
expect(tester.rowDiv).toBeFalsy();
});
it('should hide truthy test, falsy value', () => {
const tester = new CardRowComponentTester();
tester.componentInstance.label = 'Label1';
tester.componentInstance.value = '';
tester.componentInstance.test = true;
tester.detectChanges();
expect(tester.rowDiv).toBeFalsy();
});
it('should create', () => {
expect(component).toBeTruthy();
it('should show truthy test, truthy value', () => {
const tester = new CardRowComponentTester();
tester.componentInstance.label = 'Label1';
tester.componentInstance.value = 'Value1';
tester.componentInstance.test = true;
tester.detectChanges();
expect(tester.rowDiv).toBeTruthy();
expect(tester.labelDiv).toContainText(tester.componentInstance.label);
expect(tester.valueDiv).toContainText(tester.componentInstance.value);
});
it('should hide falsy test, provided template', async(() => {
const fixture = TestBed.createComponent(CardRowWithTemplateComponent);
fixture.componentInstance.component.label = 'Label1';
fixture.componentInstance.component.test = '';
fixture.detectChanges();
const element: HTMLElement = fixture.nativeElement;
expect(element.querySelector('div.row')).toBeFalsy();
}));
it('should show truthy test, provided template', async(() => {
const fixture = TestBed.createComponent(CardRowWithTemplateComponent);
const component = fixture.componentInstance.component;
component.label = 'Label2';
component.test = true;
fixture.detectChanges();
const element: HTMLElement = fixture.nativeElement;
expect(element.querySelector('div.row')).toBeTruthy();
expect(element.querySelector('div.field').textContent).toContain(component.label);
expect(element.querySelector('div.col').textContent).toContain('Value HTML template');
}));
});
......@@ -13,4 +13,13 @@ export class CardRowComponent {
@ContentChild(TemplateRef) template: TemplateRef<any>;
shouldShow(): boolean {
return this.test && (
// Value not provided and template provided
(this.value === undefined && this.template !== undefined)
||
// Or value truthy
!!this.value
);
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, TestBed } from '@angular/core/testing';
import { CardSectionComponent } from './card-section.component';
import { speculoosMatchers } from 'ngx-speculoos';
import { Component, ViewChild } from '@angular/core';
/**
* Test gpds-card-section with a provided `ng-template`
*/
@Component({
selector: 'gpds-test',
template: `
<gpds-card-section>
<ng-template>
<div class="test-body">Body HTML template</div>
</ng-template>
</gpds-card-section>`
})
class CardSectionTestWrapperComponent {
@ViewChild(CardSectionComponent) component: CardSectionComponent;
}
describe('CardSectionComponent', () => {
let component: CardSectionComponent;
let fixture: ComponentFixture<CardSectionComponent>;
beforeEach(() => jasmine.addMatchers(speculoosMatchers));
beforeEach(async(() => {
beforeEach(async(() =>
TestBed.configureTestingModule({
declarations: [CardSectionComponent]
})
.compileComponents();
declarations: [CardSectionComponent, CardSectionTestWrapperComponent]
}).compileComponents()
));
it('should hide falsy test', async(() => {
const fixture = TestBed.createComponent(CardSectionTestWrapperComponent);
const component = fixture.componentInstance.component;
component.header = 'Header1';
component.test = '';
fixture.detectChanges();
const element: HTMLElement = fixture.nativeElement;
const cardDiv = element.querySelector('div.card');
expect(cardDiv).toBeFalsy();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CardSectionComponent);
component = fixture.componentInstance;
it('should show truthy test', async(() => {
const fixture = TestBed.createComponent(CardSectionTestWrapperComponent);
const component = fixture.componentInstance.component;
component.header = 'Header2';
component.test = true;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
const element: HTMLElement = fixture.nativeElement;
const cardDiv = element.querySelector('div.card');
const headerDiv = element.querySelector('div.card-header');
const bodyDiv = element.querySelector('div.test-body');
expect(cardDiv).toBeTruthy();
expect(headerDiv.textContent).toContain(component.header);
expect(bodyDiv.textContent).toContain('Body HTML template');
}));
});
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, TestBed } from '@angular/core/testing';
import { CardTableComponent } from './card-table.component';
import { Component, ViewChild } from '@angular/core';
/**
* Test gpds-card-table with a simple provided row `ng-template`
*/
@Component({
selector: 'gpds-test',
template: `
<gpds-card-table>
<ng-template let-row>
<tr>
<td>{{ row[0] }}</td>
<td>{{ row[1] }}</td>
<td>{{ row[2] }}</td>
</tr>
</ng-template>
</gpds-card-table>`
})
class CardTableTestWrapperComponent {
@ViewChild(CardTableComponent) component: CardTableComponent;
}
describe('CardTableComponent', () => {
let component: CardTableComponent;
let fixture: ComponentFixture<CardTableComponent>;
beforeEach(async(() => {
beforeEach(async(() =>
TestBed.configureTestingModule({
declarations: [CardTableComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CardTableComponent);
component = fixture.componentInstance;
declarations: [CardTableComponent, CardTableTestWrapperComponent]
}).compileComponents()
));
const headers = [
'h1', 'h2', 'h3'
];
const rows = [
['a', 'b', 'c'],
['d', 'e', 'f'],
['g', 'h', 'i'],
];
it('should hide headers and show rows', () => {
const fixture = TestBed.createComponent(CardTableTestWrapperComponent);
const component = fixture.componentInstance.component;
component.headers = null;
component.rows = rows;
fixture.detectChanges();
const element: HTMLElement = fixture.nativeElement;
const thead = element.querySelector('thead');
expect(thead).toBeFalsy();
const tds = element.querySelectorAll('td');
expect(tds.length).toBe(9);
expect(tds[0].textContent).toContain(rows[0][0]);
expect(tds[5].textContent).toContain(rows[1][2]);
});
it('should create', () => {
expect(component).toBeTruthy();
it('should show headers and rows', () => {
const fixture = TestBed.createComponent(CardTableTestWrapperComponent);
const component = fixture.componentInstance.component;
component.headers = headers;
component.rows = rows;
fixture.detectChanges();
const element: HTMLElement = fixture.nativeElement;
const ths = element.querySelectorAll('thead th');
expect(ths.length).toBe(3);
expect(ths[0].textContent).toContain(headers[0]);
expect(ths[2].textContent).toContain(headers[2]);
const tds = element.querySelectorAll('td');
expect(tds.length).toBe(9);
expect(tds[0].textContent).toContain(rows[0][0]);
expect(tds[5].textContent).toContain(rows[1][2]);
});
});
<ul class="nav nav-tabs">
<li class="nav-item">
<a tabindex="0"
class="nav-link {{ activeTab == 'Germplasm' ? 'active' : ''}}"
(click)="activeTab='Germplasm'">
Germplasm
class="nav-link germplasm {{ getNavClass(tabs.GERMPLASM) }}"
(click)="activeTab=tabs.GERMPLASM">
{{ tabs.GERMPLASM }}
</a>
</li>
<li class="nav-item">
<a tabindex="1"
class="nav-link {{ activeTab == 'Variable' ? 'active' : ''}}"
(click)="activeTab='Variable'">
Trait
class="nav-link trait {{ getNavClass(tabs.TRAIT) }}"
(click)="activeTab=tabs.TRAIT">
{{ tabs.TRAIT }}
</a>
</li>
</ul>
<!-- Germplasm tab -->
<div class="{{ activeTab == 'Germplasm' ? 'visible' : 'd-none' }}">
<div class="germplasm {{ getTabClass(tabs.GERMPLASM) }}">
<!-- Input for the crops field -->
<div class="form-group row pt-3">
<label for="crops" class="col-sm-4">
......@@ -70,8 +70,8 @@
</div>
</div>
<!-- Variable tab -->
<div class="{{ activeTab == 'Variable' ? 'visible' : 'd-none' }}">
<!-- Trait tab -->
<div class="trait {{ getTabClass(tabs.TRAIT) }}">
<gpds-trait-ontology-widget
[criteria$]="criteria$"
(initialized)="traitWidgetInitialized.emit($event)">
......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { async, TestBed } from '@angular/core/testing';
import { FormComponent } from './form.component';
import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
import { Component, EventEmitter, Input, Output } from '@angular/core';
/**
* Mock gpds-suggestion-field
*/
@Component({
selector: 'gpds-suggestion-field',
template: '<br/>'
})
class MockFieldComponent {
class MockSuggestionFieldComponent {
@Input() criteria$: any;
}
/**
* Mock gpds-trait-ontology-widget
*/
@Component({
selector: 'gpds-trait-ontology-widget',
template: '<br/>'
})
class MockTraitWidgetComponent {
@Input() criteria$: any;
@Output() initialized = new EventEmitter();
}
describe('FormComponent', () => {
let component: FormComponent;
let fixture: ComponentFixture<FormComponent>;
beforeEach(() => {
beforeEach(async(() =>
TestBed.configureTestingModule({
declarations: [FormComponent, MockFieldComponent],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
declarations: [FormComponent, MockSuggestionFieldComponent, MockTraitWidgetComponent],
}).compileComponents()
));
fixture = TestBed.createComponent(FormComponent);
component = fixture.componentInstance;
it('should switch tabs', async(() => {
const fixture = TestBed.createComponent(FormComponent);
fixture.detectChanges();
});
it('should create', () => {
const element: HTMLElement = fixture.nativeElement;
const germplasmNav: HTMLElement = element.querySelector('a.germplasm');
const germplasmTab: HTMLElement = element.querySelector('div.germplasm');
const traitNav: HTMLElement = element.querySelector('a.trait');
const traitTab: HTMLElement = element.querySelector('div.trait');
// Check default tab is active
expect(germplasmNav.getAttribute('class')).toContain('active');
expect(germplasmTab.getAttribute('class')).toContain('visible');
expect(traitNav.getAttribute('class')).not.toContain('active');
expect(traitTab.getAttribute('class')).toContain('d-none');
traitNav.click();
fixture.detectChanges();
// Check tab switched
expect(traitNav.getAttribute('class')).toContain('active');
expect(traitTab.getAttribute('class')).toContain('visible');
expect(germplasmNav.getAttribute('class')).not.toContain('active');
expect(germplasmTab.getAttribute('class')).toContain('d-none');
germplasmNav.click();
fixture.detectChanges();
expect(component).toBeTruthy();
});
// Check tab switched back
expect(germplasmNav.getAttribute('class')).toContain('active');
expect(germplasmTab.getAttribute('class')).toContain('visible');
expect(traitNav.getAttribute('class')).not.toContain('active');
expect(traitTab.getAttribute('class')).toContain('d-none');
}));
});
......@@ -2,6 +2,11 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
import { DataDiscoveryCriteria } from '../models/data-discovery.model';
import { BehaviorSubject } from 'rxjs';
enum Tabs {
GERMPLASM = 'Germplasm',
TRAIT = 'Trait'
}
@Component({
selector: 'gpds-form',
templateUrl: './form.component.html',
......@@ -10,5 +15,18 @@ import { BehaviorSubject } from 'rxjs';
export class FormComponent {
@Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>;
@Output() traitWidgetInitialized = new EventEmitter();
activeTab = 'Germplasm';
// Default active tab
activeTab: Tabs = Tabs.GERMPLASM;
// to give access in HTML template
tabs = Tabs;
getNavClass(tab: Tabs) {
return this.activeTab === tab ? 'active' : '';
}
getTabClass(tab: Tabs) {
return this.activeTab === tab ? 'visible' : 'd-none';
}
}
......@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
import { BrapiService } from '../brapi.service';
import { ActivatedRoute } from '@angular/router';
import { BrapiLocation } from '../models/brapi.model';
import { KeyValueObject } from '../utils';
import { KeyValueObject, toKeyValueObjects } from '../utils';
import { DataDiscoverySource } from '../models/data-discovery.model';
import { GnpisService } from '../gnpis.service';
......@@ -30,7 +30,7 @@ export class SiteCardComponent implements OnInit {
this.location = response.result;
this.additionalInfos = [];
if (this.location.additionalInfo) {
this.manageAdditionalInfo(KeyValueObject.fromObject(this.location.additionalInfo).sort());
this.manageAdditionalInfo(toKeyValueObjects(this.location.additionalInfo).sort());
}
const sourceURI = location['schema:includedInDataCatalog'];
// TODO Remove the condition when the field includedInDataCatalog will be added to URGI study.
......
......@@ -5,7 +5,7 @@ import { BrapiGermplasm, BrapiObservationVariable, BrapiStudy, BrapiTrial } from
import { GnpisService } from '../gnpis.service';
import { DataDiscoverySource } from '../models/data-discovery.model';
import { KeyValueObject } from '../utils';
import { KeyValueObject, toKeyValueObjects } from '../utils';
@Component({
selector: 'gpds-study-card',
......@@ -43,7 +43,7 @@ export class StudyCardComponent implements OnInit {
this.additionalInfos = [];
if (this.study.additionalInfo) {
this.additionalInfos = KeyValueObject.fromObject(this.study.additionalInfo).sort();
this.additionalInfos = toKeyValueObjects(this.study.additionalInfo).sort();
}
// Get study trials
......
import { KeyValueObject } from './utils';
import { KeyValueObject, toKeyValueObjects } from './utils';
describe('KeyValueObject', () => {
it('should convert JS object to array of KeyValueObject', () => {
const actual = KeyValueObject.fromObject({
const actual = toKeyValueObjects({
'a': '1',
'b': '2',
'c': null,
......@@ -13,10 +13,10 @@ describe('KeyValueObject', () => {
'f': '3'
});
const expected = [
new KeyValueObject('a', '1'),
new KeyValueObject('b', '2'),
new KeyValueObject('f', '3'),
const expected: KeyValueObject[] = [
{ key: 'a', value: '1' },
{ key: 'b', value: '2' },
{ key: 'f', value: '3' },
];
expect(actual).toEqual(expected);
......
......@@ -8,19 +8,18 @@ export function asArray<T>(obj: T | T[]): T[] {
return [obj];
}
export class KeyValueObject {
public key: string;
public value: string;
constructor(key: string, value: string) {
this.key = key;
this.value = value;
}
export interface KeyValueObject {
key: string;
value: string;
}
static fromObject(o: { [key: string]: string }): KeyValueObject[] {
return Object.entries(o)
/**
* Transform an object with string keys and values to a list of `KeyValueObject`.
* Also makes sure the keys and values are truthy.