Commit ba3fd067 authored by Jean-Baptiste Nizet's avatar Jean-Baptiste Nizet
Browse files

feat: implement frontend listing of pillars

parent aeea2067
......@@ -15,6 +15,7 @@ import { GeneticResourcesComponent } from './genetic-resources/genetic-resources
import { GeneticResourceComponent } from './genetic-resource/genetic-resource.component';
import { AggregationsComponent } from './aggregations/aggregations.component';
import { AggregationComponent } from './aggregation/aggregation.component';
import { PillarsComponent } from './pillars/pillars.component';
registerLocaleData(localeFr);
......@@ -26,7 +27,8 @@ registerLocaleData(localeFr);
GeneticResourcesComponent,
GeneticResourceComponent,
AggregationsComponent,
AggregationComponent
AggregationComponent,
PillarsComponent
],
imports: [
BrowserModule,
......
import { TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import { LOCALE_ID } from '@angular/core';
import { ComponentTester, speculoosMatchers } from 'ngx-speculoos';
import { GeneticResourcesComponent } from './genetic-resources.component';
import { GeneticResourceComponent } from '../genetic-resource/genetic-resource.component';
import { toGeneticResource, toSinglePage } from '../models/test-model-generators';
import { LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import { GeneticResourceModel } from '../models/genetic-resource.model';
describe('GeneticResourcesComponent', () => {
......
<div class="mt-5">
<form class="input-group" [formGroup]="searchForm" (ngSubmit)="search()">
<input class="form-control form-control-lg" type="text"
placeholder="Exemples : pisum sativum, rosa"
formControlName="search"
[ngbTypeahead]="suggesterTypeahead">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">Recherche</button>
</div>
</form>
<div class="row mt-5">
<div class="col-md-8 col-lg-9 order-md-last">
<form class="input-group" [formGroup]="searchForm" (ngSubmit)="search()">
<input class="form-control form-control-lg" type="text"
placeholder="Exemples : pisum sativum, rosa"
formControlName="search"
[ngbTypeahead]="suggesterTypeahead">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">Recherche</button>
</div>
</form>
</div>
<div class="col-md-4 col-lg-3 order-md-first mt-5 mt-md-0">
<rare-pillars></rare-pillars>
</div>
</div>
......@@ -8,6 +8,8 @@ import { HomeComponent } from './home.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { SearchService } from '../search.service';
import { PillarsComponent } from '../pillars/pillars.component';
import { By } from '@angular/platform-browser';
class HomeComponentTester extends ComponentTester<HomeComponent> {
constructor() {
......@@ -21,12 +23,17 @@ class HomeComponentTester extends ComponentTester<HomeComponent> {
get searchButton() {
return this.button('button');
}
get pillarsComponent() {
return this.debugElement.query(By.directive(PillarsComponent));
}
}
describe('HomeComponent', () => {
beforeEach(() => TestBed.configureTestingModule({
imports: [ReactiveFormsModule, RouterTestingModule, HttpClientTestingModule, NgbTypeaheadModule.forRoot()],
declarations: [HomeComponent]
declarations: [HomeComponent, PillarsComponent],
providers: [HttpClientTestingModule]
}));
beforeEach(() => jasmine.addMatchers(speculoosMatchers));
......@@ -70,4 +77,11 @@ describe('HomeComponent', () => {
expect(component.search).toHaveBeenCalled();
expect(component.searchForm.get('search').value).toBe(query);
});
it('should display the pillars', () => {
const tester = new HomeComponentTester();
tester.detectChanges();
expect(tester.pillarsComponent).not.toBeNull();
});
});
export interface DatabaseSourceModel {
name: string;
documentCount: number;
url: string;
}
export interface PillarModel {
name: string;
databaseSources: Array<DatabaseSourceModel>;
}
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { PillarService } from './pillar.service';
import { PillarModel } from './models/pillar.model';
describe('PillarService', () => {
let service: PillarService;
let http: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule
]
});
service = TestBed.get(PillarService) as PillarService;
http = TestBed.get(HttpTestingController) as HttpTestingController;
});
it('should list pillars', () => {
let actualResults: Array<PillarModel>;
service.list().subscribe(results => actualResults = results);
const expectedResults = [
{
name: 'Plant'
}
] as Array<PillarModel>;
http.expectOne('/api/pillars').flush(expectedResults);
expect(actualResults).toEqual(expectedResults);
http.verify();
});
});
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { PillarModel } from './models/pillar.model';
@Injectable({
providedIn: 'root'
})
export class PillarService {
constructor(private http: HttpClient) { }
list(): Observable<Array<PillarModel>> {
return this.http.get<Array<PillarModel>>('/api/pillars');
}
}
<ul class="list-unstyled pillar">
<li *ngFor="let pillar of (pillars$ | async)">
<span class="pillar-name">{{ pillar.name }}</span>
<ul class="list-unstyled source">
<li *ngFor="let databaseSource of pillar.databaseSources">
<a *ngIf="databaseSource.url" [href]="databaseSource.url">{{ databaseSource.name }}</a>
<span *ngIf="!databaseSource.url">{{ databaseSource.name }}</span>
<small class="text-muted ml-2">[{{ databaseSource.documentCount | number }}]</small>
</li>
</ul>
</li>
</ul>
.pillar-name {
font-weight: 500;
}
.source {
padding-left: .8em;
}
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { EMPTY, of } from 'rxjs';
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import { LOCALE_ID } from '@angular/core';
import { ComponentTester, speculoosMatchers } from 'ngx-speculoos';
import { PillarsComponent } from './pillars.component';
import { PillarService } from '../pillar.service';
import { PillarModel } from '../models/pillar.model';
class PillarsComponentTester extends ComponentTester<PillarsComponent> {
constructor() {
super(PillarsComponent);
}
get pillarListItems() {
return this.elements('ul.pillar > li');
}
pillarListItem(index: number) {
return this.pillarListItems[index];
}
databaseSourceItem(pillarIndex: number, sourceIndex: number) {
return this.pillarListItem(pillarIndex).elements('li')[sourceIndex];
}
databaseSourceLink(pillarIndex: number, sourceIndex: number) {
return this.databaseSourceItem(pillarIndex, sourceIndex).element('a');
}
}
describe('PillarsComponent', () => {
let tester: PillarsComponentTester;
let pillarService: PillarService;
beforeEach(() => {
registerLocaleData(localeFr);
TestBed.configureTestingModule({
declarations: [PillarsComponent],
imports: [HttpClientTestingModule],
providers: [
{ provide: LOCALE_ID, useValue: 'fr-FR' }
]
});
tester = new PillarsComponentTester();
pillarService = TestBed.get(PillarService);
jasmine.addMatchers(speculoosMatchers);
});
it('should not display any pillar while pillars are not available yet', () => {
spyOn(pillarService, 'list').and.returnValue(EMPTY);
tester.detectChanges();
expect(tester.pillarListItems.length).toBe(0);
});
it('should display pillars', () => {
const pillars: Array<PillarModel> = [
{
name: 'Plant',
databaseSources: [
{
name: 'Florilège',
documentCount: 1000,
url: 'http://florilege.arcad-project.org/fr/collections'
},
{
name: 'CNRGV',
documentCount: 200,
url: null
}
]
},
{
name: 'Forest',
databaseSources: []
}
];
spyOn(pillarService, 'list').and.returnValue(of(pillars));
tester.detectChanges();
expect(tester.pillarListItem(0)).toContainText('Plant');
expect(tester.pillarListItem(1)).toContainText('Forest');
expect(tester.databaseSourceItem(0, 0)).toContainText('Florilège');
expect(tester.databaseSourceItem(0, 0)).toContainText('[1\u00a0000]');
expect(tester.databaseSourceItem(0, 1)).toContainText('CNRGV');
expect(tester.databaseSourceItem(0, 1)).toContainText('[200]');
expect(tester.databaseSourceLink(0, 0).attr('href'))
.toBe('http://florilege.arcad-project.org/fr/collections');
expect(tester.databaseSourceLink(0, 1)).toBeNull();
});
});
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { PillarService } from '../pillar.service';
import { PillarModel } from '../models/pillar.model';
@Component({
selector: 'rare-pillars',
templateUrl: './pillars.component.html',
styleUrls: ['./pillars.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PillarsComponent implements OnInit {
pillars$: Observable<Array<PillarModel>>;
constructor(private pillarService: PillarService) { }
ngOnInit() {
this.pillars$ = this.pillarService.list();
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment