From 91f97ecc4317dc8b4ccb47cac898d608a11cfd00 Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Tue, 10 Nov 2020 08:57:19 +0100 Subject: [PATCH] bugfix: remove additional layers --- deploy/atlas/sanity.spec.js | 106 ++++++++++++++++++ .../browsingForDatasets.prod.e2e-spec.js | 2 +- e2e/src/selecting/atlas.prod.e2e-spec.js | 56 ++++++++- e2e/src/selecting/region.prod.e2e-spec.js | 4 +- e2e/util/selenium/iav.js | 2 +- e2e/util/selenium/layout.js | 40 ++++--- src/res/ext/waxholmRatV2_0.json | 2 + .../state/viewerState.store.helper.ts | 29 +++-- .../atlasLayerSelector.template.html | 14 +-- 9 files changed, 217 insertions(+), 38 deletions(-) create mode 100644 deploy/atlas/sanity.spec.js diff --git a/deploy/atlas/sanity.spec.js b/deploy/atlas/sanity.spec.js new file mode 100644 index 000000000..21d4be22a --- /dev/null +++ b/deploy/atlas/sanity.spec.js @@ -0,0 +1,106 @@ +const fs = require('fs') +const path = require('path') +const { promisify } = require('util') +const asyncReadfile = promisify(fs.readFile) +const { expect } = require('chai') +const { pid } = require('process') +const { assert } = require('console') + +const templateFiles = [ + 'bigbrain.json', + 'colin.json', + 'MNI152.json', + 'waxholmRatV2_0.json', + 'allenMouse.json' +] + +const atlasFiles = [ + 'atlas_multiLevelHuman.json', + 'atlas_waxholmRat.json', + 'atlas_allenMouse.json' +] + +const templateIdToParcsMap = new Map() +const templateIdToTemplateMap = new Map() +const parcIdToTemplateId = new Map() +const parcIdToParcMap = new Map() + +const rootDir = path.join(__dirname, '../../src/res/ext') + +describe('> atlas sanity check', () => { + before(async () => { + for (const templateFile of templateFiles){ + const txt = await asyncReadfile( + path.join(rootDir, templateFile), + 'utf-8' + ) + const template = JSON.parse(txt) + const { ['@id']: templateId, name: templateName, parcellations } = template + if (!templateId) throw new Error(`${templateFile} / ${templateName} / @id not defined`) + templateIdToTemplateMap.set(templateId, template) + + for (const parc of parcellations) { + const { ['@id']: parcId, name: parcName } = parc + if (!parcId) throw new Error(`${templateFile} / ${parcName} /@id not defined`) + parcIdToParcMap.set(parcId, parc) + + const arr = templateIdToParcsMap.get(templateId) || [] + templateIdToParcsMap.set(templateId, arr.concat(parcId) ) + + const arr2 = parcIdToTemplateId.get(parcId) || [] + parcIdToTemplateId.set(parcId, arr2.concat(templateId)) + } + } + }) + + for (const atlas of atlasFiles) { + describe(`> checking ${atlas}`, () => { + + beforeEach(async () => { + const txt = await asyncReadfile( + path.join(rootDir, 'atlas', atlas) + ) + const { ['@id']: atlasId, name: atlasName, templateSpaces, parcellations } = JSON.parse(txt) + + describe(`> checking ${atlasName}`, () => { + describe('> checking template spaces', () => { + for (const { name: tName, ['@id']: tId, availableIn } of templateSpaces) { + describe(`> checking ${tName} with id ${tId}`, () => { + for (const { ['@id']: pId, name: pName } of availableIn) { + describe(`> checking ${pName}, with id ${pId}`, () => { + it('> template maps to parc', () => { + const arr = templateIdToParcsMap.get(tId) + const idx = arr.findIndex(id => id === pId) + assert( + idx >= 0, + 'entry can be found' + ) + arr.splice(idx, 1) + }) + + it('> parc maps to tmpl', () => { + + const arr = parcIdToTemplateId.get(pId) + if (!arr) throw new Error(`${pName} cannot be found`) + const idx = arr.findIndex(id => id === tId) + assert( + idx >= 0, + 'entry can be found' + ) + arr.splice(idx, 1) + }) + }) + } + }) + } + }) + }) + + }) + + it('> dummy test', () => { + expect(true).to.equal(true) + }) + }) + } +}) diff --git a/e2e/src/advanced/browsingForDatasets.prod.e2e-spec.js b/e2e/src/advanced/browsingForDatasets.prod.e2e-spec.js index fba414f29..28900df32 100644 --- a/e2e/src/advanced/browsingForDatasets.prod.e2e-spec.js +++ b/e2e/src/advanced/browsingForDatasets.prod.e2e-spec.js @@ -75,7 +75,7 @@ describe('> dataset browser', () => { describe(`> in template: ${template}`, () => { beforeAll(async () => { await iavPage.goto() - await iavPage.selectAtlasTemplateParcellation(atlasName, template) + await iavPage.setAtlasSpecifications(atlasName, [ template ]) // account for linear template translation backend await iavPage.wait(5000) diff --git a/e2e/src/selecting/atlas.prod.e2e-spec.js b/e2e/src/selecting/atlas.prod.e2e-spec.js index 75dac89ac..e5b703809 100644 --- a/e2e/src/selecting/atlas.prod.e2e-spec.js +++ b/e2e/src/selecting/atlas.prod.e2e-spec.js @@ -19,7 +19,7 @@ describe('> atlases are generally available', () => { }) }) -describe('> generic atlaes behaviours', () => { +describe('> generic atlas behaviours', () => { let atlasPage = new AtlasPage() beforeEach(async () => { atlasPage = new AtlasPage() @@ -30,7 +30,8 @@ describe('> generic atlaes behaviours', () => { it('> on launch, shows atlas name as a pill', async () => { await atlasPage.goto() await atlasPage.clearAlerts() - await atlasPage.selectAtlasTemplateParcellation(atlas) + await atlasPage.setAtlasSpecifications(atlas) + await atlasPage.wait(500) await atlasPage.waitUntilAllChunksLoaded() const txtArr = await atlasPage.getAllChipsText() expect( @@ -40,4 +41,53 @@ describe('> generic atlaes behaviours', () => { }) }) } -}) \ No newline at end of file +}) + +describe('> in human multi level', () => { + let atlasPage = new AtlasPage() + beforeAll(async () => { + atlasPage = new AtlasPage() + await atlasPage.init() + await atlasPage.goto() + await atlasPage.clearAlerts() + }) + describe('> removal of additional layers should restore base layer', () => { + it('> in mni152', async () => { + await atlasPage.setAtlasSpecifications(atlases[1], [`Fibre tracts`, `Short Bundle`]) + await atlasPage.wait(500) + await atlasPage.waitForAsync() + + const txtArr = await atlasPage.getAllChipsText() + expect( + txtArr.find(txt => /short bundle/i.test(txt)) + ).toBeTruthy() + + await atlasPage.clickChip(/short bundle/i, '.fa-times') + + const txtArr2 = await atlasPage.getAllChipsText() + expect( + txtArr2.find(txt => /short bundle/i.test(txt)) + ).toBeFalsy() + + }) + + it('> in bigbrain', async () => { + await atlasPage.setAtlasSpecifications(atlases[1], [/isocortex/i]) + await atlasPage.wait(500) + await atlasPage.waitForAsync() + + const txtArr = await atlasPage.getAllChipsText() + expect( + txtArr.find(txt => /isocortex/i.test(txt)) + ).toBeTruthy() + + await atlasPage.clickChip(/isocortex/i, '.fa-times') + + const txtArr2 = await atlasPage.getAllChipsText() + expect( + txtArr2.find(txt => /isocortex/i.test(txt)) + ).toBeFalsy() + + }) + }) +}) diff --git a/e2e/src/selecting/region.prod.e2e-spec.js b/e2e/src/selecting/region.prod.e2e-spec.js index d2f63699d..01b30f83e 100644 --- a/e2e/src/selecting/region.prod.e2e-spec.js +++ b/e2e/src/selecting/region.prod.e2e-spec.js @@ -14,7 +14,7 @@ describe('> selecting regions', () => { const newPage = new AtlasPage() await newPage.init() await newPage.goto() - await newPage.selectAtlasTemplateParcellation(duplicatedRegion.atlas, duplicatedRegion.template) + await newPage.setAtlasSpecifications(duplicatedRegion.atlas, [ duplicatedRegion.template ]) await newPage.wait(500) await newPage.waitForAsync() await newPage.execScript(`interactiveViewer.viewerHandle.setNavigationLoc(${JSON.stringify(duplicatedRegion.position.map(v => v*1e6))}, true)`) @@ -64,7 +64,7 @@ describe('> selecting regions', () => { }) it('> on change atlas, multi region panel are dismissed', async () => { - await newPage.selectAtlasTemplateParcellation(humanAtlasName) + await newPage.setAtlasSpecifications(humanAtlasName) await newPage.wait(500) await newPage.waitForAsync() diff --git a/e2e/util/selenium/iav.js b/e2e/util/selenium/iav.js index 75f011dee..f93946027 100644 --- a/e2e/util/selenium/iav.js +++ b/e2e/util/selenium/iav.js @@ -39,7 +39,7 @@ class WdIavPage extends WdLayoutPage{ } async selectDropdownTemplate(title) { - throw new Error(`selectDropdownTemplate has been deprecated. use changeTemplate instead`) + throw new Error(`selectDropdownTemplate has been deprecated. use setAtlasSpecifications instead`) } async _getSearchRegionInput(){ diff --git a/e2e/util/selenium/layout.js b/e2e/util/selenium/layout.js index 99b7e97c6..e4a01a627 100644 --- a/e2e/util/selenium/layout.js +++ b/e2e/util/selenium/layout.js @@ -258,6 +258,20 @@ class WdLayoutPage extends WdBase{ return visibility } + async clickChip(search, cssSelector) { + const allChips = await this._getChips() + const idx = await _getIndexFromArrayOfWebElements(search, allChips) + if (idx < 0) throw new Error(`clickChip ${search.toString()} not found`) + if (!cssSelector) { + return await allChips[idx].click() + } + const el = await allChips[idx].findElement( + By.css(cssSelector) + ) + if (!el) throw new Error(`clickChip css selector ${cssSelector} not found`) + return await el.click() + } + /** * Cards */ @@ -297,7 +311,7 @@ class WdLayoutPage extends WdBase{ } async selectTitleTemplateParcellation(templateName, parcellationName){ - throw new Error(`selectTitleTemplateParcellation has been deprecated. use selectAtlasTemplateParcellation`) + throw new Error(`selectTitleTemplateParcellation has been deprecated. use setAtlasSpecifications`) } /** @@ -320,17 +334,15 @@ class WdLayoutPage extends WdBase{ } } - async changeTemplate(templateName){ - if (!templateName) throw new Error(`templateName needs to be provided`) + async selectTile(tileName){ + if (!tileName) throw new Error(`tileName needs to be provided`) await this._setAtlasSelectorExpanded(true) await this.wait(1000) - const allTiles = await this._browser - .findElement( By.css('atlas-layer-selector') ) - .findElements( By.css(`mat-grid-tile`) ) + const allTiles = await this._browser.findElements( By.css(`mat-grid-tile`) ) - const idx = await _getIndexFromArrayOfWebElements(templateName, allTiles) + const idx = await _getIndexFromArrayOfWebElements(tileName, allTiles) if (idx >= 0) await allTiles[idx].click() - else throw new Error(`#changeTemplate: templateName ${templateName} cannot be found.`) + else throw new Error(`#selectTile: tileName ${tileName} cannot be found.`) } async changeParc(parcName) { @@ -341,7 +353,7 @@ class WdLayoutPage extends WdBase{ throw new Error(`changeParcVersion NYI`) } - async selectAtlasTemplateParcellation(atlasName, templateName, parcellationName, parcVersion) { + async setAtlasSpecifications(atlasName, atlasSpecifications = [], parcVersion = null) { if (!atlasName) throw new Error(`atlasName needs to be provided`) try { /** @@ -357,16 +369,10 @@ class WdLayoutPage extends WdBase{ await this.selectDropdownOption(`[aria-label="${ARIA_LABELS.SELECT_ATLAS}"]`, atlasName) } - if (templateName) { - await this.wait(1000) - await this.waitUntilAllChunksLoaded() - await this.changeTemplate(templateName) - } - - if (parcellationName) { + for (const spec of atlasSpecifications) { await this.wait(1000) await this.waitUntilAllChunksLoaded() - await this.changeParc(parcellationName) + await this.selectTile(spec) } if (parcVersion) { diff --git a/src/res/ext/waxholmRatV2_0.json b/src/res/ext/waxholmRatV2_0.json index a6cd785fe..8c07984a4 100644 --- a/src/res/ext/waxholmRatV2_0.json +++ b/src/res/ext/waxholmRatV2_0.json @@ -13,6 +13,8 @@ "parcellations": [ { "ngId": "WHS_SD_rat_atlas_v4_beta", + "@id": "juelich/iav/atlas/v1.0.0/9", + "fullId": "juelich/iav/atlas/v1.0.0/9", "type": "parcellation", "name": "Waxholm Space rat brain atlas v4 beta", "regions": [ diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts index 16ee0b763..307ea047f 100644 --- a/src/services/state/viewerState.store.helper.ts +++ b/src/services/state/viewerState.store.helper.ts @@ -1,6 +1,6 @@ // TODO merge with viewerstate.store.ts when refactor is done -import { createReducer, on, ActionReducer, createSelector, Store, select } from "@ngrx/store"; -import { generalApplyState } from "../stateStore.helper"; +import { createReducer, on, ActionReducer, Store, select } from "@ngrx/store"; +import { generalActionError, generalApplyState } from "../stateStore.helper"; import { Effect, Actions, ofType } from "@ngrx/effects"; import { Observable } from "rxjs"; import { withLatestFrom, map } from "rxjs/operators"; @@ -112,11 +112,26 @@ export class ViewerStateHelperEffect{ @Effect() selectParcellationWithId$: Observable<any> = this.actions$.pipe( ofType(viewerStateRemoveAdditionalLayer.type), - withLatestFrom(this.store$.pipe( - select(viewerStateGetSelectedAtlas) - )), - map(([ { payload }, selectedAtlas ]) => { - const baseLayer = selectedAtlas['parcellations'].find(p => p['baseLayer']) + withLatestFrom( + this.store$.pipe( + select(viewerStateGetSelectedAtlas) + ), + this.store$.pipe( + select(viewerStateSelectedTemplateSelector) + ) + ), + map(([ { payload }, selectedAtlas, selectedTemplate ]) => { + const tmpl = selectedAtlas['templateSpaces'].find(t => t['@id'] === selectedTemplate['@id']) + if (!tmpl) { + return generalActionError({ + message: `templateSpace with id ${selectedTemplate['@id']} cannot be found in atlas with id ${selectedAtlas['@id']}` + }) + } + + const eligibleParcIdSet = new Set( + tmpl.availableIn.map(p => p['@id']) + ) + const baseLayer = selectedAtlas['parcellations'].find(fullP => fullP['baseLayer'] && eligibleParcIdSet.has(fullP['@id'])) return viewerStateHelperSelectParcellationWithId({ payload: baseLayer }) }) ) diff --git a/src/ui/atlasLayerSelector/atlasLayerSelector.template.html b/src/ui/atlasLayerSelector/atlasLayerSelector.template.html index c3e55596b..8e1293981 100644 --- a/src/ui/atlasLayerSelector/atlasLayerSelector.template.html +++ b/src/ui/atlasLayerSelector/atlasLayerSelector.template.html @@ -171,12 +171,11 @@ hasBackdrop="false"> <ng-template matMenuContent let-layerGroupItems="layerGroupItems"> - - <div iav-stop="click" - class="d-flex flex-column align-items-center" + <mat-grid-list cols="1" + rowHeight="1:1" + iav-stop="click" (iav-outsideClick)="collapseExpandedGroup()"> - - <div class="single-column-tile" *ngFor="let layer of layerGroupItems"> + <mat-grid-tile *ngFor="let layer of layerGroupItems"> <ng-container *ngTemplateOutlet="tileTmpl; context: { tileSrc: layer, @@ -186,7 +185,8 @@ } "> </ng-container> - </div> - </div> + </mat-grid-tile> + </mat-grid-list> + </ng-template> </mat-menu> -- GitLab