diff --git a/src/app/group-monitor/booklet.service.spec.ts b/src/app/group-monitor/booklet/booklet.service.spec.ts
similarity index 75%
rename from src/app/group-monitor/booklet.service.spec.ts
rename to src/app/group-monitor/booklet/booklet.service.spec.ts
index d6dacc9e7aa26d5282e6fd5358f8acc89fe04507..bea117e3e128797b0af33b22d89b566bfc98861a 100644
--- a/src/app/group-monitor/booklet.service.spec.ts
+++ b/src/app/group-monitor/booklet/booklet.service.spec.ts
@@ -3,8 +3,7 @@ import { MatIconModule } from '@angular/material/icon';
 import { TestBed } from '@angular/core/testing';
 import { Observable, of } from 'rxjs';
 import { BookletService } from './booklet.service';
-import { BackendService } from './backend.service';
-import { unitTestExampleBooklets } from './test-data.spec';
+import { BackendService } from '../backend.service';
 
 class MockBackendService {
   // eslint-disable-next-line class-methods-use-this,@typescript-eslint/no-unused-vars
@@ -34,24 +33,6 @@ describe('BookletService', () => {
     expect(service).toBeTruthy();
   });
 
-  it('getFirstUnit() should get first unit if a testlet, regardless of nested sub-testlets', () => {
-    expect(BookletService.getFirstUnit(unitTestExampleBooklets.example_booklet_1.units).id).toEqual('unit-1');
-    expect(BookletService.getFirstUnit(unitTestExampleBooklets.example_booklet_2.units).id).toEqual('unit-1');
-    expect(BookletService.getFirstUnit(unitTestExampleBooklets.example_booklet_2.units.children[2])).toBeNull();
-  });
-
-  describe('getBlockById()', () => {
-    it('should return the block by id', () => {
-      const result = BookletService.getBlockById('block-2', unitTestExampleBooklets.example_booklet_1);
-      expect(result.id).toEqual('alf');
-    });
-
-    it('should return null when blockId is not found in booklet', () => {
-      const result = BookletService.getBlockById('not-existing', unitTestExampleBooklets.example_booklet_1);
-      expect(result).toBeNull();
-    });
-  });
-
   xit('parseBookletXml() should parse a Booklet-Xml to a Booklet-Object', () => {
     // TODO implement unit.test
   });
diff --git a/src/app/group-monitor/booklet.service.ts b/src/app/group-monitor/booklet/booklet.service.ts
similarity index 83%
rename from src/app/group-monitor/booklet.service.ts
rename to src/app/group-monitor/booklet/booklet.service.ts
index 7b192bf6b09f9c592c0edfcd15885cb3d79954e1..7f7d468f8e3b46f5caffa275660d7afb8c8df5a0 100644
--- a/src/app/group-monitor/booklet.service.ts
+++ b/src/app/group-monitor/booklet/booklet.service.ts
@@ -3,13 +3,13 @@
 import { Injectable } from '@angular/core';
 import { Observable, of } from 'rxjs';
 import { map, shareReplay } from 'rxjs/operators';
-import { MainDataService } from '../maindata.service';
-import { BackendService } from './backend.service';
+import { MainDataService } from '../../maindata.service';
+import { BackendService } from '../backend.service';
 import {
   Booklet, BookletError, BookletMetadata, isUnit, Restrictions, Testlet, Unit
-} from './group-monitor.interfaces';
+} from '../group-monitor.interfaces';
 // eslint-disable-next-line import/extensions
-import { BookletConfig } from '../config/booklet-config';
+import { BookletConfig } from '../../config/booklet-config';
 
 @Injectable()
 export class BookletService {
@@ -19,49 +19,6 @@ export class BookletService {
     private bs: BackendService
   ) { }
 
-  static getFirstUnit(testletOrUnit: Testlet|Unit): Unit|null {
-    while (!isUnit(testletOrUnit)) {
-      if (!testletOrUnit.children.length) {
-        return null;
-      }
-      // eslint-disable-next-line no-param-reassign,prefer-destructuring
-      testletOrUnit = testletOrUnit.children[0];
-    }
-    return testletOrUnit;
-  }
-
-  static getFirstUnitOfBlock(blockId: string, booklet: Booklet): Unit|null {
-    for (let i = 0; i < booklet.units.children.length; i++) {
-      const child = booklet.units.children[i];
-      if (!isUnit(child) && (child.blockId === blockId)) {
-        return BookletService.getFirstUnit(child);
-      }
-    }
-    return null;
-  }
-
-  static getBlockById(blockId: string, booklet: Booklet): Testlet {
-    return <Testlet>booklet.units.children
-      .filter(testletOrUnit => !isUnit(testletOrUnit))
-      .reduce((found: Testlet, block: Testlet) => ((block.blockId === blockId) ? block : found), null);
-  }
-
-  static addBookletStructureInformation(booklet: Booklet): void {
-    booklet.species = BookletService.getBookletSpecies(booklet);
-    booklet.units.children
-      .filter(testletOrUnit => !isUnit(testletOrUnit))
-      .forEach((block: Testlet, index, blocks) => {
-        block.blockId = `block ${index + 1}`;
-        if (index < blocks.length - 1) {
-          block.nextBlockId = `block ${index + 2}`;
-        }
-      });
-  }
-
-  static getBookletSpecies(booklet: Booklet): string {
-    return `species: ${booklet.units.children.filter(testletOrUnit => !isUnit(testletOrUnit)).length}`;
-  }
-
   getBooklet(bookletName = ''): Observable<Booklet|BookletError> {
     if (typeof this.booklets[bookletName] !== 'undefined') {
       return this.booklets[bookletName];
@@ -103,6 +60,22 @@ export class BookletService {
     }
   }
 
+  private static addBookletStructureInformation(booklet: Booklet): void {
+    booklet.species = BookletService.getBookletSpecies(booklet);
+    booklet.units.children
+      .filter(testletOrUnit => !isUnit(testletOrUnit))
+      .forEach((block: Testlet, index, blocks) => {
+        block.blockId = `block ${index + 1}`;
+        if (index < blocks.length - 1) {
+          block.nextBlockId = `block ${index + 2}`;
+        }
+      });
+  }
+
+  private static getBookletSpecies(booklet: Booklet): string {
+    return `species: ${booklet.units.children.filter(testletOrUnit => !isUnit(testletOrUnit)).length}`;
+  }
+
   private static parseBookletConfig(bookletElement: Element): BookletConfig {
     const bookletConfigElements = BookletService.xmlGetChildIfExists(bookletElement, 'BookletConfig', true);
     const bookletConfig = new BookletConfig();
diff --git a/src/app/group-monitor/booklet/booklet.util.spec.ts b/src/app/group-monitor/booklet/booklet.util.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..649668bd43c28b429331d52b690a53428d5d5030
--- /dev/null
+++ b/src/app/group-monitor/booklet/booklet.util.spec.ts
@@ -0,0 +1,22 @@
+import { unitTestExampleBooklets } from '../unit-test-example-data.spec';
+import { BookletUtil } from './booklet.util';
+
+describe('BookletUtil', () => {
+  it('getFirstUnit() should get first unit if a testlet, regardless of nested sub-testlets', () => {
+    expect(BookletUtil.getFirstUnit(unitTestExampleBooklets.example_booklet_1.units).id).toEqual('unit-1');
+    expect(BookletUtil.getFirstUnit(unitTestExampleBooklets.example_booklet_2.units).id).toEqual('unit-1');
+    expect(BookletUtil.getFirstUnit(unitTestExampleBooklets.example_booklet_2.units.children[2])).toBeNull();
+  });
+
+  describe('getBlockById()', () => {
+    it('should return the block by id', () => {
+      const result = BookletUtil.getBlockById('block-2', unitTestExampleBooklets.example_booklet_1);
+      expect(result.id).toEqual('alf');
+    });
+
+    it('should return null when blockId is not found in booklet', () => {
+      const result = BookletUtil.getBlockById('not-existing', unitTestExampleBooklets.example_booklet_1);
+      expect(result).toBeNull();
+    });
+  });
+});
diff --git a/src/app/group-monitor/booklet/booklet.util.ts b/src/app/group-monitor/booklet/booklet.util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..decadb25200cee31461ced4bee2b06e07bce93c4
--- /dev/null
+++ b/src/app/group-monitor/booklet/booklet.util.ts
@@ -0,0 +1,32 @@
+import {
+  Booklet, isUnit, Testlet, Unit
+} from '../group-monitor.interfaces';
+
+export class BookletUtil {
+  static getFirstUnit(testletOrUnit: Testlet|Unit): Unit|null {
+    while (!isUnit(testletOrUnit)) {
+      if (!testletOrUnit.children.length) {
+        return null;
+      }
+      // eslint-disable-next-line no-param-reassign,prefer-destructuring
+      testletOrUnit = testletOrUnit.children[0];
+    }
+    return testletOrUnit;
+  }
+
+  static getFirstUnitOfBlock(blockId: string, booklet: Booklet): Unit|null {
+    for (let i = 0; i < booklet.units.children.length; i++) {
+      const child = booklet.units.children[i];
+      if (!isUnit(child) && (child.blockId === blockId)) {
+        return BookletUtil.getFirstUnit(child);
+      }
+    }
+    return null;
+  }
+
+  static getBlockById(blockId: string, booklet: Booklet): Testlet {
+    return <Testlet>booklet.units.children
+      .filter(testletOrUnit => !isUnit(testletOrUnit))
+      .reduce((found: Testlet, block: Testlet) => ((block.blockId === blockId) ? block : found), null);
+  }
+}
diff --git a/src/app/group-monitor/group-monitor.component.css b/src/app/group-monitor/group-monitor.component.css
index 0c6eff40c6f590a74946bab15358d639703abb80..59fd008c65be41b33156e57b2dba5cd5a7cb20ee 100644
--- a/src/app/group-monitor/group-monitor.component.css
+++ b/src/app/group-monitor/group-monitor.component.css
@@ -2,25 +2,25 @@
   overflow-x: hidden;
 }
 
-.test-view-table {
+.test-session-table {
   border-collapse: collapse;
   display: table;
   width: 100%;
 }
 
-.test-view-table thead tr td {
+.test-session-table thead tr td {
   position: sticky;
   top: 0;
   z-index: 2;
   background: rgba(255, 255, 255, 0.8);
 }
 
-.test-view-table td {
+.test-session-table td {
   padding-top: 15px;
   margin-right: 1em;
 }
 
-.test-view-table td[mat-sort-header="_checked"] {
+.test-session-table td[mat-sort-header="_checked"] {
   padding-left: 5px;
 }
 
diff --git a/src/app/group-monitor/group-monitor.component.html b/src/app/group-monitor/group-monitor.component.html
index 4071b48b0dc9e2fb95576c54c7023eafbfbb19fc..37111736f0f64fe1bf2b5816eee577ff443f6570 100644
--- a/src/app/group-monitor/group-monitor.component.html
+++ b/src/app/group-monitor/group-monitor.component.html
@@ -5,7 +5,7 @@
   </p>
   <span class="fill-remaining-space"></span>
   <p>
-    <mat-chip-list *ngIf="gms.connectionStatus$ | async as connectionStatus">
+    <mat-chip-list *ngIf="connectionStatus$ | async as connectionStatus">
 
       <mat-chip [class]="connectionStatus + ' connection-status'">
         <mat-icon>
@@ -38,7 +38,7 @@
 </mat-menu>
 
 <mat-menu #filters="matMenu">
-  <button mat-menu-item *ngFor="let filterOption of gms.filterOptions; let i = index" (click)="gms.switchFilter(i)">
+  <button mat-menu-item *ngFor="let filterOption of tsm.filterOptions; let i = index" (click)="tsm.switchFilter(i)">
     <mat-icon *ngIf="filterOption.selected">check</mat-icon>
     <span>{{filterOption.label | customtext:filterOption.label | async}}</span>
   </button>
@@ -85,12 +85,12 @@
 
       <h2>{{'Test-Steuerung' | customtext:'gm_controls' | async}}</h2>
 
-      <div class="toolbar-section" *ngIf="gms.sessionsStats$ | async as sessionsStats">
+      <div class="toolbar-section" *ngIf="tsm.sessionsStats$ | async as sessionsStats">
         <mat-slide-toggle
             color="accent"
             (change)="toggleAlwaysCheckAll($event)"
-            [disabled]="!gms.checkingOptions.enableAutoCheckAll"
-            [checked]="gms.checkingOptions.autoCheckAll"
+            [disabled]="!tsm.checkingOptions.enableAutoCheckAll"
+            [checked]="tsm.checkingOptions.autoCheckAll"
             [matTooltip]="(sessionsStats.differentBookletSpecies > 1) ? (
             'Die verwendeten Booklets sind zu unterschiedlich, um gemeinsam gesteuert zu werden.'
               | customtext:'gm_multiple_booklet_species_warning'
@@ -103,7 +103,7 @@
 
       <div class="toolbar-section min-height-section">
         <ng-container *ngIf="displayOptions.manualChecking">
-          <ng-container *ngIf="gms.checkedStats$ | async as checkedStats">
+          <ng-container *ngIf="tsm.checkedStats$ | async as checkedStats">
             <alert
               *ngIf="checkedStats.number; else noCheckedSession"
               level="info"
@@ -125,12 +125,12 @@
       </div>
 
       <div class="toolbar-section">
-        <button mat-raised-button class="control" color="primary" (click)="gms.testCommandResume()">
+        <button mat-raised-button class="control" color="primary" (click)="tsm.testCommandResume()">
           <mat-icon>play_arrow</mat-icon>
           {{'weiter' | customtext:'gm_control_resume' | async}}
         </button>
 
-        <button mat-raised-button class="control" color="primary" (click)="gms.testCommandPause()">
+        <button mat-raised-button class="control" color="primary" (click)="tsm.testCommandPause()">
           <mat-icon>pause</mat-icon>
           {{'pause' | customtext:'gm_control_pause' | async}}
         </button>
@@ -208,13 +208,13 @@
           </button>
         </div>
 
-        <div class="test-view-table-wrapper">
-          <table class="test-view-table" matSort (matSortChange)="setTableSorting($event)">
+        <div class="test-session-table-wrapper">
+          <table class="test-session-table" matSort (matSortChange)="setTableSorting($event)">
             <thead>
               <tr class="mat-sort-container">
                 <td mat-sort-header="_checked" *ngIf="displayOptions.manualChecking">
                   <mat-checkbox
-                    *ngIf="gms.checkedStats$ | async as checkedStats"
+                    *ngIf="tsm.checkedStats$ | async as checkedStats"
                     (click)="$event.stopPropagation()"
                     (change)="toggleCheckAll($event)"
                     [checked]="checkedStats.all"
@@ -245,19 +245,19 @@
               </tr>
             </thead>
 
-            <ng-container *ngFor="let session of gms.sessions$ | async; trackBy: trackSession">
-              <tc-test-view
+            <ng-container *ngFor="let session of tsm.sessions$ | async; trackBy: trackSession">
+              <tc-test-session
                 [testSession]="session"
                 [displayOptions]="displayOptions"
                 [marked]="markedElement"
                 (markedElement$)="markElement($event)"
                 [selected]="selectedElement"
                 (selectedElement$)="selectElement($event)"
-                [checked]="gms.isChecked(session)"
+                [checked]="tsm.isChecked(session)"
                 (checked$)="toggleChecked($event, session)"
                 [ngStyle]="{background: getSessionColor(session)}"
               >
-              </tc-test-view>
+              </tc-test-session>
             </ng-container>
           </table>
         </div>
diff --git a/src/app/group-monitor/group-monitor.component.spec.ts b/src/app/group-monitor/group-monitor.component.spec.ts
index d336edf2e43958994a8b7691cdb7d2bd07fa756e..24fe392dfcd25a39d0113daa9b8ef3840669a0ac 100644
--- a/src/app/group-monitor/group-monitor.component.spec.ts
+++ b/src/app/group-monitor/group-monitor.component.spec.ts
@@ -22,14 +22,14 @@ import {
   TestSessionData, TestSessionSetStats
 } from './group-monitor.interfaces';
 import { BackendService } from './backend.service';
-import { TestViewComponent } from './test-view/test-view.component';
-import { GroupMonitorService } from './group-monitor.service';
+import { TestSessionComponent } from './test-session/test-session.component';
+import { TestSessionManager } from './test-session-manager/test-session-manager.service';
 import {
   unitTestSessionsStats,
   unitTestCheckedStats,
   unitTestExampleSessions,
   unitTestCommandResponse
-} from './test-data.spec';
+} from './unit-test-example-data.spec';
 import { AlertModule } from '../shared/alert/alert.module';
 
 class MockMatDialog {
@@ -55,7 +55,7 @@ class MockBackendService {
   cutConnection(): void {}
 }
 
-class MockGroupMonitorService {
+class MockTestSessionManagerService {
   checkingOptions: CheckingOptions = {
     enableAutoCheckAll: false,
     autoCheckAll: true
@@ -89,7 +89,7 @@ describe('GroupMonitorComponent', () => {
     TestBed.configureTestingModule({
       declarations: [
         GroupMonitorComponent,
-        TestViewComponent,
+        TestSessionComponent,
         CustomtextPipe
       ],
       imports: [
@@ -107,7 +107,7 @@ describe('GroupMonitorComponent', () => {
         AlertModule
       ],
       providers: [
-        { provide: GroupMonitorService, useValue: new MockGroupMonitorService() },
+        { provide: TestSessionManager, useValue: new MockTestSessionManagerService() },
         { provide: MatDialog, useValue: new MockMatDialog() },
         { provide: BackendService, useValue: new MockBackendService() }
       ]
diff --git a/src/app/group-monitor/group-monitor.component.ts b/src/app/group-monitor/group-monitor.component.ts
index 1c1016220485ffc543313b24984441ea7f23ed69..4d7e1b5d17734f72f975b688abf16cca7ae84de6 100644
--- a/src/app/group-monitor/group-monitor.component.ts
+++ b/src/app/group-monitor/group-monitor.component.ts
@@ -16,8 +16,9 @@ import {
   TestViewDisplayOptions,
   TestViewDisplayOptionKey, Selected, TestSession, TestSessionSetStats, CommandResponse, UIMessage, isBooklet
 } from './group-monitor.interfaces';
-import { GroupMonitorService } from './group-monitor.service';
-import { BookletService } from './booklet.service';
+import { TestSessionManager } from './test-session-manager/test-session-manager.service';
+import { ConnectionStatus } from '../shared/websocket-backend.service';
+import { BookletUtil } from './booklet/booklet.util';
 
 @Component({
   selector: 'app-group-monitor',
@@ -28,12 +29,14 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
   constructor(
     public dialog: MatDialog,
     private route: ActivatedRoute,
-    private bs: BackendService, // TODO move completely to service
-    public gms: GroupMonitorService,
+    private bs: BackendService,
+    public tsm: TestSessionManager,
     private router: Router,
     private cts: CustomtextService
   ) {}
 
+  connectionStatus$: Observable<ConnectionStatus>;
+
   ownGroup$: Observable<GroupData>;
   private ownGroupName = '';
 
@@ -65,21 +68,23 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
       this.route.params.subscribe(params => {
         this.ownGroup$ = this.bs.getGroupData(params['group-name']);
         this.ownGroupName = params['group-name'];
-        this.gms.connect(params['group-name']);
+        this.tsm.connect(params['group-name']);
       }),
-      this.gms.sessionsStats$.subscribe(stats => {
+      this.tsm.sessionsStats$.subscribe(stats => {
         this.onSessionsUpdate(stats);
       }),
-      this.gms.checkedStats$.subscribe(stats => {
+      this.tsm.checkedStats$.subscribe(stats => {
         this.onCheckedChange(stats);
       }),
-      this.gms.commandResponses$.subscribe(commandResponse => {
+      this.tsm.commandResponses$.subscribe(commandResponse => {
         this.messages.push(this.commandResponseToMessage(commandResponse));
       }),
-      this.gms.commandResponses$
+      this.tsm.commandResponses$
         .pipe(switchMap(() => interval(7000)))
         .subscribe(() => this.messages.shift())
     ];
+
+    this.connectionStatus$ = this.bs.connectionStatus$;
   }
 
   private commandResponseToMessage(commandResponse: CommandResponse): UIMessage {
@@ -102,7 +107,7 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
   }
 
   ngOnDestroy(): void {
-    this.gms.disconnect();
+    this.tsm.disconnect();
     this.subscriptions.forEach(subscription => subscription.unsubscribe());
   }
 
@@ -113,7 +118,7 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
   private onSessionsUpdate(stats: TestSessionSetStats): void {
     this.displayOptions.highlightSpecies = (stats.differentBookletSpecies > 1);
 
-    if (!this.gms.checkingOptions.enableAutoCheckAll) {
+    if (!this.tsm.checkingOptions.enableAutoCheckAll) {
       this.displayOptions.manualChecking = true;
     }
   }
@@ -130,7 +135,7 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
     if (!sort.active || sort.direction === '') {
       return;
     }
-    this.gms.sortBy$.next(sort);
+    this.tsm.sortBy$.next(sort);
   }
 
   setDisplayOption(option: string, value: TestViewDisplayOptions[TestViewDisplayOptionKey]): void {
@@ -180,7 +185,7 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
   }
 
   selectElement(selected: Selected): void {
-    this.gms.checkSessionsBySelection(selected);
+    this.tsm.checkSessionsBySelection(selected);
     this.selectedElement = selected;
   }
 
@@ -198,7 +203,7 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
     dialogRef.afterClosed().subscribe((confirmed: boolean) => {
       if (confirmed) {
         this.isClosing = true;
-        this.gms.commandFinishEverything()
+        this.tsm.commandFinishEverything()
           .subscribe(() => {
             setTimeout(() => { this.router.navigateByUrl('/r/login'); }, 5000); // go away
           });
@@ -214,7 +219,7 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
         text: 'Kein Zielblock ausgewählt'
       });
     } else {
-      this.gms.testCommandGoto(this.selectedElement)
+      this.tsm.testCommandGoto(this.selectedElement)
         .subscribe(() => this.selectNextBlock());
     }
   }
@@ -225,7 +230,7 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
     }
     this.selectedElement = {
       element: this.selectedElement.element.nextBlockId ?
-        BookletService.getBlockById(
+        BookletUtil.getBlockById(
           this.selectedElement.element.nextBlockId,
           this.selectedElement.originSession.booklet
         ) : null,
@@ -236,40 +241,40 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
   }
 
   unlockCommand(): void {
-    this.gms.testCommandUnlock();
+    this.tsm.testCommandUnlock();
   }
 
   toggleChecked(checked: boolean, session: TestSession): void {
-    if (!this.gms.isChecked(session)) {
-      this.gms.checkSession(session);
+    if (!this.tsm.isChecked(session)) {
+      this.tsm.checkSession(session);
     } else {
-      this.gms.uncheckSession(session);
+      this.tsm.uncheckSession(session);
     }
   }
 
   invertChecked(event: Event): boolean {
     event.preventDefault();
-    this.gms.invertChecked();
+    this.tsm.invertChecked();
     return false;
   }
 
   toggleAlwaysCheckAll(event: MatSlideToggleChange): void {
-    if (this.gms.checkingOptions.enableAutoCheckAll && event.checked) {
-      this.gms.checkAll();
+    if (this.tsm.checkingOptions.enableAutoCheckAll && event.checked) {
+      this.tsm.checkAll();
       this.displayOptions.manualChecking = false;
-      this.gms.checkingOptions.autoCheckAll = true;
+      this.tsm.checkingOptions.autoCheckAll = true;
     } else {
-      this.gms.checkNone();
+      this.tsm.checkNone();
       this.displayOptions.manualChecking = true;
-      this.gms.checkingOptions.autoCheckAll = false;
+      this.tsm.checkingOptions.autoCheckAll = false;
     }
   }
 
   toggleCheckAll(event: MatCheckboxChange): void {
     if (event.checked) {
-      this.gms.checkAll();
+      this.tsm.checkAll();
     } else {
-      this.gms.checkNone();
+      this.tsm.checkNone();
     }
   }
 }
diff --git a/src/app/group-monitor/group-monitor.module.ts b/src/app/group-monitor/group-monitor.module.ts
index 00625b1fd19740a5f662387a029a2a6bc5e13e79..08c5bf51de3628f5a235b84756fcc858e3d25a25 100644
--- a/src/app/group-monitor/group-monitor.module.ts
+++ b/src/app/group-monitor/group-monitor.module.ts
@@ -20,16 +20,15 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
 import { GroupMonitorRoutingModule } from './group-monitor-routing.module';
 import { GroupMonitorComponent } from './group-monitor.component';
 import { BackendService } from './backend.service';
-import { BookletService } from './booklet.service';
-import { TestViewComponent } from './test-view/test-view.component';
-import { TestSessionService } from './test-session.service';
-import { GroupMonitorService } from './group-monitor.service';
+import { BookletService } from './booklet/booklet.service';
+import { TestSessionComponent } from './test-session/test-session.component';
+import { TestSessionManager } from './test-session-manager/test-session-manager.service';
 import { AlertModule } from '../shared/alert/alert.module';
 
 @NgModule({
   declarations: [
     GroupMonitorComponent,
-    TestViewComponent
+    TestSessionComponent
   ],
   imports: [
     CommonModule,
@@ -55,8 +54,7 @@ import { AlertModule } from '../shared/alert/alert.module';
   providers: [
     BackendService,
     BookletService,
-    TestSessionService,
-    GroupMonitorService
+    TestSessionManager
   ]
 })
 export class GroupMonitorModule {
diff --git a/src/app/group-monitor/group-monitor.service.spec.ts b/src/app/group-monitor/test-session-manager/test-session-manager.service.spec.ts
similarity index 94%
rename from src/app/group-monitor/group-monitor.service.spec.ts
rename to src/app/group-monitor/test-session-manager/test-session-manager.service.spec.ts
index 610d30a7170468d056883d6084621f433c74cbb4..1e03671db776dc1375b6bfe628e196ab67f1d534 100644
--- a/src/app/group-monitor/group-monitor.service.spec.ts
+++ b/src/app/group-monitor/test-session-manager/test-session-manager.service.spec.ts
@@ -8,11 +8,13 @@ import {
   BookletError, CommandResponse,
   GroupData, Selected, Testlet,
   TestSessionData, TestSessionFilter, TestSessionSetStats
-} from './group-monitor.interfaces';
-import { BookletService } from './booklet.service';
-import { BackendService } from './backend.service';
-import { GroupMonitorService } from './group-monitor.service';
-import { unitTestExampleSessions, unitTestExampleBooklets, additionalUnitTestExampleSessions } from './test-data.spec';
+} from '../group-monitor.interfaces';
+import { BookletService } from '../booklet/booklet.service';
+import { BackendService } from '../backend.service';
+import { TestSessionManager } from './test-session-manager.service';
+import {
+  unitTestExampleSessions, unitTestExampleBooklets, additionalUnitTestExampleSessions
+} from '../unit-test-example-data.spec';
 
 class MockBookletService {
   booklets: Observable<Booklet>[] = [of(unitTestExampleBooklets.example_booklet_1)];
@@ -58,21 +60,21 @@ class MockCustomtextPipe {
   }
 }
 
-describe('GroupMonitorService', () => {
-  let service: GroupMonitorService;
+describe('TestSessionManager', () => {
+  let service: TestSessionManager;
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       declarations: [],
       imports: [],
       providers: [
-        GroupMonitorService,
+        TestSessionManager,
         { provide: BookletService, useValue: new MockBookletService() },
         { provide: BackendService, useValue: new MockBackendService() }
       ]
     })
       .compileComponents();
-    service = TestBed.inject(GroupMonitorService);
+    service = TestBed.inject(TestSessionManager);
     service.connect('unit-test-group-name');
   }));
 
@@ -161,7 +163,7 @@ describe('GroupMonitorService', () => {
   describe('getSessionSetStats', () => {
     it('should fetch correct stats from sessions', () => {
       // eslint-disable-next-line @typescript-eslint/dot-notation
-      const result = GroupMonitorService['getSessionSetStats'](unitTestExampleSessions, 2);
+      const result = TestSessionManager['getSessionSetStats'](unitTestExampleSessions, 2);
       const expectation: TestSessionSetStats = {
         number: 3,
         differentBooklets: 3,
@@ -176,7 +178,7 @@ describe('GroupMonitorService', () => {
 
   describe('filterSessions', () => {
     // eslint-disable-next-line @typescript-eslint/dot-notation
-    const filterSessions = GroupMonitorService['filterSessions'];
+    const filterSessions = TestSessionManager['filterSessions'];
 
     it('should filter the sessions array by various filters', () => {
       const sessionsSet = [...unitTestExampleSessions];
@@ -245,7 +247,7 @@ describe('GroupMonitorService', () => {
       };
       const sessions = [...unitTestExampleSessions, ...additionalUnitTestExampleSessions];
       // eslint-disable-next-line @typescript-eslint/dot-notation
-      const result = GroupMonitorService['groupForGoto'](sessions, selection);
+      const result = TestSessionManager['groupForGoto'](sessions, selection);
       expect(result).toEqual({
         example_booklet_1: { testIds: [1, 33], firstUnitId: 'unit-3' },
         example_booklet_3: { testIds: [34], firstUnitId: 'unit-1' }
diff --git a/src/app/group-monitor/group-monitor.service.ts b/src/app/group-monitor/test-session-manager/test-session-manager.service.ts
similarity index 87%
rename from src/app/group-monitor/group-monitor.service.ts
rename to src/app/group-monitor/test-session-manager/test-session-manager.service.ts
index e67cb87663cf0b37406f1314d7d1293a0c1e2515..6ef84b7ec3181bd182ff853ba17dfd8523c68dd0 100644
--- a/src/app/group-monitor/group-monitor.service.ts
+++ b/src/app/group-monitor/test-session-manager/test-session-manager.service.ts
@@ -6,26 +6,24 @@ import { Sort } from '@angular/material/sort';
 import {
   delay, flatMap, map, switchMap, tap
 } from 'rxjs/operators';
-import { BackendService } from './backend.service';
-import { BookletService } from './booklet.service';
-import { TestSessionService } from './test-session.service';
+import { BackendService } from '../backend.service';
+import { BookletService } from '../booklet/booklet.service';
+import { TestSessionUtil } from '../test-session/test-session.util';
 import {
   isBooklet,
   Selected, CheckingOptions,
   TestSession,
   TestSessionFilter, TestSessionSetStats,
   TestSessionsSuperStates, CommandResponse, GotoCommandData
-} from './group-monitor.interfaces';
-import { ConnectionStatus } from '../shared/websocket-backend.service';
+} from '../group-monitor.interfaces';
+import { BookletUtil } from '../booklet/booklet.util';
 
 @Injectable()
-export class GroupMonitorService {
+export class TestSessionManager {
   sortBy$: Subject<Sort>;
   filters$: Subject<TestSessionFilter[]>;
   checkingOptions: CheckingOptions;
 
-  connectionStatus$: Observable<ConnectionStatus>;
-
   private groupName: string;
 
   get sessions$(): Observable<TestSession[]> {
@@ -96,8 +94,8 @@ export class GroupMonitorService {
       autoCheckAll: true
     };
 
-    this._checkedStats$ = new BehaviorSubject<TestSessionSetStats>(GroupMonitorService.getEmptyStats());
-    this._sessionsStats$ = new BehaviorSubject<TestSessionSetStats>(GroupMonitorService.getEmptyStats());
+    this._checkedStats$ = new BehaviorSubject<TestSessionSetStats>(TestSessionManager.getEmptyStats());
+    this._sessionsStats$ = new BehaviorSubject<TestSessionSetStats>(TestSessionManager.getEmptyStats());
     this._commandResponses$ = new Subject<CommandResponse>();
 
     this.monitor$ = this.bs.observeSessionsMonitor()
@@ -105,7 +103,7 @@ export class GroupMonitorService {
         switchMap(sessions => zip(...sessions
           .map(session => this.bookletService.getBooklet(session.bookletName)
             .pipe(
-              map(booklet => TestSessionService.analyzeTestSession(session, booklet))
+              map(booklet => TestSessionUtil.analyzeTestSession(session, booklet))
             ))))
       );
 
@@ -113,12 +111,10 @@ export class GroupMonitorService {
     combineLatest<[Sort, TestSessionFilter[], TestSession[]]>([this.sortBy$, this.filters$, this.monitor$])
       .pipe(
         // eslint-disable-next-line max-len
-        map(([sortBy, filters, sessions]) => this.sortSessions(sortBy, GroupMonitorService.filterSessions(sessions, filters))),
+        map(([sortBy, filters, sessions]) => this.sortSessions(sortBy, TestSessionManager.filterSessions(sessions, filters))),
         tap(sessions => this.synchronizeChecked(sessions))
       )
       .subscribe(this._sessions$);
-
-    this.connectionStatus$ = this.bs.connectionStatus$;
   }
 
   disconnect(): void {
@@ -139,7 +135,7 @@ export class GroupMonitorService {
   private static filterSessions(sessions: TestSession[], filters: TestSessionFilter[]): TestSession[] {
     return sessions
       .filter(session => session.data.testId && session.data.testId > -1) // testsession without testId is deprecated
-      .filter(session => GroupMonitorService.applyFilters(session, filters));
+      .filter(session => TestSessionManager.applyFilters(session, filters));
   }
 
   private static applyFilters(session: TestSession, filters: TestSessionFilter[]): boolean {
@@ -187,7 +183,7 @@ export class GroupMonitorService {
   }
 
   private synchronizeChecked(sessions: TestSession[]): void {
-    const sessionsStats = GroupMonitorService.getSessionSetStats(sessions);
+    const sessionsStats = TestSessionManager.getSessionSetStats(sessions);
 
     this.checkingOptions.enableAutoCheckAll = (sessionsStats.differentBookletSpecies < 2);
 
@@ -204,7 +200,7 @@ export class GroupMonitorService {
       });
     this._checked = newCheckedSessions;
 
-    this._checkedStats$.next(GroupMonitorService.getSessionSetStats(Object.values(this._checked), sessions.length));
+    this._checkedStats$.next(TestSessionManager.getSessionSetStats(Object.values(this._checked), sessions.length));
     this._sessionsStats$.next(sessionsStats);
   }
 
@@ -248,7 +244,7 @@ export class GroupMonitorService {
 
   testCommandPause(): void {
     const testIds = this.checked
-      .filter(session => !TestSessionService.isPaused(session))
+      .filter(session => !TestSessionUtil.isPaused(session))
       .map(session => session.data.testId);
     if (!testIds.length) {
       this._commandResponses$.next({ commandType: 'pause', testIds });
@@ -272,7 +268,7 @@ export class GroupMonitorService {
   }
 
   testCommandGoto(selection: Selected): Observable<true> {
-    const gfd = GroupMonitorService.groupForGoto(this.checked, selection);
+    const gfd = TestSessionManager.groupForGoto(this.checked, selection);
     const allTestIds = this.checked.map(s => s.data.testId);
     return zip(
       ...Object.keys(gfd).map(key => this.bs.command('goto', ['id', gfd[key].firstUnitId], gfd[key].testIds))
@@ -291,7 +287,7 @@ export class GroupMonitorService {
     const groupedByBooklet: GotoCommandData = {};
     sessionsSet.forEach(session => {
       if (!groupedByBooklet[session.data.bookletName] && isBooklet(session.booklet)) {
-        const firstUnit = BookletService.getFirstUnitOfBlock(selection.element.blockId, session.booklet);
+        const firstUnit = BookletUtil.getFirstUnitOfBlock(selection.element.blockId, session.booklet);
         if (firstUnit) {
           groupedByBooklet[session.data.bookletName] = {
             testIds: [],
@@ -308,7 +304,7 @@ export class GroupMonitorService {
 
   testCommandUnlock(): void {
     const testIds = this.checked
-      .filter(TestSessionService.isLocked)
+      .filter(TestSessionUtil.isLocked)
       .map(session => session.data.testId);
     if (!testIds.length) {
       this._commandResponses$.next({ commandType: 'unlock', testIds });
@@ -322,14 +318,14 @@ export class GroupMonitorService {
   // todo unit test
   commandFinishEverything(): Observable<CommandResponse> {
     const getUnlockedConnectedTestIds = () => Object.values(this._sessions$.getValue())
-      .filter(session => !TestSessionService.hasState(session.data.testState, 'status', 'locked') &&
-        !TestSessionService.hasState(session.data.testState, 'CONTROLLER', 'TERMINATED') &&
-        (TestSessionService.hasState(session.data.testState, 'CONNECTION', 'POLLING') ||
-          TestSessionService.hasState(session.data.testState, 'CONNECTION', 'WEBSOCKET')))
+      .filter(session => !TestSessionUtil.hasState(session.data.testState, 'status', 'locked') &&
+        !TestSessionUtil.hasState(session.data.testState, 'CONTROLLER', 'TERMINATED') &&
+        (TestSessionUtil.hasState(session.data.testState, 'CONNECTION', 'POLLING') ||
+          TestSessionUtil.hasState(session.data.testState, 'CONNECTION', 'WEBSOCKET')))
       .map(session => session.data.testId);
     const getUnlockedTestIds = () => Object.values(this._sessions$.getValue())
       .filter(session => session.data.testId > 0)
-      .filter(session => !TestSessionService.hasState(session.data.testState, 'status', 'locked'))
+      .filter(session => !TestSessionUtil.hasState(session.data.testState, 'status', 'locked'))
       .map(session => session.data.testId);
 
     this.filters$.next([]);
@@ -414,7 +410,7 @@ export class GroupMonitorService {
   }
 
   private onCheckedChanged(): void {
-    this._checkedStats$.next(GroupMonitorService.getSessionSetStats(this.checked, this.sessions.length));
+    this._checkedStats$.next(TestSessionManager.getSessionSetStats(this.checked, this.sessions.length));
   }
 
   private static getSessionSetStats(sessionSet: TestSession[], all: number = sessionSet.length): TestSessionSetStats {
@@ -427,8 +423,8 @@ export class GroupMonitorService {
       .forEach(session => {
         booklets.add(session.data.bookletName);
         bookletSpecies.add(session.booklet.species);
-        if (TestSessionService.isPaused(session)) paused += 1;
-        if (TestSessionService.isLocked(session)) locked += 1;
+        if (TestSessionUtil.isPaused(session)) paused += 1;
+        if (TestSessionUtil.isLocked(session)) locked += 1;
       });
 
     return {
diff --git a/src/app/group-monitor/test-view/super-states.js b/src/app/group-monitor/test-session/super-states.js
similarity index 100%
rename from src/app/group-monitor/test-view/super-states.js
rename to src/app/group-monitor/test-session/super-states.js
diff --git a/src/app/group-monitor/test-view/test-view.component.css b/src/app/group-monitor/test-session/test-session.component.css
similarity index 84%
rename from src/app/group-monitor/test-view/test-view.component.css
rename to src/app/group-monitor/test-session/test-session.component.css
index c5ae762d7925132e2d62a91f1e547a477d74c2bd..9db1aecb229c78da2afd7c440c4042b07c500aee 100644
--- a/src/app/group-monitor/test-view/test-view.component.css
+++ b/src/app/group-monitor/test-session/test-session.component.css
@@ -1,3 +1,38 @@
+:host(tc-test-session) {
+  display: table-row;
+  vertical-align: middle;
+}
+
+td {
+  padding-bottom: 0.2em;
+  padding-top: 0.2em;
+  border-bottom: 1px solid silver;
+  padding-right: 2em;
+}
+
+:host(tc-test-session):last-of-type td {
+  border-bottom: none;
+}
+
+td.super-state,
+td.selected,
+td:last-child {
+  padding-right: 0;
+}
+
+td.selected {
+  padding-left: 5px;
+}
+
+td:last-child {
+  min-width: 100%;
+}
+
+:host(test-session:last-child) td {
+  border-bottom: none;
+
+}
+
 h1,
 h2 {
   font-size: 100%;
diff --git a/src/app/group-monitor/test-view/test-view.component.html b/src/app/group-monitor/test-session/test-session.component.html
similarity index 100%
rename from src/app/group-monitor/test-view/test-view.component.html
rename to src/app/group-monitor/test-session/test-session.component.html
diff --git a/src/app/group-monitor/test-view/test-view.component.spec.ts b/src/app/group-monitor/test-session/test-session.component.spec.ts
similarity index 74%
rename from src/app/group-monitor/test-view/test-view.component.spec.ts
rename to src/app/group-monitor/test-session/test-session.component.spec.ts
index f0f4890107cc1c54e980992690afe49f2b987ae2..a142205a19a84d32fd4581e6b446b1016c95118f 100644
--- a/src/app/group-monitor/test-view/test-view.component.spec.ts
+++ b/src/app/group-monitor/test-session/test-session.component.spec.ts
@@ -2,24 +2,24 @@ import { MatIconModule } from '@angular/material/icon';
 import { MatTooltipModule } from '@angular/material/tooltip';
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 import { MatCheckboxModule } from '@angular/material/checkbox';
-import { TestViewComponent } from './test-view.component';
+import { TestSessionComponent } from './test-session.component';
 import { TestViewDisplayOptions } from '../group-monitor.interfaces';
-import { unitTestExampleSessions } from '../test-data.spec';
+import { unitTestExampleSessions } from '../unit-test-example-data.spec';
 
 describe('TestViewComponent', () => {
-  let component: TestViewComponent;
-  let fixture: ComponentFixture<TestViewComponent>;
+  let component: TestSessionComponent;
+  let fixture: ComponentFixture<TestSessionComponent>;
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
-      declarations: [TestViewComponent],
+      declarations: [TestSessionComponent],
       imports: [MatIconModule, MatTooltipModule, MatCheckboxModule]
     })
       .compileComponents();
   }));
 
   beforeEach(() => {
-    fixture = TestBed.createComponent(TestViewComponent);
+    fixture = TestBed.createComponent(TestSessionComponent);
     component = fixture.componentInstance;
     component.testSession = unitTestExampleSessions[0];
     component.displayOptions = <TestViewDisplayOptions>{
diff --git a/src/app/group-monitor/test-view/test-view.component.ts b/src/app/group-monitor/test-session/test-session.component.ts
similarity index 90%
rename from src/app/group-monitor/test-view/test-view.component.ts
rename to src/app/group-monitor/test-session/test-session.component.ts
index ac9abc69ceb21ac87227297131c3a0731388b696..27fb7a797dd100a76336ef951a1f912c47549a53 100644
--- a/src/app/group-monitor/test-view/test-view.component.ts
+++ b/src/app/group-monitor/test-session/test-session.component.ts
@@ -6,7 +6,7 @@ import {
   Testlet, Unit, TestViewDisplayOptions,
   isUnit, Selected, TestSession, TestSessionSuperState
 } from '../group-monitor.interfaces';
-import { TestSessionService } from '../test-session.service';
+import { TestSessionUtil } from './test-session.util';
 import { superStates } from './super-states';
 
 interface IconData {
@@ -17,11 +17,11 @@ interface IconData {
 }
 
 @Component({
-  selector: 'tc-test-view',
-  templateUrl: './test-view.component.html',
-  styleUrls: ['./test-view.component.css', './test-view-table.css']
+  selector: 'tc-test-session',
+  templateUrl: './test-session.component.html',
+  styleUrls: ['./test-session.component.css']
 })
-export class TestViewComponent {
+export class TestSessionComponent {
   @Input() testSession: TestSession;
   @Input() displayOptions: TestViewDisplayOptions;
   @Input() marked: Selected;
@@ -34,9 +34,9 @@ export class TestViewComponent {
 
   superStateIcons: { [key in TestSessionSuperState]: IconData } = superStates;
 
-  stateString = TestSessionService.stateString;
+  stateString = TestSessionUtil.stateString;
 
-  hasState = TestSessionService.hasState;
+  hasState = TestSessionUtil.hasState;
 
   getTestletType = (testletOrUnit: Unit|Testlet): 'testlet'|'unit' => (isUnit(testletOrUnit) ? 'unit' : 'testlet');
 
diff --git a/src/app/group-monitor/test-session.service.spec.ts b/src/app/group-monitor/test-session/test-session.util.spec.ts
similarity index 72%
rename from src/app/group-monitor/test-session.service.spec.ts
rename to src/app/group-monitor/test-session/test-session.util.spec.ts
index f2f428c50e6de4883d310d3278044aff02da6bce..e06707dc9fa5535b5281695c723b706e7074035f 100644
--- a/src/app/group-monitor/test-session.service.spec.ts
+++ b/src/app/group-monitor/test-session/test-session.util.spec.ts
@@ -1,26 +1,9 @@
 /* eslint-disable object-curly-newline */
-import { TestBed } from '@angular/core/testing';
-import { TestSessionService } from './test-session.service';
-import { unitTestExampleBooklets } from './test-data.spec';
-import { Testlet, UnitContext } from './group-monitor.interfaces';
-
-describe('TestSessionService', () => {
-  let service: TestSessionService;
-
-  beforeEach(() => {
-    TestBed.configureTestingModule({
-      providers: [
-        TestSessionService
-      ]
-    });
-    service = TestBed.inject(TestSessionService);
-  });
-
-  it('should be created', () => {
-    expect(service)
-      .toBeTruthy();
-  });
+import { TestSessionUtil } from './test-session.util';
+import { unitTestExampleBooklets } from '../unit-test-example-data.spec';
+import { Testlet, UnitContext } from '../group-monitor.interfaces';
 
+describe('TestSessionUtil', () => {
   describe('getCurrent()', () => {
     it('should find correct indices for unit, parent and ancestor ( = top-level-testlet or root)', () => {
       const fakeTestlet = (id: string): Testlet => ({
@@ -104,7 +87,7 @@ describe('TestSessionService', () => {
 
       for (let i = 1; i < 11; i++) {
         // eslint-disable-next-line @typescript-eslint/dot-notation
-        const result = TestSessionService['getCurrent'](unitTestExampleBooklets.example_booklet_1.units, `unit-${i}`);
+        const result = TestSessionUtil['getCurrent'](unitTestExampleBooklets.example_booklet_1.units, `unit-${i}`);
         const expectation = expectations[`unit-${i}`];
         expect(result.indexGlobal)
           .withContext(`global index of unit-${i}`)
@@ -129,7 +112,7 @@ describe('TestSessionService', () => {
 
     it('should find return a unitContext without unit for not existing id', () => {
       // eslint-disable-next-line @typescript-eslint/dot-notation
-      const result = TestSessionService['getCurrent'](unitTestExampleBooklets.example_booklet_1.units, 'not-existing');
+      const result = TestSessionUtil['getCurrent'](unitTestExampleBooklets.example_booklet_1.units, 'not-existing');
       expect(result.unit).toBeNull();
     });
   });
@@ -141,15 +124,15 @@ describe('TestSessionService', () => {
         second_key: 'second_value'
       };
 
-      let result = TestSessionService.stateString(stateObject, ['first_key'], '|');
+      let result = TestSessionUtil.stateString(stateObject, ['first_key'], '|');
       let expectation = 'first_value';
       expect(result).withContext('one existing value').toEqual(expectation);
 
-      result = TestSessionService.stateString(stateObject, ['first_key', 'second_key'], '|');
+      result = TestSessionUtil.stateString(stateObject, ['first_key', 'second_key'], '|');
       expectation = 'first_value|second_value';
       expect(result).withContext('two existing values').toEqual(expectation);
 
-      result = TestSessionService.stateString(stateObject, ['first_key', 'second_key', 'not_existing'], '|');
+      result = TestSessionUtil.stateString(stateObject, ['first_key', 'second_key', 'not_existing'], '|');
       expectation = 'first_value|second_value';
       expect(result).withContext('two existing values and one not existing').toEqual(expectation);
     });
@@ -162,29 +145,17 @@ describe('TestSessionService', () => {
         second_key: null
       };
 
-      let result = TestSessionService.hasState(stateObject, 'first_key', 'first_value');
+      let result = TestSessionUtil.hasState(stateObject, 'first_key', 'first_value');
       expect(result).withContext('key exists and has value').toBeTrue();
 
-      result = TestSessionService.hasState(stateObject, 'first_key', 'something_else');
+      result = TestSessionUtil.hasState(stateObject, 'first_key', 'something_else');
       expect(result).withContext('key exists and not has value').toBeFalse();
 
-      result = TestSessionService.hasState(stateObject, 'first_key');
+      result = TestSessionUtil.hasState(stateObject, 'first_key');
       expect(result).withContext('key exists').toBeTrue();
 
-      result = TestSessionService.hasState(stateObject, 'non_existing_key');
+      result = TestSessionUtil.hasState(stateObject, 'non_existing_key');
       expect(result).withContext('key exists not').toBeFalse();
     });
   });
-
-  describe('parseJsonState()', () => {
-    xit('should parse an string containing a state-object', () => {
-      // TOOD implement unit-test
-    });
-  });
-
-  describe('getMode()', () => {
-    xit('should transform mode-string into label', () => {
-      // TOOD implement unit-test
-    });
-  });
 });
diff --git a/src/app/group-monitor/test-session.service.ts b/src/app/group-monitor/test-session/test-session.util.ts
similarity index 80%
rename from src/app/group-monitor/test-session.service.ts
rename to src/app/group-monitor/test-session/test-session.util.ts
index 9ca3c31a723264cc703155c0cfbf4e61411c769d..369e440dbbb814fba885b04d5e1a02aecdbfaa87 100644
--- a/src/app/group-monitor/test-session.service.ts
+++ b/src/app/group-monitor/test-session/test-session.util.ts
@@ -1,4 +1,3 @@
-import { Injectable } from '@angular/core';
 import {
   Booklet,
   BookletError, isBooklet,
@@ -8,37 +7,36 @@ import {
   TestSessionData,
   TestSessionSuperState,
   UnitContext
-} from './group-monitor.interfaces';
+} from '../group-monitor.interfaces';
 
-@Injectable()
-export class TestSessionService {
+export class TestSessionUtil {
   static hasState(state: Record<string, unknown>, key: string, value = null): boolean {
     return ((typeof state[key] !== 'undefined') && ((value !== null) ? (state[key] === value) : true));
   }
 
   static isPaused(session: TestSession): boolean {
-    return TestSessionService.hasState(session.data.testState, 'CONTROLLER', 'PAUSED');
+    return TestSessionUtil.hasState(session.data.testState, 'CONTROLLER', 'PAUSED');
   }
 
   static isLocked(session: TestSession): boolean {
-    return TestSessionService.hasState(session.data.testState, 'status', 'locked');
+    return TestSessionUtil.hasState(session.data.testState, 'status', 'locked');
   }
 
   static analyzeTestSession(session: TestSessionData, booklet: Booklet | BookletError): TestSession {
-    const current = isBooklet(booklet) ? TestSessionService.getCurrent(booklet.units, session.unitName) : null;
+    const current = isBooklet(booklet) ? TestSessionUtil.getCurrent(booklet.units, session.unitName) : null;
     return {
       data: session,
-      state: TestSessionService.getSuperState(session),
+      state: TestSessionUtil.getSuperState(session),
       current: current && current.unit ? current : null,
       booklet,
-      timeLeft: TestSessionService.parseJsonState(session.testState, 'TESTLETS_TIMELEFT'),
-      clearedCodes: TestSessionService.parseJsonState(session.testState, 'TESTLETS_CLEARED_CODE')
+      timeLeft: TestSessionUtil.parseJsonState(session.testState, 'TESTLETS_TIMELEFT'),
+      clearedCodes: TestSessionUtil.parseJsonState(session.testState, 'TESTLETS_CLEARED_CODE')
     };
   }
 
   static stateString(state: Record<string, string>, keys: string[], glue = ''): string {
     return keys
-      .map((key: string) => (TestSessionService.hasState(state, key) ? state[key] : null))
+      .map((key: string) => (TestSessionUtil.hasState(state, key) ? state[key] : null))
       .filter((value: string) => value !== null)
       .join(glue);
   }
@@ -67,7 +65,7 @@ export class TestSessionService {
     if (this.hasState(state, 'FOCUS', 'HAS_NOT')) {
       return 'focus_lost';
     }
-    if (TestSessionService.idleSinceMinutes(session) > 5) {
+    if (TestSessionUtil.idleSinceMinutes(session) > 5) {
       return 'idle';
     }
     if (this.hasState(state, 'CONNECTION', 'WEBSOCKET')) {
@@ -121,7 +119,7 @@ export class TestSessionService {
           return result;
         }
       } else {
-        const subResult = TestSessionService.getCurrent(child, searchUnitId, level + 1, {
+        const subResult = TestSessionUtil.getCurrent(child, searchUnitId, level + 1, {
           unit: null,
           parent: child,
           ancestor: level < 1 ? child : result.ancestor,
diff --git a/src/app/group-monitor/test-view/test-view-table.css b/src/app/group-monitor/test-view/test-view-table.css
deleted file mode 100644
index af59988162ad3f4fc67be4d78d4b3c174d17c6a5..0000000000000000000000000000000000000000
--- a/src/app/group-monitor/test-view/test-view-table.css
+++ /dev/null
@@ -1,34 +0,0 @@
-:host(tc-test-view) {
-  display: table-row;
-  vertical-align: middle;
-}
-
-td {
-  padding-bottom: 0.2em;
-  padding-top: 0.2em;
-  border-bottom: 1px solid silver;
-  padding-right: 2em;
-}
-
-:host(tc-test-view):last-of-type td {
-  border-bottom: none;
-}
-
-td.super-state,
-td.selected,
-td:last-child {
-  padding-right: 0;
-}
-
-td.selected {
-  padding-left: 5px;
-}
-
-td:last-child {
-  min-width: 100%;
-}
-
-:host(test-view:last-child) td {
-  border-bottom: none;
-
-}
diff --git a/src/app/group-monitor/test-data.spec.ts b/src/app/group-monitor/unit-test-example-data.spec.ts
similarity index 97%
rename from src/app/group-monitor/test-data.spec.ts
rename to src/app/group-monitor/unit-test-example-data.spec.ts
index 0da3e961d8e13a7e86e2e6596f8d0e876136855e..1f770d296a16286aa29327b8549b6c1ee68ff2a7 100644
--- a/src/app/group-monitor/test-data.spec.ts
+++ b/src/app/group-monitor/unit-test-example-data.spec.ts
@@ -1,7 +1,7 @@
 import {
   Booklet, CommandResponse, TestSession, TestSessionData, TestSessionSetStats
 } from './group-monitor.interfaces';
-import { TestSessionService } from './test-session.service';
+import { TestSessionUtil } from './test-session/test-session.util';
 
 // labels are: {global index}-{ancestor index}-{local index}
 export const unitTestExampleBooklets: { [name: string]: Booklet } = {
@@ -211,7 +211,7 @@ export const unitTestExampleSessions: TestSession[] = [
     timestamp: 10000000
   }
 ]
-  .map(session => TestSessionService.analyzeTestSession(
+  .map(session => TestSessionUtil.analyzeTestSession(
     session, unitTestExampleBooklets[session.bookletName] || { error: 'missing-file', species: null }
   ));
 
@@ -249,7 +249,7 @@ export const additionalUnitTestExampleSessions: TestSession[] = [
     timestamp: 10000340
   }
 ]
-  .map(session => TestSessionService.analyzeTestSession(
+  .map(session => TestSessionUtil.analyzeTestSession(
     session, unitTestExampleBooklets[session.bookletName] || { error: 'missing-file', species: null }
   ));
 
diff --git a/src/scripts/generateDocs-testSuperState.js b/src/scripts/generateDocs-testSuperState.js
index db0be97163aeb3443f342fdb0463d5cd06d0aae0..8ac37b05ad8a99287c780f38a7e7ce039ff779cb 100644
--- a/src/scripts/generateDocs-testSuperState.js
+++ b/src/scripts/generateDocs-testSuperState.js
@@ -1,6 +1,6 @@
 /* eslint-disable no-console,@typescript-eslint/no-var-requires */
 const fs = require('fs');
-const { superStates } = require('../app/group-monitor/test-view/super-states');
+const { superStates } = require('../app/group-monitor/test-session/super-states');
 
 const mdTargetFilename = '../../docs/super-states.html';