UNPKG

@dbg-riskit/angular-testing

Version:

1 lines 96.2 kB
{"version":3,"file":"dbg-riskit-angular-testing.mjs","sources":["../../../../pkg/dbg-riskit/angular-testing/src/lib/environment.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/events.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/stubs/http.service.stub.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/message.def.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/page.base.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/download.menu.page.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/data.table.definition.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/highlighter.directive.page.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/by.util.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/layout.component.page.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/link.definition.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/link.only.page.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/login.menu.page.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/login.page.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/definitions/message.page.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/stubs/auth/auth.routing.flow.service.stub.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/stubs/auth/auth.service.stub.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/stubs/auth/well.known.service.stub.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/stubs/router.stub.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/stubs/login.menu.stub.component.ts","../../../../pkg/dbg-riskit/angular-testing/src/lib/utils.ts","../../../../pkg/dbg-riskit/angular-testing/src/public_api.ts","../../../../pkg/dbg-riskit/angular-testing/src/dbg-riskit-angular-testing.ts"],"sourcesContent":["// Has to be first in this order\nimport '@dbg-riskit/angular-polyfill';\n\n// Other modules follow\nimport {getTestBed} from '@angular/core/testing';\nimport {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';\n\nexport const COMPILE_TIMEOUT_INTERVAL = Math.pow(2, 31) - 1;\n\nexport function initTestEnvironment() {\n Error.stackTraceLimit = 10;\n\n jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;\n\n // TODO: Quick fix for memory leaks\n window.addEventListener = () => {\n return;\n };\n window.document.addEventListener = () => {\n return;\n };\n\n // First, initialize the Angular testing environment.\n getTestBed().initTestEnvironment(\n BrowserDynamicTestingModule,\n platformBrowserDynamicTesting(), {\n teardown: {destroyAfterEach: false}\n }\n );\n}\n","import {DebugElement} from '@angular/core';\n\nimport {tick} from '@angular/core/testing';\n\n/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */\nexport const BUTTON_CLICK_EVENTS = {\n left : {button: 0},\n right: {button: 2}\n};\n\n/** Simulate element click. Defaults to mouse left-button click event. */\nexport function click(el: DebugElement | HTMLElement, eventObj: any = BUTTON_CLICK_EVENTS.left): void {\n if (el instanceof HTMLElement) {\n el.click();\n } else {\n el.triggerEventHandler('click', eventObj);\n }\n}\n\nexport function setNgModelValue(element: DebugElement, value: string, realAsync = false): void {\n if (!(element.nativeElement instanceof HTMLInputElement)) {\n throw new Error('Not an instance of HTMLInputElement');\n }\n const input: HTMLInputElement = element.nativeElement;\n input.value = value;\n\n dispatchEvent(input, 'input'); // tell Angular\n if (!realAsync) {\n tick();\n }\n}\n\nexport function setNgModelSelectValue(element: DebugElement, selectedIndex: number, realAsync = false): void {\n if (!(element.nativeElement instanceof HTMLSelectElement)) {\n throw new Error('Not an instance of HTMLInputElement');\n }\n const input: HTMLSelectElement = element.nativeElement;\n input.selectedIndex = selectedIndex;\n\n dispatchEvent(input, 'change'); // tell Angular\n if (!realAsync) {\n tick();\n }\n}\n\nexport function dispatchEvent(element: DebugElement | HTMLElement | Window, eventName: string): void {\n if (element instanceof HTMLElement) {\n element.dispatchEvent(newEvent(eventName));\n } else if (element instanceof Window) {\n element.dispatchEvent(newEvent(eventName));\n } else {\n element.nativeElement.dispatchEvent(newEvent(eventName));\n }\n}\n\n/**\n * Create custom DOM event the old fashioned way\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent\n * Although officially deprecated, some browsers (phantom) don't accept the preferred \"new Event(eventName)\"\n */\nexport function newEvent(eventName: string, bubbles = false, cancelable = false): CustomEvent {\n const evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'\n evt.initCustomEvent(eventName, bubbles, cancelable, null);\n return evt;\n}\n","import {EventEmitter, Injectable} from '@angular/core';\nimport {Request, RequestWithData} from '@dbg-riskit/angular-common';\nimport {ErrorMessage} from '@dbg-riskit/common';\nimport {Observable, of, throwError as _throwError, timer} from 'rxjs';\nimport {switchMap} from 'rxjs/operators';\n\nexport const FAKE_HTTP_ASYNC_TIMEOUT = 1000;\n\n@Injectable()\nexport class HttpServiceStub<T> {\n\n private value: T[] = [];\n private error: ErrorMessage[] = [];\n\n public unauthorized: EventEmitter<ErrorMessage> = new EventEmitter();\n\n public returnValue(value: T) {\n this.value.push(value);\n }\n\n public popReturnValue(): T | undefined {\n return this.value.pop();\n }\n\n public shiftReturnValue(): T | undefined {\n return this.value.shift();\n }\n\n public throwError(value: ErrorMessage) {\n this.error.push(value);\n }\n\n public get(request: Request<T>): Observable<T | undefined> {\n if (this.error.length) {\n const error = this.error.shift();\n return _throwError(error);\n }\n const value: T | undefined = this.value.shift();\n return of(value);\n }\n\n public post(request: RequestWithData<T>): Observable<T | undefined> {\n return this.get(request);\n }\n}\n\n@Injectable()\nexport class HttpAsyncServiceStub<T> extends HttpServiceStub<T> {\n\n public get(request: Request<T>, auth = true): Observable<T | undefined> {\n return timer(FAKE_HTTP_ASYNC_TIMEOUT).pipe(\n switchMap(() => super.get(request))\n );\n }\n\n public post(request: RequestWithData<T>): Observable<T | undefined> {\n return this.get(request);\n }\n}\n","import {DebugElement} from '@angular/core';\nimport {MatCard} from '@angular/material/card';\nimport {By} from '@angular/platform-browser';\n\nexport class RiskMessageComponentDef {\n\n public constructor(public debugElement: DebugElement) {\n if (debugElement == null) {\n throw new Error('Debug element not found!');\n }\n }\n\n public get text(): string {\n return this.debugElement.query(By.directive(MatCard)).nativeElement.textContent.replace(/\\n\\s*/g, ' ')\n .replace(/\\r\\s*/g, ' ').trim();\n }\n}\n","import {DebugElement} from '@angular/core';\nimport {ComponentFixture, discardPeriodicTasks, tick} from '@angular/core/testing';\nimport {By} from '@angular/platform-browser';\nimport {RISK_INITIAL_LOAD_SELECTOR, RISK_NO_DATA_SELECTOR} from '@dbg-riskit/angular-view';\nimport {FAKE_HTTP_ASYNC_TIMEOUT} from '../stubs/http.service.stub';\nimport {RiskMessageComponentDef} from './message.def';\n\nexport class Page<T> {\n\n public debugElement: DebugElement;\n public component: T;\n private _timeOffset = 0;\n\n public constructor(protected fixture: ComponentFixture<T>) {\n this.debugElement = fixture.debugElement;\n this.component = this.debugElement.componentInstance;\n }\n\n public detectChanges(millis = 0): void {\n this.fixture.detectChanges();\n tick(millis);\n this._timeOffset += millis;\n }\n\n public advanceAndDetectChanges(millis = 0): void {\n tick(millis);\n this._timeOffset += millis;\n this.detectChanges();\n }\n\n public advanceHTTP(): void {\n this.advanceAndDetectChanges(FAKE_HTTP_ASYNC_TIMEOUT);\n }\n\n public advanceAndDetectChangesUsingOffset(millis: number): void {\n this.advanceAndDetectChanges(millis - this._timeOffset);\n this.resetTimeOffset();\n }\n\n public resetTimeOffset(): void {\n this._timeOffset = 0;\n }\n\n public destroy() {\n this.fixture.destroy();\n try {\n // Move a lot into fututre ;)\n tick(60 * 60 * 1000);\n discardPeriodicTasks();\n } catch (ignore) {\n // Nothing to do...\n }\n }\n\n public setInput(inputName: string, value: unknown): void {\n this.fixture.componentRef.setInput(inputName, value);\n this.detectChanges();\n }\n}\n\nexport class PageWithLoading<T> extends Page<T> {\n\n public constructor(fixture: ComponentFixture<T>) {\n super(fixture);\n }\n\n public get initialLoadComponent(): RiskMessageComponentDef | null {\n const element = this.debugElement.query(By.css(RISK_INITIAL_LOAD_SELECTOR));\n if (element) {\n return new RiskMessageComponentDef(element);\n }\n return null;\n }\n\n public get noDataComponent(): RiskMessageComponentDef | null {\n const element = this.debugElement.query(By.css(RISK_NO_DATA_SELECTOR));\n if (element) {\n return new RiskMessageComponentDef(element);\n }\n return null;\n }\n}\n","import {Component, DebugElement} from '@angular/core';\nimport {ComponentFixture} from '@angular/core/testing';\nimport {MatMenuItem, MatMenuTrigger} from '@angular/material/menu';\nimport {By} from '@angular/platform-browser';\nimport {\n AbstractRiskFileDownloadDirective,\n RiskCSVDownloadMenuComponent,\n RiskCSVFileDownloadDirective,\n RiskFileDownloadDirective\n} from '@dbg-riskit/angular-file';\nimport 'file-saver';\nimport {Observable} from 'rxjs';\nimport {click} from '../events';\nimport {Page} from './page.base';\nimport Spy = jasmine.Spy;\n\nexport class RiskCSVDownloadMenuPage extends Page<RiskCSVDownloadMenuComponent> {\n\n public constructor(fixture: ComponentFixture<RiskCSVDownloadMenuComponent>) {\n super(fixture);\n }\n\n public get downloadWindowsLink(): DownloadLink {\n // Open the menu first\n click(this.debugElement.query(By.directive(MatMenuTrigger)));\n this.detectChanges(500);\n\n return new DownloadLink(this.debugElement.queryAll(By.directive(MatMenuItem))[0], this);\n }\n\n public get downloadUnixLink(): DownloadLink {\n // Open the menu first\n click(this.debugElement.query(By.directive(MatMenuTrigger)));\n this.detectChanges(500);\n\n return new DownloadLink(this.debugElement.queryAll(By.directive(MatMenuItem))[1], this);\n }\n}\n\nexport class DownloadLink {\n private _saveBlobSpy?: jasmine.Spy;\n\n public constructor(public element: DebugElement, private page: { detectChanges: () => void }) {\n }\n\n private _blobSpy?: jasmine.Spy;\n\n public get blobSpy(): Spy {\n this.setupBlobConstructorSpy();\n return this._blobSpy!;\n }\n\n public get saveSpy(): Spy {\n this.setupSaveBlobSpy();\n return this._saveBlobSpy!;\n }\n\n public click(): void {\n this.setupBlobConstructorSpy();\n this.setupSaveBlobSpy();\n click(this.element);\n this.page.detectChanges();\n }\n\n private setupBlobConstructorSpy() {\n if (!this._blobSpy) {\n let downloadDirective: AbstractRiskFileDownloadDirective<unknown>;\n try {\n downloadDirective = this.element.injector.get(RiskCSVFileDownloadDirective);\n } catch (_) {\n downloadDirective = this.element.injector.get(RiskFileDownloadDirective);\n }\n this._blobSpy = spyOn(downloadDirective, 'createBlob').and.callThrough();\n }\n }\n\n private setupSaveBlobSpy() {\n if (!this._saveBlobSpy) {\n this._saveBlobSpy = spyOn(window, 'saveAs');\n }\n }\n}\n\n@Component({\n template: `\n <a [risk-download]=\"data\"\n [risk-download-content-type]=\"contentType\"\n [risk-download-filename]=\"filename\">Download</a>\n `,\n imports : [RiskFileDownloadDirective]\n})\nexport class DownloadTestComponent {\n public data: any | Observable<any>;\n\n public contentType?: string;\n\n public filename?: string;\n}\n\nexport class DownloadTestComponentPage extends Page<DownloadTestComponent> {\n\n public constructor(fixture: ComponentFixture<DownloadTestComponent>) {\n super(fixture);\n }\n\n public get downloadLink(): DownloadLink {\n return new DownloadLink(this.debugElement.query(By.directive(RiskFileDownloadDirective)), this);\n }\n}\n","import {Component, DebugElement, DebugNode, input, signal} from '@angular/core';\nimport {ComponentFixture} from '@angular/core/testing';\nimport {MatIconAnchor, MatIconButton} from '@angular/material/button';\nimport {MatButtonToggle} from '@angular/material/button-toggle';\nimport {MatIcon} from '@angular/material/icon';\nimport {MatTooltip} from '@angular/material/tooltip';\nimport {By} from '@angular/platform-browser';\nimport {\n HIGHLIGHTER_CLASS,\n OrderingCriteria,\n RiskDataTableColumnCellDirective,\n RiskDataTableColumnDirective,\n RiskDataTableColumnFooterDirective,\n RiskDataTableColumnGroupDirective,\n RiskDataTableComponent,\n RiskDataTablePagingComponent,\n RiskDataTableRowDetailDirective,\n RiskDataTableRowDetailExpanderComponent\n} from '@dbg-riskit/angular-datatable';\nimport {secureRandom, ValueGetter} from '@dbg-riskit/common';\nimport {click} from '../events';\nimport {Page} from './page.base';\n\nexport class RiskDataTableDefinition {\n\n public constructor(public debugElement: DebugElement,\n private page: { detectChanges: () => void, advanceAndDetectChanges: () => void }) {\n }\n\n public get component(): RiskDataTableComponent<unknown, unknown> {\n return this.debugElement.componentInstance;\n }\n\n public get data(): any[] {\n return this.component.plainData;\n }\n\n public get element(): DebugElement {\n return this.debugElement.query(By.css('.risk-data-table-wrapper > table'));\n }\n\n public get header(): TableHeader {\n return new TableHeader(this.debugElement.query((de) => de.references.mainHeader), this.page);\n }\n\n public get sorting(): TableSorting {\n return new TableSorting(this, this.page);\n }\n\n public get body(): TableBody {\n return new TableBody(this.debugElement.query((de) => de.references.mainBody), this.page);\n }\n\n public get footer(): TableFooter {\n return new TableFooter(this.debugElement.query((de) => de.references.mainFooter));\n }\n\n public get recordsCount(): RecordsCount {\n return new RecordsCount(this.debugElement.query(By.css('.risk-data-table-page-count')));\n }\n\n public get pager(): Pager {\n return new Pager(this.debugElement.query(By.directive(RiskDataTablePagingComponent)), this.page);\n }\n}\n\n// <editor-fold defaultstate=\"collapsed\" desc=\"Table header\">\n\nexport class TableHeader {\n\n public constructor(public element: DebugElement, private page: { detectChanges: () => void }) {\n }\n\n public get rows(): TableHeaderRow[] {\n return this.element.queryAll(By.css('tr')).map((element: DebugElement) => {\n return new TableHeaderRow(element, this.page);\n });\n }\n\n public get cells(): TableHeaderCell[] {\n return this.element.queryAll(By.css('th')).map((element: DebugElement) => {\n return new TableHeaderCell(element, this.page);\n });\n }\n\n}\n\nexport class TableHeaderRow {\n public constructor(public element: DebugElement, private page: { detectChanges: () => void }) {\n }\n\n public get cells(): TableHeaderCell[] {\n return this.element.queryAll(By.css('th')).map((element: DebugElement) => {\n return new TableHeaderCell(element, this.page);\n });\n }\n}\n\nexport class TableHeaderCell {\n\n public constructor(public element: DebugElement, private page: { detectChanges: () => void }) {\n }\n\n public get sortingHandle(): SortingHandle | null {\n const handle = this.element.query(By.directive(MatIconButton));\n if (handle) {\n return new SortingHandle(this.page, handle);\n }\n return null;\n }\n\n public get tooltip(): string | null {\n const handle = this.element.query(By.directive(MatTooltip));\n if (handle) {\n return handle.injector.get(MatTooltip).message;\n }\n return null;\n }\n\n public get title(): string {\n const textNode = this.element.childNodes.find((node: DebugNode) => node.nativeNode.nodeType === Node.TEXT_NODE);\n if (textNode != null) {\n return textNode\n .nativeNode.textContent.trim();\n }\n return '';\n }\n\n public get colspan(): number {\n return this.element.nativeElement.colspan;\n }\n\n public get rowspan(): number {\n return this.element.nativeElement.rowspan;\n }\n\n}\n\n// <editor-fold defaultstate=\"collapsed\" desc=\"Sorting\">\n\nexport class TableSorting {\n\n public constructor(private table: RiskDataTableDefinition, private page: { detectChanges: () => void }) {\n }\n\n public get handles(): SortingHandle[] {\n const handles = this.table.debugElement.query((de) => de.references.mainHeader)\n .queryAll(By.directive(MatIconButton));\n if (!handles) {\n return [];\n }\n return handles.map((handle: DebugElement) => {\n return new SortingHandle(this.page, handle);\n });\n }\n\n public get detailRowHandles(): SortingHandle[] | null {\n const handles = this.table.debugElement.query(By.css('.risk-data-table-detail')).query(By.css('thead'))\n .queryAll(By.directive(MatIconButton));\n if (!handles) {\n return null;\n }\n return handles.map((handle: DebugElement) => {\n return new SortingHandle(this.page, handle);\n });\n }\n\n public get currentOrdering(): Array<OrderingCriteria<any>> {\n return (this.table.component as any).ordering();\n }\n\n public checkSorting(firstNRows: number = this.table.component.rows.length,\n criterium?: OrderingCriteria<any>): void {\n // WARN: call data once as it calls .map on all table rows\n const data = this.table.data;\n\n let ordering: Array<OrderingCriteria<any>>;\n if (criterium) {\n ordering = [criterium].concat((this.table.component as any)._defaultOrdering());\n } else {\n ordering = this.currentOrdering;\n }\n for (let i = 1; i < Math.min(data.length, firstNRows); i++) {\n ordering.some((criteria: OrderingCriteria<any>) => {\n //noinspection EqualityComparisonWithCoercionJS\n const valueA: any = criteria.get(data[i - 1]);\n const valueB: any = criteria.get(data[i]);\n if (valueA == null && valueB == null) {\n return false;\n }\n expect(valueA).not.toBeNull();\n expect(valueA).not.toBeUndefined();\n if (valueB != null) {\n if (criteria.descending) {\n expect(valueA >= valueB)\n .toBeTruthy('Expect: ' + valueA + ' >= '\n + valueB);\n } else {\n expect(valueA <= valueB)\n .toBeTruthy('Expect: ' + valueA + ' <= '\n + valueB);\n }\n }\n return valueA !== valueB;\n });\n }\n }\n}\n\nexport class SortingHandle {\n\n public constructor(private page: { detectChanges: () => void }, private handle: DebugElement) {\n }\n\n public click() {\n click(this.handle);\n this.page.detectChanges();\n }\n}\n\n// </editor-fold>\n\n// </editor-fold>\n\n// <editor-fold defaultstate=\"collapsed\" desc=\"Table body\">\n\nexport class TableBody {\n\n public constructor(public element: DebugElement, private page: { detectChanges: () => void }) {\n }\n\n public get rows(): TableBodyRow[] {\n return this.element.queryAll((de) => de.references.masterRow).map((element: DebugElement) => {\n return new TableBodyRow(element, this.page);\n });\n }\n\n public get cells(): TableBodyCell[] {\n let cells: TableBodyCell[] = [];\n this.element.queryAll((de) => de.references.masterRow).forEach((row: DebugElement) => {\n cells = cells.concat(row.queryAll(By.css('td')).map((cell: DebugElement) => {\n return new TableBodyCell(cell);\n }));\n });\n return cells;\n }\n}\n\nexport class TableBodyRow {\n\n public constructor(public element: DebugElement, private page: { detectChanges: () => void }) {\n }\n\n public get expander(): RowExpander {\n return new RowExpander(\n this.element.query(By.directive(RiskDataTableRowDetailExpanderComponent))\n .query(By.directive(MatIconAnchor)));\n }\n\n public get cells(): TableBodyCell[] {\n return this.element.queryAll(By.css('td')).map((element: DebugElement) => {\n return new TableBodyCell(element);\n });\n }\n\n public get rowDetail(): TableBodyDetail | null {\n const parent = this.element.parent;\n if (parent != null) {\n const next = parent.children[parent.children.indexOf(this.element) + 1];\n if (next && !next.references.masterRow) {\n return new TableBodyDetail(next, this.page);\n }\n }\n return null;\n }\n\n public get highlighted(): boolean {\n return this.element.nativeElement.classList.contains(HIGHLIGHTER_CLASS);\n }\n\n public expandRow(): void {\n click(this.element);\n this.page.detectChanges();\n }\n}\n\nexport class RowExpander {\n public constructor(public element: DebugElement) {\n }\n\n public get icon(): string {\n return this.element.query(By.directive(MatIcon)).nativeElement.textContent.trim();\n }\n\n public get opened(): boolean {\n return this.icon === 'expand_less';\n }\n\n public get closed(): boolean {\n return this.icon === 'expand_more';\n }\n}\n\nexport class TableBodyCell {\n\n public constructor(public element: DebugElement) {\n }\n\n public get colspan(): number {\n return this.element.nativeElement.colspan;\n }\n\n public get rowspan(): number {\n return this.element.nativeElement.rowspan;\n }\n}\n\n// <editor-fold defaultstate=\"collapsed\" desc=\"Row detail\">\n\nexport class TableBodyDetail {\n\n public constructor(public element: DebugElement, private page: { detectChanges: () => void }) {\n }\n\n public get body(): TableBodyDetailBody {\n return new TableBodyDetailBody(this.element.query(By.css('tbody')));\n }\n\n public get highlighted(): boolean {\n return this.element.nativeElement.classList.contains(HIGHLIGHTER_CLASS);\n }\n\n public get colspan(): number {\n return this.element.children[0].nativeElement.colspan;\n }\n\n public header(): TableHeader {\n return new TableHeader(this.element.query(By.css('thead')), this.page);\n }\n}\n\nexport class TableBodyDetailBody {\n\n public constructor(public element: DebugElement) {\n }\n\n public get rows(): TableBodyDetailRow[] {\n return this.element.queryAll(By.css('tr')).map((element: DebugElement) => {\n return new TableBodyDetailRow(element);\n });\n }\n\n public get cells(): TableBodyCell[] {\n let cells: TableBodyCell[] = [];\n this.element.queryAll(By.css('tr')).forEach((row: DebugElement) => {\n cells = cells.concat(row.queryAll(By.css('td')).map((cell: DebugElement) => {\n return new TableBodyCell(cell);\n }));\n });\n return cells;\n }\n}\n\nexport class TableBodyDetailRow {\n\n public constructor(public element: DebugElement) {\n }\n\n public get cells(): TableBodyCell[] {\n return this.element.queryAll(By.css('td')).map((element: DebugElement) => {\n return new TableBodyCell(element);\n });\n }\n}\n\n// </editor-fold>\n\n// </editor-fold>\n\n// <editor-fold defaultstate=\"collapsed\" desc=\"Table footer\">\n\nexport class TableFooter {\n\n public constructor(public element: DebugElement) {\n }\n\n public get rows(): TableFooterRow[] {\n return this.element.queryAll(By.css('tr')).map((element: DebugElement) => {\n return new TableFooterRow(element);\n });\n }\n\n public get cells(): TableBodyCell[] {\n let cells: TableBodyCell[] = [];\n this.element.queryAll(By.css('tr')).forEach((row: DebugElement) => {\n cells = cells.concat(row.queryAll(By.css('th')).map((cell: DebugElement) => {\n return new TableBodyCell(cell);\n }));\n });\n return cells;\n }\n}\n\nexport class TableFooterRow {\n\n public constructor(public element: DebugElement) {\n }\n\n public get cells(): TableBodyCell[] {\n return this.element.queryAll(By.css('th')).map((element: DebugElement) => {\n return new TableBodyCell(element);\n });\n }\n}\n\n// </editor-fold>\n\nexport class RecordsCount {\n\n public constructor(public element: DebugElement) {\n }\n\n public get message(): string {\n return this.element.nativeElement.textContent;\n }\n}\n\nexport class Pager {\n\n public constructor(public element: DebugElement,\n private page: { detectChanges: () => void, advanceAndDetectChanges: () => void }) {\n }\n\n public get pageButtons(): DebugElement[] {\n return this.element.queryAll(By.directive(MatButtonToggle));\n }\n\n public expectLeadingButtonsDisabled() {\n for (let i = 0; i < 2; i++) {\n expect(this.pageButtons[i].classes['mat-button-toggle-disabled'])\n .toBe(true, 'First two are disabled.');\n }\n }\n\n public expectLeadingButtonsNotDisabled() {\n for (let i = 0; i < 2; i++) {\n expect(this.pageButtons[i].classes['mat-button-toggle-disabled'])\n .not.toBe(true, 'First two are not disabled.');\n }\n }\n\n public expectTrailingButtonsDisabled() {\n for (let i = this.pageButtons.length - 1; i > this.pageButtons.length - 3; i--) {\n expect(this.pageButtons[i].classes['mat-button-toggle-disabled'])\n .toBe(true, 'Last two are disabled.');\n }\n }\n\n public expectTrailingButtonsNotDisabled() {\n for (let i = this.pageButtons.length - 1; i > this.pageButtons.length - 3; i--) {\n expect(this.pageButtons[i].classes['mat-button-toggle-disabled'])\n .not.toBe(true, 'Last two are not disabled.');\n }\n }\n\n public expectButtonNumbers(numbers: number[]) {\n for (let i = 0; i < numbers.length; i++) {\n expect(this.pageButtons[i + 2].query(By.css('.mat-button-toggle-label-content'))\n .nativeElement.textContent.trim())\n .toEqual(numbers[i] + '', 'Button numbers are correct');\n }\n }\n\n public expectButtonActive(index: number) {\n expect(this.pageButtons[index].classes['mat-button-toggle-checked'])\n .toBe(true, 'Button is active.');\n }\n\n public click(index: number) {\n click(this.pageButtons[index].query(By.css('.mat-button-toggle-label-content')).nativeElement);\n this.page.detectChanges();\n this.page.advanceAndDetectChanges();\n }\n}\n\nexport function chceckSorting(page: { detectChanges: () => void, dataTable: RiskDataTableDefinition },\n criteria: Array<ValueGetter<any>>) {\n page.dataTable.sorting.checkSorting(150);\n\n page.dataTable.sorting.handles.forEach((handle: SortingHandle, index: number) => {\n // Tigger sort based on a handle\n handle.click();\n page.detectChanges();\n\n // Check the sorting\n page.dataTable.sorting.checkSorting(150, {\n get: criteria[index]\n });\n\n // Tigger sort based on a handle\n handle.click();\n page.detectChanges();\n\n // Check the sorting\n page.dataTable.sorting.checkSorting(150, {\n get : criteria[index],\n descending: true\n });\n });\n}\n\nexport class DataTableDefinitionHosted extends Page<TestDataTableHostComponent> {\n\n public constructor(fixture: ComponentFixture<TestDataTableHostComponent>) {\n super(fixture);\n }\n\n public get dataTable(): RiskDataTableDefinition {\n return new RiskDataTableDefinition(this.debugElement.query(By.directive(RiskDataTableComponent)), this);\n }\n\n public setData(data: any): void {\n this.fixture.componentRef.setInput('data', data);\n this.detectChanges();\n }\n\n public setPageSize(size: number): void {\n this.fixture.componentRef.setInput('pageSize', size);\n this.detectChanges();\n }\n}\n\n@Component({\n template: `\n <risk-data-table [data]=\"data()\"\n [footer]=\"footer()\"\n [pageSize]=\"pageSize()\"\n [defaultOrdering]=\"defaultOrdering\"\n [striped]=\"true\">\n <risk-data-table-column title=\"Test Column 1\"\n [sortingKey]=\"valueGetter\">\n <ng-template let-record=\"row\" risk-data-table-cell>\n {{ record.value1 }}\n </ng-template>\n </risk-data-table-column>\n <risk-data-table-column>\n <ng-template risk-data-table-cell let-record=\"row\" let-expanded=\"expanded\">\n <risk-data-table-detail-expander [expanded]=\"expanded\"/>\n </ng-template>\n <ng-template risk-data-table-footer-cell let-footer=\"footer\">\n {{ footer.value1 }}\n </ng-template>\n </risk-data-table-column>\n <!-- Sub detail -->\n <risk-data-table-detail-row>\n <risk-data-table-column-group>\n <risk-data-table-column title=\"Test Detail Column 1\"\n [sortingKey]=\"valueGetter\">\n <ng-template risk-data-table-cell let-record=\"row\">\n {{ record.value1 }}\n </ng-template>\n </risk-data-table-column>\n </risk-data-table-column-group>\n <risk-data-table-column-group>\n <risk-data-table-column title=\"Test Detail Column 2\">\n <ng-template risk-data-table-cell let-record=\"row\">\n {{ record.value2 }}\n </ng-template>\n </risk-data-table-column>\n <risk-data-table-column title=\"Test Detail Column 3\">\n <ng-template risk-data-table-cell let-record=\"row\">\n {{ record.value3 }}\n </ng-template>\n </risk-data-table-column>\n </risk-data-table-column-group>\n <risk-data-table-column-group/>\n </risk-data-table-detail-row>\n </risk-data-table>`,\n imports : [\n RiskDataTableColumnCellDirective,\n RiskDataTableColumnDirective,\n RiskDataTableColumnFooterDirective,\n RiskDataTableColumnGroupDirective,\n RiskDataTableComponent,\n RiskDataTableRowDetailDirective,\n RiskDataTableRowDetailExpanderComponent\n ]\n})\nexport class TestDataTableHostComponent {\n private static readonly defaultData: any[] = (() => {\n const data = [];\n for (let i = 0; i < 500; i++) {\n data.push({\n value1: Math.floor(secureRandom() * 20) + ' - value 1',\n value2: Math.floor(secureRandom() * 20) + ' - value 2',\n value3: Math.floor(secureRandom() * 20) + ' - value 3'\n });\n }\n return data;\n })();\n\n public readonly data = input<any[]>(TestDataTableHostComponent.defaultData);\n public readonly pageSize = input(20);\n public readonly footer = signal<any>(TestDataTableHostComponent.defaultData[0]);\n\n public readonly defaultOrdering = [\n {\n get : (record: any) => {\n return record.value3;\n },\n descending: true\n },\n (record: any) => {\n return record.value2;\n }\n ];\n\n public readonly valueGetter = (record: any) => {\n return record.value1;\n };\n}\n","import {Component, DebugElement} from '@angular/core';\nimport {ComponentFixture} from '@angular/core/testing';\nimport {By} from '@angular/platform-browser';\nimport {RiskHighlighterDirective, Row} from '@dbg-riskit/angular-datatable';\nimport {Page} from './page.base';\n\n@Component({\n template: `\n <div [risk-data-table-highlighter]=\"trackBy\" [risk-data-table-highlighter-context]=\"context\"></div>`,\n imports : [RiskHighlighterDirective]\n})\nexport class RiskHighLighterDirectiveTestComponent {\n\n public context?: { row: any, storage?: any, index: number, enabled: boolean };\n\n public trackBy(index: number, row: Row<any>): any {\n return row.rowData;\n }\n}\n\nexport class HighLighterDirectivePage extends Page<RiskHighLighterDirectiveTestComponent> {\n\n public constructor(fixture: ComponentFixture<RiskHighLighterDirectiveTestComponent>) {\n super(fixture);\n }\n\n public get highlightedElement(): DebugElement {\n return this.debugElement.query(By.directive(RiskHighlighterDirective));\n }\n\n public get classList(): DOMTokenList {\n return this.highlightedElement.nativeElement.classList;\n }\n\n public get highlighter(): RiskHighlighterDirective<unknown> {\n return this.highlightedElement.injector.get(RiskHighlighterDirective);\n }\n}\n","import {Predicate} from '@angular/core';\n\n// @dynamic\nexport class ByUtil {\n\n public static and<T>(...predicates: Array<Predicate<T>>): Predicate<T> {\n return (val: T) => {\n return predicates.every((predicate: Predicate<T>) => predicate(val));\n };\n }\n\n public static or<T>(...predicates: Array<Predicate<T>>): Predicate<T> {\n return (val: T) => {\n return predicates.some((predicate: Predicate<T>) => predicate(val));\n };\n }\n\n public static not<T>(predicate: Predicate<T>): Predicate<T> {\n return (val: T) => {\n return !predicate(val);\n };\n }\n}\n","import {Component, DebugElement} from '@angular/core';\nimport {MatSidenav, MatSidenavContainer} from '@angular/material/sidenav';\nimport {MatToolbar} from '@angular/material/toolbar';\nimport {By} from '@angular/platform-browser';\nimport {\n RiskLayoutComponent,\n RiskLayoutHorizontalDirective,\n RiskLayoutVerticalDirective\n} from '@dbg-riskit/angular-view';\nimport {ByUtil} from '../by.util';\nimport {Page} from './page.base';\n\nexport class RiskLayoutComponentDefinition {\n\n public constructor(public debugElement: DebugElement,\n private page: { advanceAndDetectChanges: (milis?: number) => void }) {\n }\n\n public get component(): RiskLayoutComponent {\n return this.debugElement.componentInstance;\n }\n\n public get headToolbar(): DebugElement {\n return this.debugElement.query(ByUtil.and(By.directive(MatToolbar), By.css('.mat-elevation-z2')));\n }\n\n public get logo(): DebugElement {\n return this.headToolbar.query(By.css('.risk-layout-logo'));\n }\n\n public get menu(): DebugElement[] {\n return this.headToolbar.queryAll(ByUtil.or(\n By.css('[menu-horizontal]'),\n By.css('risk-layout-horizontal')));\n }\n\n public get sideNavContainer(): DebugElement {\n return this.debugElement.query(By.directive(MatSidenavContainer));\n }\n\n public get sideNav(): DebugElement {\n return this.sideNavContainer.query(By.directive(MatSidenav));\n }\n\n public get sideNavMenu(): DebugElement[] {\n return this.sideNav.queryAll(ByUtil.or(\n By.css('[menu-vertical]'),\n By.css('risk-layout-vertical')));\n }\n\n public get content(): DebugElement {\n return this.sideNavContainer.query(By.css('.risk-layout-content'));\n }\n\n public openSideNav(): void {\n (this.sideNav.componentInstance as MatSidenav).open();\n this.page.advanceAndDetectChanges();\n }\n\n public closeSideNav(): void {\n (this.sideNav.componentInstance as MatSidenav).close();\n this.page.advanceAndDetectChanges(1);\n }\n\n}\n\nexport class RiskLayoutTestComponentHostPage extends Page<RiskLayoutTestHostComponent> {\n\n public get layoutComponent(): RiskLayoutComponentDefinition {\n return new RiskLayoutComponentDefinition(this.debugElement.query(By.directive(RiskLayoutComponent)), this);\n }\n}\n\n@Component({\n template: `\n <risk-layout>\n <!-- menu-horizontal -->\n <risk-layout-horizontal>\n HorizontalMenu\n </risk-layout-horizontal>\n <!-- menu-horizontal -->\n\n <!-- menu-vertical -->\n <risk-layout-vertical>\n Vertical menu\n </risk-layout-vertical>\n\n <span>Content</span>\n </risk-layout>\n `,\n imports : [\n RiskLayoutComponent,\n RiskLayoutHorizontalDirective,\n RiskLayoutVerticalDirective\n ]\n})\nexport class RiskLayoutTestHostComponent {\n\n}\n","import {DebugElement} from '@angular/core';\nimport {RouterLink} from '@angular/router';\nimport {click} from '../events';\nimport {Page} from './page.base';\n\nexport class LinkDefinition {\n\n public constructor(private page: Page<any>, public link: DebugElement) {\n }\n\n public get stub(): RouterLink {\n return this.link.injector.get(RouterLink);\n }\n\n public get text(): string {\n return this.link.nativeElement.textContent.trim();\n }\n\n public click() {\n click(this.link.nativeElement);\n this.page.advanceAndDetectChanges();\n }\n}\n","import {ComponentFixture} from '@angular/core/testing';\nimport {By} from '@angular/platform-browser';\nimport {RouterLink} from '@angular/router';\nimport {LinkDefinition} from './link.definition';\nimport {Page} from './page.base';\n\nexport class LinkOnlyPage<T> extends Page<T> {\n\n public constructor(fixture: ComponentFixture<T>) {\n super(fixture);\n }\n\n public get link(): LinkDefinition {\n return new LinkDefinition(this, this.debugElement.query(By.directive(RouterLink)));\n }\n}\n","import {DebugElement} from '@angular/core';\nimport {MatMenuItem, MatMenuTrigger} from '@angular/material/menu';\nimport {By} from '@angular/platform-browser';\nimport {RiskLoginMenuComponent} from '@dbg-riskit/angular-login';\nimport {ByUtil} from '../by.util';\nimport {click} from '../events';\nimport {LinkDefinition} from './link.definition';\nimport {Page} from './page.base';\n\nexport class LoginMenuPage extends Page<RiskLoginMenuComponent> {\n\n public get menuTrigger(): DebugElement {\n return this.debugElement.query(By.directive(MatMenuTrigger));\n }\n\n public clickMenuTrigger(): void {\n click(this.menuTrigger);\n this.waitForMenu();\n }\n\n public waitForMenu(): void {\n this.detectChanges(500);\n }\n\n public get loginLink(): LinkDefinition {\n return new LinkDefinition(this, this.debugElement.query(\n ByUtil.and(\n By.directive(MatMenuItem),\n (value: DebugElement) => value.nativeElement.textContent.trim().endsWith('Login'))));\n }\n\n public get logoutLink(): LinkDefinition {\n return new LinkDefinition(this, this.debugElement.query(\n ByUtil.and(\n By.directive(MatMenuItem),\n (value: DebugElement) => value.nativeElement.textContent.trim().endsWith('Logout'))));\n }\n}\n","import {DebugElement} from '@angular/core';\nimport {ComponentFixture, tick} from '@angular/core/testing';\nimport {MatButton} from '@angular/material/button';\nimport {By} from '@angular/platform-browser';\nimport {RiskLoginComponent} from '@dbg-riskit/angular-login';\nimport {RISK_ERROR_SELECTOR, RISK_GOOD_SELECTOR, RiskMessageComponent} from '@dbg-riskit/angular-view';\nimport {ByUtil} from '../by.util';\nimport {click, setNgModelValue} from '../events';\nimport {RiskMessageComponentDef} from './message.def';\nimport {Page} from './page.base';\n\nexport class RiskLoginPage extends Page<RiskLoginComponent> {\n\n public constructor(fixture: ComponentFixture<RiskLoginComponent>) {\n super(fixture);\n }\n\n public get formElement(): DebugElement {\n return this.debugElement.query(By.css('form'));\n }\n\n public get usernameElement(): DebugElement {\n return this.formElement.query(By.css('input[name=username]'));\n }\n\n public set username(username: string) {\n setNgModelValue(this.usernameElement, username);\n this.fixture.detectChanges();\n }\n\n public get passwordElement(): DebugElement {\n return this.formElement.query(By.css('input[name=password]'));\n }\n\n public set password(password: string) {\n setNgModelValue(this.passwordElement, password);\n this.fixture.detectChanges();\n }\n\n public get loginButtonElement(): DebugElement {\n return this.formElement.query(By.directive(MatButton));\n }\n\n public get successMessage(): RiskMessageComponentDef | null {\n const debugElement = this.debugElement.query(\n ByUtil.and(\n By.directive(RiskMessageComponent),\n By.css(RISK_GOOD_SELECTOR)));\n if (!debugElement) {\n return null;\n }\n return new RiskMessageComponentDef(debugElement);\n }\n\n public get errorMessage(): RiskMessageComponentDef | null {\n const debugElement = this.debugElement.query(\n ByUtil.or(\n ByUtil.and(\n By.directive(RiskMessageComponent),\n By.css(RISK_ERROR_SELECTOR)),\n By.css('h3')));\n if (!debugElement) {\n return null;\n }\n return new RiskMessageComponentDef(debugElement);\n }\n\n public clickLogin() {\n click(this.loginButtonElement.nativeElement);\n tick();\n this.detectChanges();\n }\n}\n","import {Component} from '@angular/core';\nimport {ComponentFixture} from '@angular/core/testing';\nimport {By} from '@angular/platform-browser';\nimport {\n RISK_ERROR_SELECTOR,\n RISK_GOOD_SELECTOR,\n RISK_INFO_SELECTOR,\n RISK_INITIAL_LOAD_SELECTOR,\n RISK_MESSAGE_SELECTOR,\n RISK_NO_DATA_SELECTOR,\n RISK_UPDATE_FAILED_SELECTOR,\n RISK_WARN_SELECTOR,\n RiskMessageComponent\n} from '@dbg-riskit/angular-view';\nimport {RiskMessageComponentDef} from './message.def';\nimport {Page} from './page.base';\n\n@Component({\n template: `\n <risk-error [message]=\"message\"/>\n <risk-good [message]=\"message\"/>\n <risk-info [message]=\"message\"/>\n <risk-message [message]=\"message\"/>\n <risk-warn [message]=\"message\"/>\n <risk-initial-load [message]=\"message\"/>\n <risk-update-failed [message]=\"message\"/>\n <risk-no-data [message]=\"message\"/>\n `,\n imports: [\n RiskMessageComponent\n ]\n})\nexport class TestMessageHostComponent {\n public message = 'custom message';\n}\n\nexport class MessageHostedPage extends Page<TestMessageHostComponent> {\n\n public constructor(fixture: ComponentFixture<TestMessageHostComponent>) {\n super(fixture);\n }\n\n public get error(): RiskMessageComponentDef {\n return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_ERROR_SELECTOR)));\n }\n\n public get good(): RiskMessageComponentDef {\n return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_GOOD_SELECTOR)));\n }\n\n public get info(): RiskMessageComponentDef {\n return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_INFO_SELECTOR)));\n }\n\n public get message(): RiskMessageComponentDef {\n return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_MESSAGE_SELECTOR)));\n }\n\n public get warn(): RiskMessageComponentDef {\n return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_WARN_SELECTOR)));\n }\n\n public get initialLoad(): RiskMessageComponentDef {\n return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_INITIAL_LOAD_SELECTOR)));\n }\n\n public get noData(): RiskMessageComponentDef {\n return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_NO_DATA_SELECTOR)));\n }\n\n public get updateFailed(): RiskMessageComponentDef {\n return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_UPDATE_FAILED_SELECTOR)));\n }\n}\n","import {inject, Injectable} from '@angular/core';\nimport {RouterStateSnapshot} from '@angular/router';\n\nimport {AUTH_CONFIG, AuthConfig, AuthFlow, AuthService} from '@dbg-riskit/angular-auth';\nimport {AUTH_PROVIDER} from '@dbg-riskit/angular-common';\n\nimport {Observable} from 'rxjs';\nimport {defaultIfEmpty, map} from 'rxjs/operators';\n\nexport const storage: { authRequestedPath: string | null; }\n = {authRequestedPath: null};\n\n@Injectable()\nexport class AuthRoutingFlowServiceStub {\n private authServiceStub = inject<AuthService>(AUTH_PROVIDER);\n private readonly authConfig = inject<AuthConfig>(AUTH_CONFIG);\n\n public constructor() {\n // cleanup\n storage.authRequestedPath = null;\n }\n\n public get authFlow(): AuthFlow {\n return this.authConfig.flow;\n }\n\n public get authorizationCodeFlow(): boolean {\n return this.authFlow === AuthFlow.AUTHORIZATION_CODE;\n }\n\n public get implicitFlow(): boolean {\n return this.authFlow === AuthFlow.IMPLICIT;\n }\n\n public get hybridFlow(): boolean {\n return this.authFlow === AuthFlow.HYBRID;\n }\n\n public get directFlow(): boolean {\n return this.authFlow === AuthFlow.DIRECT;\n }\n\n public logout(): Observable<boolean> {\n return this.authServiceStub.logout().pipe(\n defaultIfEmpty(0),\n map(() => true));\n }\n\n public login(username?: string, password?: string): Observable<boolean> {\n return this.authServiceStub.directLogin(username as any, password as any);\n }\n\n public loginViaService(): Observable<boolean> {\n return this.authServiceStub.loginViaAuthService();\n }\n\n public storeRequestedPath(state?: RouterStateSnapshot): void {\n storage.authRequestedPath = state?.url || null;\n }\n}\n","import {Injectable} from '@angular/core';\nimport {AuthProvider, ReplaySubjectExt, UserInfo} from '@dbg-riskit/common';\nimport {defer, EMPTY, Observable, of} from 'rxjs';\n\n@Injectable()\nexport class AuthServiceStub implements AuthProvider {\n private user?: string;\n\n private readonly _loggedInStream: ReplaySubjectExt<boolean> = new ReplaySubjectExt<boolean>(1);\n\n public get loggedIn(): Observable<boolean> {\n return defer(() => of(!!this.user));\n }\n\n public get userProfile(): Observable<UserInfo | undefined> {\n return defer(() => of(this.user ? {\n name: this.user\n } : undefined));\n }\n\n public get loggedInStream(): Observable<boolean> {\n return this._loggedInStream.asObservable();\n }\n\n public loginViaAuthService(): Observable<boolean> {\n return of(true);\n }\n\n public checkLocationForLoginData(): Observable<boolean> {\n return of(true);\n }\n\n public directLogin(username: string, password: string): Observable<boolean> {\n this.user = username;\n this.emitLoginStatusChange(true);\n return of(true);\n }\n\n public logout(): Observable<never> {\n this.emitLoginStatusChange(false);\n delete this.user;\n return EMPTY;\n }\n\n public emitLoginStatusChange(status: boolean)