From d89a1b04479164de59224adfae07f27b9565120b Mon Sep 17 00:00:00 2001 From: Camille Moinier Date: Wed, 5 Jun 2024 15:45:32 +0200 Subject: [PATCH] feat: tests and startwith falsy --- .../record-metadata.component.ts | 5 +- .../fixtures/src/lib/records.fixtures.ts | 8 ++ .../src/lib/service/data.service.spec.ts | 27 +++++ .../src/lib/utils/map-utils.service.spec.ts | 10 ++ .../src/lib/state/mdview.facade.spec.ts | 114 +++++++++++++++++- .../record/src/lib/state/mdview.facade.ts | 4 +- 6 files changed, 163 insertions(+), 5 deletions(-) diff --git a/apps/datahub/src/app/record/record-metadata/record-metadata.component.ts b/apps/datahub/src/app/record/record-metadata/record-metadata.component.ts index 1edb1ea1eb..602cd1b312 100644 --- a/apps/datahub/src/app/record/record-metadata/record-metadata.component.ts +++ b/apps/datahub/src/app/record/record-metadata/record-metadata.component.ts @@ -3,7 +3,7 @@ import { SourcesService } from '@geonetwork-ui/feature/catalog' import { SearchService } from '@geonetwork-ui/feature/search' import { ErrorType } from '@geonetwork-ui/ui/elements' import { BehaviorSubject, combineLatest } from 'rxjs' -import { filter, map, mergeMap } from 'rxjs/operators' +import { filter, map, mergeMap, startWith } from 'rxjs/operators' import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' import { Keyword, @@ -26,7 +26,8 @@ export class RecordMetadataComponent { ]).pipe( map(([mapApiLinks, geoDataLinksWithGeometry]) => { return mapApiLinks?.length > 0 || geoDataLinksWithGeometry?.length > 0 - }) + }), + startWith(false) ) displayData$ = combineLatest([ diff --git a/libs/common/fixtures/src/lib/records.fixtures.ts b/libs/common/fixtures/src/lib/records.fixtures.ts index e1893abb07..c193335ee6 100644 --- a/libs/common/fixtures/src/lib/records.fixtures.ts +++ b/libs/common/fixtures/src/lib/records.fixtures.ts @@ -117,6 +117,14 @@ Cette section contient des *caractères internationaux* (ainsi que des "caractè description: 'This WFS service offers direct download capability', identifierInService: 'my:featuretype', }, + { + type: 'service', + url: new URL('https://my-org.net/ogc'), + accessServiceProtocol: 'ogcFeatures', + name: 'my:featuretype', + description: 'This OGC service offers direct download capability', + identifierInService: 'my:featuretype', + }, ], lineage: `This record was edited manually to test the conversion processes diff --git a/libs/feature/dataviz/src/lib/service/data.service.spec.ts b/libs/feature/dataviz/src/lib/service/data.service.spec.ts index c54e86d8d7..a26e84a814 100644 --- a/libs/feature/dataviz/src/lib/service/data.service.spec.ts +++ b/libs/feature/dataviz/src/lib/service/data.service.spec.ts @@ -93,6 +93,13 @@ jest.mock('@camptocamp/ogc-client', () => ({ }) } allCollections = Promise.resolve([{ name: 'collection1' }]) + featureCollections = + this.url.indexOf('error.http') > -1 + ? Promise.reject(new Error()) + : Promise.resolve(['collection1', 'collection2']) + getCollectionItem(collection, id) { + return Promise.resolve('item1') + } }, })) @@ -700,5 +707,25 @@ describe('DataService', () => { ) }) }) + describe('#getItemsFromOgcApi', () => { + describe('calling getItemsFromOgcApi() with a valid URL', () => { + it('returns the first collection item when collections array is not empty', async () => { + const item = await service.getItemsFromOgcApi( + 'https://my.ogc.api/features' + ) + expect(item).toBe('item1') + }) + }) + + describe('calling getItemsFromOgcApi() with an erroneous URL', () => { + it('throws an error', async () => { + try { + await service.getItemsFromOgcApi('http://error.http/ogcapi') + } catch (e) { + expect(e.message).toBe('ogc.unreachable.unknown') + } + }) + }) + }) }) }) diff --git a/libs/feature/map/src/lib/utils/map-utils.service.spec.ts b/libs/feature/map/src/lib/utils/map-utils.service.spec.ts index ceb87a5d20..c73f4a053d 100644 --- a/libs/feature/map/src/lib/utils/map-utils.service.spec.ts +++ b/libs/feature/map/src/lib/utils/map-utils.service.spec.ts @@ -327,6 +327,16 @@ describe('MapUtilsService', () => { }) }) + describe('getRecordExtent', () => { + it('should return null if spatialExtents is not present or is an empty array', () => { + const record1: Partial = {} + const record2: Partial = { spatialExtents: [] } + + expect(service.getRecordExtent(record1)).toBeNull() + expect(service.getRecordExtent(record2)).toBeNull() + }) + }) + describe('#prioritizePageScroll', () => { const interactions = defaults() let dragRotate diff --git a/libs/feature/record/src/lib/state/mdview.facade.spec.ts b/libs/feature/record/src/lib/state/mdview.facade.spec.ts index e83b9dbdd4..24957eea0c 100644 --- a/libs/feature/record/src/lib/state/mdview.facade.spec.ts +++ b/libs/feature/record/src/lib/state/mdview.facade.spec.ts @@ -1,4 +1,4 @@ -import { TestBed } from '@angular/core/testing' +import { TestBed, fakeAsync, tick } from '@angular/core/testing' import { MockStore, provideMockStore } from '@ngrx/store/testing' import { initialMetadataViewState, @@ -13,6 +13,26 @@ import { } from '@geonetwork-ui/common/fixtures' import { DatavizConfigurationModel } from '@geonetwork-ui/common/domain/model/dataviz/dataviz-configuration.model' import { AvatarServiceInterface } from '@geonetwork-ui/api/repository' +import { TestScheduler } from 'rxjs/testing' + +const newEndpointCall = jest.fn() +let testScheduler: TestScheduler + +jest.mock('@camptocamp/ogc-client', () => ({ + _newEndpointCall: jest.fn(), + OgcApiEndpoint: class { + constructor(private url) { + newEndpointCall(url) // to track endpoint creation + } + featureCollections = + this.url.indexOf('error.http') > -1 + ? Promise.reject(new Error()) + : Promise.resolve(['collection1', 'collection2']) + getCollectionItem(collection, id) { + return Promise.resolve('item1') + } + }, +})) describe('MdViewFacade', () => { let store: MockStore @@ -217,4 +237,96 @@ describe('MdViewFacade', () => { expect(store.scannedActions$).toBeObservable(expected) }) }) + + describe('geoDataLinksWithGeometry$', () => { + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected) + }) + store.setState({ + [METADATA_VIEW_FEATURE_STATE_KEY]: { + ...initialMetadataViewState, + metadata: DATASET_RECORDS[0], + }, + }) + }) + it('should return OGC links that have geometry', fakeAsync(() => { + const values = { + a: [ + { + type: 'download', + url: new URL('http://my-org.net/download/2.geojson'), + mimeType: 'application/geo+json', + name: 'Direct download', + }, + { + type: 'service', + url: new URL('https://my-org.net/wfs'), + accessServiceProtocol: 'wfs', + name: 'my:featuretype', // FIXME: same as identifier otherwise it will be lost in iso... + description: 'This WFS service offers direct download capability', + identifierInService: 'my:featuretype', + }, + { + type: 'service', + url: new URL('https://my-org.net/ogc'), + accessServiceProtocol: 'ogcFeatures', + name: 'my:featuretype', + description: 'This OGC service offers direct download capability', + identifierInService: 'my:featuretype', + }, + ], + } + jest.spyOn(facade.dataService, 'getItemsFromOgcApi').mockResolvedValue({ + id: '123', + type: 'Feature', + time: null, + properties: { + type: '', + title: '', + }, + links: [], + geometry: { type: 'MultiPolygon', coordinates: [] }, + }) + let result + facade.geoDataLinksWithGeometry$.subscribe((v) => (result = v)) + tick() + expect(result).toEqual(values.a) + })) + it('should not return OGC links that do not have geometry', fakeAsync(() => { + const values = { + a: [ + { + type: 'download', + url: new URL('http://my-org.net/download/2.geojson'), + mimeType: 'application/geo+json', + name: 'Direct download', + }, + { + type: 'service', + url: new URL('https://my-org.net/wfs'), + accessServiceProtocol: 'wfs', + name: 'my:featuretype', // FIXME: same as identifier otherwise it will be lost in iso... + description: 'This WFS service offers direct download capability', + identifierInService: 'my:featuretype', + }, + ], + } + jest.spyOn(facade.dataService, 'getItemsFromOgcApi').mockResolvedValue({ + id: '123', + type: 'Feature', + time: null, + properties: { + type: '', + title: '', + }, + links: [], + geometry: null, + }) + let result + facade.geoDataLinksWithGeometry$.subscribe((v) => (result = v)) + tick() + expect(result).toEqual(values.a) + })) + }) }) diff --git a/libs/feature/record/src/lib/state/mdview.facade.ts b/libs/feature/record/src/lib/state/mdview.facade.ts index 135d4be9b3..814b6a7dcd 100644 --- a/libs/feature/record/src/lib/state/mdview.facade.ts +++ b/libs/feature/record/src/lib/state/mdview.facade.ts @@ -24,9 +24,9 @@ import { DataService } from '@geonetwork-ui/feature/dataviz' export class MdViewFacade { constructor( private store: Store, - private linkClassifier: LinkClassifierService, + public linkClassifier: LinkClassifierService, private avatarService: AvatarServiceInterface, - private dataService: DataService + public dataService: DataService ) {} isPresent$ = this.store.pipe(