diff --git a/.eslintrc.js b/.eslintrc.js
index 50daf71a8a04c39b3ed039d47ec2927a76edcafc..e01d3ce69821ff914b61facb9543909eab401ae9 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -35,6 +35,7 @@ module.exports = {
     }],
     "@typescript-eslint/explicit-function-return-type": "off",
     "@typescript-eslint/no-explicit-any": "off",
-    "@typescript-eslint/no-use-before-define": "off"
+    "@typescript-eslint/no-use-before-define": "off",
+    "no-extra-boolean-cast": "off"
   }
 };
\ No newline at end of file
diff --git a/package.json b/package.json
index 8f1950bde254af599a540f749cbf6fd16776966c..05771f5d2fe1fddf8da20c1369e70e4be49e407f 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
     "@angular/platform-browser-dynamic": "^7.2.15",
     "@angular/router": "^7.2.15",
     "@ngrx/effects": "^7.4.0",
-    "@ngrx/store": "^6.0.1",
+    "@ngrx/store": "^7.4.0",
     "@ngtools/webpack": "^6.0.5",
     "@types/chart.js": "^2.7.20",
     "@types/jasmine": "^3.3.12",
@@ -59,6 +59,7 @@
     "html2canvas": "^1.0.0-rc.1",
     "jasmine": "^3.1.0",
     "jasmine-core": "^3.5.0",
+    "jasmine-marbles": "^0.6.0",
     "jasmine-spec-reporter": "^4.2.1",
     "json-loader": "^0.5.7",
     "karma": "^4.1.0",
diff --git a/src/atlasViewer/atlasViewer.history.service.spec.ts b/src/atlasViewer/atlasViewer.history.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb3bc0c8a8c2b1b8b681b41a2734b47649d937e8
--- /dev/null
+++ b/src/atlasViewer/atlasViewer.history.service.spec.ts
@@ -0,0 +1,125 @@
+import { AtlasViewerHistoryUseEffect } from './atlasViewer.history.service'
+import { TestBed, tick, fakeAsync, flush } from '@angular/core/testing'
+import { provideMockActions } from '@ngrx/effects/testing'
+import { provideMockStore } from '@ngrx/store/testing'
+import { Observable, of, Subscription } from 'rxjs'
+import { Action, Store } from '@ngrx/store'
+import { defaultRootState } from '../services/stateStore.service'
+import { HttpClientModule } from '@angular/common/http'
+import { cold } from 'jasmine-marbles'
+
+const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json')
+
+const actions$: Observable<Action> = of({type: 'TEST'})
+
+describe('atlasviewer.history.service.ts', () => {
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        HttpClientModule
+      ],
+      providers: [
+        AtlasViewerHistoryUseEffect,
+        provideMockActions(() => actions$),
+        provideMockStore({ initialState: defaultRootState })
+      ]
+    })
+  })
+
+  afterEach(() => {
+  })
+
+  describe('currentStateSearchParam$', () => {
+
+    it('should fire when template is set', () => {
+
+      const effect = TestBed.get(AtlasViewerHistoryUseEffect)
+      const store = TestBed.get(Store)
+      const { viewerState } = defaultRootState
+      store.setState({
+        ...defaultRootState,
+        viewerState: {
+          ...viewerState,
+          templateSelected: bigbrainJson
+        }
+      })
+      
+      const expected = cold('(a)', {
+        a: 'templateSelected=Big+Brain+%28Histology%29'
+      })
+      expect(effect.currentStateSearchParam$).toBeObservable(expected)
+    })
+  
+    it('should fire when template and parcellation is set', () => {
+  
+      const effect = TestBed.get(AtlasViewerHistoryUseEffect)
+      const store = TestBed.get(Store)
+      const { viewerState } = defaultRootState
+      store.setState({
+        ...defaultRootState,
+        viewerState: {
+          ...viewerState,
+          templateSelected: bigbrainJson,
+          parcellationSelected: bigbrainJson.parcellations[0]
+        }
+      })
+      
+      const expected = cold('(a)', {
+        a: 'templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps'
+      })
+      
+      expect(effect.currentStateSearchParam$).toBeObservable(expected)
+    })
+  })
+
+
+  describe('setNewSearchString$', () => {
+
+    const obj = {
+      spiedFn: () => {}
+    }
+    const subscriptions: Subscription[] = []
+
+    let spy
+
+    beforeAll(() => {
+      spy = spyOn(obj, 'spiedFn')
+    })
+
+    beforeEach(() => {
+      spy.calls.reset()
+    })
+
+    afterEach(() => {
+      while (subscriptions.length > 0) subscriptions.pop().unsubscribe()
+    })
+
+    it('should fire when set', fakeAsync(() => {
+
+      const store = TestBed.get(Store)
+      const effect = TestBed.get(AtlasViewerHistoryUseEffect)
+      subscriptions.push(
+        effect.setNewSearchString$.subscribe(obj.spiedFn)
+      )
+      const { viewerState } = defaultRootState
+  
+      store.setState({
+        ...defaultRootState,
+        viewerState: {
+          ...viewerState,
+          templateSelected: bigbrainJson,
+          parcellationSelected: bigbrainJson.parcellations[0]
+        }
+      })
+      tick(100)
+      expect(spy).toHaveBeenCalledTimes(1)
+    }))
+
+    it('should not call window.history.pushState on start', fakeAsync(() => {
+      tick(100)
+      expect(spy).toHaveBeenCalledTimes(0)
+    }))
+  
+  })
+  
+})
\ No newline at end of file
diff --git a/src/atlasViewer/atlasViewer.history.service.ts b/src/atlasViewer/atlasViewer.history.service.ts
index 4427bbd77f66fcc3292f6d704a0bbf9a9ab34b25..5df388213e91d5f7bdadd1bb938fb90be4140ad6 100644
--- a/src/atlasViewer/atlasViewer.history.service.ts
+++ b/src/atlasViewer/atlasViewer.history.service.ts
@@ -2,7 +2,7 @@ import { Injectable, OnDestroy } from "@angular/core";
 import { Actions, Effect, ofType } from '@ngrx/effects'
 import { Store } from "@ngrx/store";
 import { fromEvent, merge, of, Subscription } from "rxjs";
-import { catchError, debounceTime, distinctUntilChanged, filter, map, startWith, switchMap, switchMapTo, take, withLatestFrom } from "rxjs/operators";
+import { debounceTime, distinctUntilChanged, filter, map, startWith, switchMap, switchMapTo, take, withLatestFrom, shareReplay } from "rxjs/operators";
 import { defaultRootState, GENERAL_ACTION_TYPES, IavRootStoreInterface } from "src/services/stateStore.service";
 import { AtlasViewerConstantsServices } from "src/ui/databrowserModule/singleDataset/singleDataset.base";
 import { cvtSearchParamToState, cvtStateToSearchParam } from "./atlasViewer.urlUtil";
@@ -79,53 +79,56 @@ export class AtlasViewerHistoryUseEffect implements OnDestroy {
   private subscriptions: Subscription[] = []
 
   private currentStateSearchParam$ = this.store$.pipe(
-    map(getSearchParamStringFromState),
-    catchError((err, _obs) => {
-      // TODO error parsing current state search param. let user know
-      return of(null)
+    map(s => {
+      try {
+        return getSearchParamStringFromState(s)
+      } catch (e) {
+        // TODO parsing state to search param error
+        return null
+      }
     }),
     filter(v => v !==  null),
   )
 
+  // GENERAL_ACTION_TYPES.APPLY_STATE is triggered by pop state or initial
+  // conventiently, the action has a state property
+  public setNewSearchString$ = this.actions$.pipe(
+    ofType(GENERAL_ACTION_TYPES.APPLY_STATE),
+    // subscribe to inner obs on init
+    startWith({}),
+    switchMap(({ state }: any) =>
+      this.currentStateSearchParam$.pipe(
+        shareReplay(1),
+        distinctUntilChanged(),
+        debounceTime(100),
+
+        // compares the searchParam triggerd by change of state with the searchParam generated by GENERAL_ACTION_TYPES.APPLY_STATE
+        // if the same, the change is induced by GENERAL_ACTION_TYPES.APPLY_STATE, and should NOT be pushed to history
+        filter((newSearchParam, index) => {
+          try {
+            const oldSearchParam = (state && getSearchParamStringFromState(state)) || ''
+
+            // in the unlikely event that user returns to the exact same state without use forward/back button
+            return index > 0 || newSearchParam !== oldSearchParam
+          } catch (e) {
+            return index > 0 || newSearchParam !== ''
+          }
+        })
+      )
+    )
+  )
+
   constructor(
     private store$: Store<IavRootStoreInterface>,
     private actions$: Actions,
-    private constantService: AtlasViewerConstantsServices,
+    private constantService: AtlasViewerConstantsServices
   ) {
-    this.subscriptions.push(
-
-      // GENERAL_ACTION_TYPES.APPLY_STATE is triggered by pop state or initial
-      // conventiently, the action has a state property
-      this.actions$.pipe(
-        ofType(GENERAL_ACTION_TYPES.APPLY_STATE),
-        // subscribe to inner obs on init
-        startWith({}),
-        switchMap(({ state }: any) =>
-          this.currentStateSearchParam$.pipe(
-            distinctUntilChanged(),
-            debounceTime(100),
 
-            // compares the searchParam triggerd by change of state with the searchParam generated by GENERAL_ACTION_TYPES.APPLY_STATE
-            // if the same, the change is induced by GENERAL_ACTION_TYPES.APPLY_STATE, and should NOT be pushed to history
-            filter((newSearchParam, index) => {
-              try {
-
-                const oldSearchParam = (state && getSearchParamStringFromState(state)) || ''
-
-                // in the unlikely event that user returns to the exact same state without use forward/back button
-                return index > 0 || newSearchParam !== oldSearchParam
-              } catch (e) {
-                return index > 0 || newSearchParam !== ''
-              }
-            }),
-          ),
-        ),
-      ).subscribe(newSearchString => {
-        const url = new URL(window.location.toString())
-        url.search = newSearchString
-        window.history.pushState(newSearchString, '', url.toString())
-      }),
-    )
+    this.setNewSearchString$.subscribe(newSearchString => {
+      const url = new URL(window.location.toString())
+      url.search = newSearchString
+      window.history.pushState(newSearchString, '', url.toString())
+    })
   }
 
   public ngOnDestroy() {
diff --git a/src/atlasViewer/atlasViewer.urlUtil.spec.ts b/src/atlasViewer/atlasViewer.urlUtil.spec.ts
index bbdd2c21e27b3843ba0c8f0d494c14e99fbbad17..e6efecf95627583dff569733087ec08411528d46 100644
--- a/src/atlasViewer/atlasViewer.urlUtil.spec.ts
+++ b/src/atlasViewer/atlasViewer.urlUtil.spec.ts
@@ -2,7 +2,7 @@
 
 import {} from 'jasmine'
 import { defaultRootState } from 'src/services/stateStore.service'
-import { cvtSearchParamToState, PARSING_SEARCHPARAM_ERROR } from './atlasViewer.urlUtil'
+import { cvtSearchParamToState, PARSING_SEARCHPARAM_ERROR, cvtStateToSearchParam } from './atlasViewer.urlUtil'
 
 const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json')
 const colin = require('!json-loader!src/res/ext/colin.json')
@@ -19,6 +19,7 @@ const fetchedTemplateRootState = {
   },
 }
 
+// TODO finish writing tests
 describe('atlasViewer.urlService.service.ts', () => {
   describe('cvtSearchParamToState', () => {
     it('convert empty search param to empty state', () => {
@@ -67,5 +68,34 @@ describe('atlasViewer.urlService.service.ts', () => {
 
   describe('cvtStateToSearchParam', () => {
 
+    it('should convert template selected', () => {
+      const { viewerState } = defaultRootState
+      const searchParam = cvtStateToSearchParam({
+        ...defaultRootState,
+        viewerState: {
+          ...viewerState,
+          templateSelected: bigbrainJson,
+        }
+      })
+
+      const stringified = searchParam.toString()
+      expect(stringified).toBe('templateSelected=Big+Brain+%28Histology%29')
+    })
+  })
+
+  it('should convert template selected and parcellation selected', () => {
+
+    const { viewerState } = defaultRootState
+    const searchParam = cvtStateToSearchParam({
+      ...defaultRootState,
+      viewerState: {
+        ...viewerState,
+        templateSelected: bigbrainJson,
+        parcellationSelected: bigbrainJson.parcellations[0]
+      }
+    })
+
+    const stringified = searchParam.toString()
+    expect(stringified).toBe('templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps')
   })
 })
diff --git a/src/atlasViewer/atlasViewer.urlUtil.ts b/src/atlasViewer/atlasViewer.urlUtil.ts
index 47486727311e07699f932bf3ea9fe7b961b211be..1f0768cbdf3adb9bf1b0e3199c9d1cbb198a0f80 100644
--- a/src/atlasViewer/atlasViewer.urlUtil.ts
+++ b/src/atlasViewer/atlasViewer.urlUtil.ts
@@ -28,7 +28,7 @@ export const cvtStateToSearchParam = (state: IavRootStoreInterface): URLSearchPa
 
   // encoding states
   searchParam.set('templateSelected', templateSelected.name)
-  searchParam.set('parcellationSelected', parcellationSelected.name)
+  if (!!parcellationSelected) searchParam.set('parcellationSelected', parcellationSelected.name)
 
   // encoding selected regions
   const accumulatorMap = new Map<string, number[]>()
@@ -41,28 +41,34 @@ export const cvtStateToSearchParam = (state: IavRootStoreInterface): URLSearchPa
   for (const [key, arr] of accumulatorMap) {
     cRegionObj[key] = arr.map(n => encodeNumber(n)).join(separator)
   }
-  searchParam.set('cRegionsSelected', JSON.stringify(cRegionObj))
+  if (Object.keys(cRegionObj).length > 0) searchParam.set('cRegionsSelected', JSON.stringify(cRegionObj))
 
   // encoding navigation
-  const { orientation, perspectiveOrientation, perspectiveZoom, position, zoom } = navigation
-  const cNavString = [
-    orientation.map(n => encodeNumber(n, {float: true})).join(separator),
-    perspectiveOrientation.map(n => encodeNumber(n, {float: true})).join(separator),
-    encodeNumber(Math.floor(perspectiveZoom)),
-    Array.from(position).map((v: number) => Math.floor(v)).map(n => encodeNumber(n)).join(separator),
-    encodeNumber(Math.floor(zoom)),
-  ].join(`${separator}${separator}`)
-  searchParam.set('cNavigation', cNavString)
+  if (navigation) {
+    const { orientation, perspectiveOrientation, perspectiveZoom, position, zoom } = navigation
+    if (orientation && perspectiveOrientation && perspectiveZoom && position && zoom) {
+      const cNavString = [
+        orientation.map(n => encodeNumber(n, {float: true})).join(separator),
+        perspectiveOrientation.map(n => encodeNumber(n, {float: true})).join(separator),
+        encodeNumber(Math.floor(perspectiveZoom)),
+        Array.from(position).map((v: number) => Math.floor(v)).map(n => encodeNumber(n)).join(separator),
+        encodeNumber(Math.floor(zoom)),
+      ].join(`${separator}${separator}`)
+      searchParam.set('cNavigation', cNavString)
+    }
+  }
 
   // encode nifti layers
-  const initialNgState = templateSelected.nehubaConfig.dataset.initialNgState
-  const { layers } = ngViewerState
-  const additionalLayers = layers.filter(layer =>
-    /^blob:/.test(layer.name) &&
-    Object.keys(initialNgState.layers).findIndex(layerName => layerName === layer.name) < 0,
-  )
-  const niftiLayers = additionalLayers.filter(layer => /^nifti:\/\//.test(layer.source))
-  if (niftiLayers.length > 0) { searchParam.set('niftiLayers', niftiLayers.join('__')) }
+  if (!!templateSelected.nehubaConfig) {
+    const initialNgState = templateSelected.nehubaConfig.dataset.initialNgState
+    const { layers } = ngViewerState
+    const additionalLayers = layers.filter(layer =>
+      /^blob:/.test(layer.name) &&
+      Object.keys(initialNgState.layers).findIndex(layerName => layerName === layer.name) < 0,
+    )
+    const niftiLayers = additionalLayers.filter(layer => /^nifti:\/\//.test(layer.source))
+    if (niftiLayers.length > 0) { searchParam.set('niftiLayers', niftiLayers.join('__')) }
+  }
 
   // plugin state
   const { initManifests } = pluginState