@dbg-riskit/angular-testing
Version:
1,315 lines (1,292 loc) • 55.2 kB
JavaScript
import '@dbg-riskit/angular-polyfill';
import { getTestBed, tick, discardPeriodicTasks } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import * as i0 from '@angular/core';
import { EventEmitter, Injectable, Component, input, signal, inject, ChangeDetectionStrategy } from '@angular/core';
import { MatMenuTrigger, MatMenuItem } from '@angular/material/menu';
import { By } from '@angular/platform-browser';
import { RiskCSVFileDownloadDirective, RiskFileDownloadDirective } from '@dbg-riskit/angular-file';
import 'file-saver';
import { RISK_INITIAL_LOAD_SELECTOR, RISK_NO_DATA_SELECTOR, RiskLayoutComponent, RiskLayoutHorizontalDirective, RiskLayoutVerticalDirective, RiskMessageComponent, RISK_GOOD_SELECTOR, RISK_ERROR_SELECTOR, RISK_INFO_SELECTOR, RISK_MESSAGE_SELECTOR, RISK_WARN_SELECTOR, RISK_UPDATE_FAILED_SELECTOR } from '@dbg-riskit/angular-view';
export { RiskLayoutComponent } from '@dbg-riskit/angular-view';
import { throwError, of, timer, defer, EMPTY, BehaviorSubject } from 'rxjs';
import { switchMap, defaultIfEmpty, map } from 'rxjs/operators';
import { MatCard } from '@angular/material/card';
import { MatIconButton, MatIconAnchor, MatButton } from '@angular/material/button';
import { MatButtonToggle } from '@angular/material/button-toggle';
import { MatIcon } from '@angular/material/icon';
import { MatTooltip } from '@angular/material/tooltip';
import { RiskDataTablePagingComponent, RiskDataTableRowDetailExpanderComponent, HIGHLIGHTER_CLASS, RiskDataTableComponent, RiskDataTableColumnCellDirective, RiskDataTableColumnDirective, RiskDataTableColumnFooterDirective, RiskDataTableColumnGroupDirective, RiskDataTableRowDetailDirective, RiskHighlighterDirective } from '@dbg-riskit/angular-datatable';
import { secureRandom, ReplaySubjectExt, globalScope } from '@dbg-riskit/common';
import { MatSidenavContainer, MatSidenav } from '@angular/material/sidenav';
import { MatToolbar } from '@angular/material/toolbar';
import { RouterLink } from '@angular/router';
import { AUTH_CONFIG, AuthFlow } from '@dbg-riskit/angular-auth';
import { AUTH_PROVIDER } from '@dbg-riskit/angular-common';
// Has to be first in this order
const COMPILE_TIMEOUT_INTERVAL = Math.pow(2, 31) - 1;
function initTestEnvironment() {
Error.stackTraceLimit = 10;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
// TODO: Quick fix for memory leaks
window.addEventListener = () => {
return;
};
window.document.addEventListener = () => {
return;
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false }
});
}
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
const BUTTON_CLICK_EVENTS = {
left: { button: 0 },
right: { button: 2 }
};
/** Simulate element click. Defaults to mouse left-button click event. */
function click(el, eventObj = BUTTON_CLICK_EVENTS.left) {
if (el instanceof HTMLElement) {
el.click();
}
else {
el.triggerEventHandler('click', eventObj);
}
}
function setNgModelValue(element, value, realAsync = false) {
if (!(element.nativeElement instanceof HTMLInputElement)) {
throw new Error('Not an instance of HTMLInputElement');
}
const input = element.nativeElement;
input.value = value;
dispatchEvent(input, 'input'); // tell Angular
if (!realAsync) {
tick();
}
}
function setNgModelSelectValue(element, selectedIndex, realAsync = false) {
if (!(element.nativeElement instanceof HTMLSelectElement)) {
throw new Error('Not an instance of HTMLInputElement');
}
const input = element.nativeElement;
input.selectedIndex = selectedIndex;
dispatchEvent(input, 'change'); // tell Angular
if (!realAsync) {
tick();
}
}
function dispatchEvent(element, eventName) {
if (element instanceof HTMLElement) {
element.dispatchEvent(newEvent(eventName));
}
else if (element instanceof Window) {
element.dispatchEvent(newEvent(eventName));
}
else {
element.nativeElement.dispatchEvent(newEvent(eventName));
}
}
/**
* Create custom DOM event the old fashioned way
*
* https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent
* Although officially deprecated, some browsers (phantom) don't accept the preferred "new Event(eventName)"
*/
function newEvent(eventName, bubbles = false, cancelable = false) {
const evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
evt.initCustomEvent(eventName, bubbles, cancelable, null);
return evt;
}
const FAKE_HTTP_ASYNC_TIMEOUT = 1000;
class HttpServiceStub {
constructor() {
this.value = [];
this.error = [];
this.unauthorized = new EventEmitter();
}
returnValue(value) {
this.value.push(value);
}
popReturnValue() {
return this.value.pop();
}
shiftReturnValue() {
return this.value.shift();
}
throwError(value) {
this.error.push(value);
}
get(request) {
if (this.error.length) {
const error = this.error.shift();
return throwError(error);
}
const value = this.value.shift();
return of(value);
}
post(request) {
return this.get(request);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: HttpServiceStub, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: HttpServiceStub }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: HttpServiceStub, decorators: [{
type: Injectable
}] });
class HttpAsyncServiceStub extends HttpServiceStub {
get(request, auth = true) {
return timer(FAKE_HTTP_ASYNC_TIMEOUT).pipe(switchMap(() => super.get(request)));
}
post(request) {
return this.get(request);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: HttpAsyncServiceStub, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: HttpAsyncServiceStub }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: HttpAsyncServiceStub, decorators: [{
type: Injectable
}] });
class RiskMessageComponentDef {
constructor(debugElement) {
this.debugElement = debugElement;
if (debugElement == null) {
throw new Error('Debug element not found!');
}
}
get text() {
return this.debugElement.query(By.directive(MatCard)).nativeElement.textContent.replace(/\n\s*/g, ' ')
.replace(/\r\s*/g, ' ').trim();
}
}
class Page {
constructor(fixture) {
this.fixture = fixture;
this._timeOffset = 0;
this.debugElement = fixture.debugElement;
this.component = this.debugElement.componentInstance;
}
detectChanges(millis = 0) {
this.fixture.detectChanges();
tick(millis);
this._timeOffset += millis;
}
advanceAndDetectChanges(millis = 0) {
tick(millis);
this._timeOffset += millis;
this.detectChanges();
}
advanceHTTP() {
this.advanceAndDetectChanges(FAKE_HTTP_ASYNC_TIMEOUT);
}
advanceAndDetectChangesUsingOffset(millis) {
this.advanceAndDetectChanges(millis - this._timeOffset);
this.resetTimeOffset();
}
resetTimeOffset() {
this._timeOffset = 0;
}
destroy() {
this.fixture.destroy();
try {
// Move a lot into fututre ;)
tick(60 * 60 * 1000);
discardPeriodicTasks();
}
catch (ignore) {
// Nothing to do...
}
}
setInput(inputName, value) {
this.fixture.componentRef.setInput(inputName, value);
this.detectChanges();
}
}
class PageWithLoading extends Page {
constructor(fixture) {
super(fixture);
}
get initialLoadComponent() {
const element = this.debugElement.query(By.css(RISK_INITIAL_LOAD_SELECTOR));
if (element) {
return new RiskMessageComponentDef(element);
}
return null;
}
get noDataComponent() {
const element = this.debugElement.query(By.css(RISK_NO_DATA_SELECTOR));
if (element) {
return new RiskMessageComponentDef(element);
}
return null;
}
}
class RiskCSVDownloadMenuPage extends Page {
constructor(fixture) {
super(fixture);
}
get downloadWindowsLink() {
// Open the menu first
click(this.debugElement.query(By.directive(MatMenuTrigger)));
this.detectChanges(500);
return new DownloadLink(this.debugElement.queryAll(By.directive(MatMenuItem))[0], this);
}
get downloadUnixLink() {
// Open the menu first
click(this.debugElement.query(By.directive(MatMenuTrigger)));
this.detectChanges(500);
return new DownloadLink(this.debugElement.queryAll(By.directive(MatMenuItem))[1], this);
}
}
class DownloadLink {
constructor(element, page) {
this.element = element;
this.page = page;
}
get blobSpy() {
this.setupBlobConstructorSpy();
return this._blobSpy;
}
get saveSpy() {
this.setupSaveBlobSpy();
return this._saveBlobSpy;
}
click() {
this.setupBlobConstructorSpy();
this.setupSaveBlobSpy();
click(this.element);
this.page.detectChanges();
}
setupBlobConstructorSpy() {
if (!this._blobSpy) {
let downloadDirective;
try {
downloadDirective = this.element.injector.get(RiskCSVFileDownloadDirective);
}
catch (_) {
downloadDirective = this.element.injector.get(RiskFileDownloadDirective);
}
this._blobSpy = spyOn(downloadDirective, 'createBlob').and.callThrough();
}
}
setupSaveBlobSpy() {
if (!this._saveBlobSpy) {
this._saveBlobSpy = spyOn(window, 'saveAs');
}
}
}
class DownloadTestComponent {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DownloadTestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: DownloadTestComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
<a [risk-download]="data"
[risk-download-content-type]="contentType"
[risk-download-filename]="filename">Download</a>
`, isInline: true, dependencies: [{ kind: "directive", type: RiskFileDownloadDirective, selector: "[risk-download]", inputs: ["risk-download", "risk-download-content-type", "risk-download-filename"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DownloadTestComponent, decorators: [{
type: Component,
args: [{
template: `
<a [risk-download]="data"
[risk-download-content-type]="contentType"
[risk-download-filename]="filename">Download</a>
`,
imports: [RiskFileDownloadDirective]
}]
}] });
class DownloadTestComponentPage extends Page {
constructor(fixture) {
super(fixture);
}
get downloadLink() {
return new DownloadLink(this.debugElement.query(By.directive(RiskFileDownloadDirective)), this);
}
}
class RiskDataTableDefinition {
constructor(debugElement, page) {
this.debugElement = debugElement;
this.page = page;
}
get component() {
return this.debugElement.componentInstance;
}
get data() {
return this.component.plainData;
}
get element() {
return this.debugElement.query(By.css('.risk-data-table-wrapper > table'));
}
get header() {
return new TableHeader(this.debugElement.query((de) => de.references.mainHeader), this.page);
}
get sorting() {
return new TableSorting(this, this.page);
}
get body() {
return new TableBody(this.debugElement.query((de) => de.references.mainBody), this.page);
}
get footer() {
return new TableFooter(this.debugElement.query((de) => de.references.mainFooter));
}
get recordsCount() {
return new RecordsCount(this.debugElement.query(By.css('.risk-data-table-page-count')));
}
get pager() {
return new Pager(this.debugElement.query(By.directive(RiskDataTablePagingComponent)), this.page);
}
}
// <editor-fold defaultstate="collapsed" desc="Table header">
class TableHeader {
constructor(element, page) {
this.element = element;
this.page = page;
}
get rows() {
return this.element.queryAll(By.css('tr')).map((element) => {
return new TableHeaderRow(element, this.page);
});
}
get cells() {
return this.element.queryAll(By.css('th')).map((element) => {
return new TableHeaderCell(element, this.page);
});
}
}
class TableHeaderRow {
constructor(element, page) {
this.element = element;
this.page = page;
}
get cells() {
return this.element.queryAll(By.css('th')).map((element) => {
return new TableHeaderCell(element, this.page);
});
}
}
class TableHeaderCell {
constructor(element, page) {
this.element = element;
this.page = page;
}
get sortingHandle() {
const handle = this.element.query(By.directive(MatIconButton));
if (handle) {
return new SortingHandle(this.page, handle);
}
return null;
}
get tooltip() {
const handle = this.element.query(By.directive(MatTooltip));
if (handle) {
return handle.injector.get(MatTooltip).message;
}
return null;
}
get title() {
const textNode = this.element.childNodes.find((node) => node.nativeNode.nodeType === Node.TEXT_NODE);
if (textNode != null) {
return textNode
.nativeNode.textContent.trim();
}
return '';
}
get colspan() {
return this.element.nativeElement.colspan;
}
get rowspan() {
return this.element.nativeElement.rowspan;
}
}
// <editor-fold defaultstate="collapsed" desc="Sorting">
class TableSorting {
constructor(table, page) {
this.table = table;
this.page = page;
}
get handles() {
const handles = this.table.debugElement.query((de) => de.references.mainHeader)
.queryAll(By.directive(MatIconButton));
if (!handles) {
return [];
}
return handles.map((handle) => {
return new SortingHandle(this.page, handle);
});
}
get detailRowHandles() {
const handles = this.table.debugElement.query(By.css('.risk-data-table-detail')).query(By.css('thead'))
.queryAll(By.directive(MatIconButton));
if (!handles) {
return null;
}
return handles.map((handle) => {
return new SortingHandle(this.page, handle);
});
}
get currentOrdering() {
return this.table.component.ordering();
}
checkSorting(firstNRows = this.table.component.rows.length, criterium) {
// WARN: call data once as it calls .map on all table rows
const data = this.table.data;
let ordering;
if (criterium) {
ordering = [criterium].concat(this.table.component._defaultOrdering());
}
else {
ordering = this.currentOrdering;
}
for (let i = 1; i < Math.min(data.length, firstNRows); i++) {
ordering.some((criteria) => {
//noinspection EqualityComparisonWithCoercionJS
const valueA = criteria.get(data[i - 1]);
const valueB = criteria.get(data[i]);
if (valueA == null && valueB == null) {
return false;
}
expect(valueA).not.toBeNull();
expect(valueA).not.toBeUndefined();
if (valueB != null) {
if (criteria.descending) {
expect(valueA >= valueB)
.toBeTruthy('Expect: ' + valueA + ' >= '
+ valueB);
}
else {
expect(valueA <= valueB)
.toBeTruthy('Expect: ' + valueA + ' <= '
+ valueB);
}
}
return valueA !== valueB;
});
}
}
}
class SortingHandle {
constructor(page, handle) {
this.page = page;
this.handle = handle;
}
click() {
click(this.handle);
this.page.detectChanges();
}
}
// </editor-fold>
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Table body">
class TableBody {
constructor(element, page) {
this.element = element;
this.page = page;
}
get rows() {
return this.element.queryAll((de) => de.references.masterRow).map((element) => {
return new TableBodyRow(element, this.page);
});
}
get cells() {
let cells = [];
this.element.queryAll((de) => de.references.masterRow).forEach((row) => {
cells = cells.concat(row.queryAll(By.css('td')).map((cell) => {
return new TableBodyCell(cell);
}));
});
return cells;
}
}
class TableBodyRow {
constructor(element, page) {
this.element = element;
this.page = page;
}
get expander() {
return new RowExpander(this.element.query(By.directive(RiskDataTableRowDetailExpanderComponent))
.query(By.directive(MatIconAnchor)));
}
get cells() {
return this.element.queryAll(By.css('td')).map((element) => {
return new TableBodyCell(element);
});
}
get rowDetail() {
const parent = this.element.parent;
if (parent != null) {
const next = parent.children[parent.children.indexOf(this.element) + 1];
if (next && !next.references.masterRow) {
return new TableBodyDetail(next, this.page);
}
}
return null;
}
get highlighted() {
return this.element.nativeElement.classList.contains(HIGHLIGHTER_CLASS);
}
expandRow() {
click(this.element);
this.page.detectChanges();
}
}
class RowExpander {
constructor(element) {
this.element = element;
}
get icon() {
return this.element.query(By.directive(MatIcon)).nativeElement.textContent.trim();
}
get opened() {
return this.icon === 'expand_less';
}
get closed() {
return this.icon === 'expand_more';
}
}
class TableBodyCell {
constructor(element) {
this.element = element;
}
get colspan() {
return this.element.nativeElement.colspan;
}
get rowspan() {
return this.element.nativeElement.rowspan;
}
}
// <editor-fold defaultstate="collapsed" desc="Row detail">
class TableBodyDetail {
constructor(element, page) {
this.element = element;
this.page = page;
}
get body() {
return new TableBodyDetailBody(this.element.query(By.css('tbody')));
}
get highlighted() {
return this.element.nativeElement.classList.contains(HIGHLIGHTER_CLASS);
}
get colspan() {
return this.element.children[0].nativeElement.colspan;
}
header() {
return new TableHeader(this.element.query(By.css('thead')), this.page);
}
}
class TableBodyDetailBody {
constructor(element) {
this.element = element;
}
get rows() {
return this.element.queryAll(By.css('tr')).map((element) => {
return new TableBodyDetailRow(element);
});
}
get cells() {
let cells = [];
this.element.queryAll(By.css('tr')).forEach((row) => {
cells = cells.concat(row.queryAll(By.css('td')).map((cell) => {
return new TableBodyCell(cell);
}));
});
return cells;
}
}
class TableBodyDetailRow {
constructor(element) {
this.element = element;
}
get cells() {
return this.element.queryAll(By.css('td')).map((element) => {
return new TableBodyCell(element);
});
}
}
// </editor-fold>
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Table footer">
class TableFooter {
constructor(element) {
this.element = element;
}
get rows() {
return this.element.queryAll(By.css('tr')).map((element) => {
return new TableFooterRow(element);
});
}
get cells() {
let cells = [];
this.element.queryAll(By.css('tr')).forEach((row) => {
cells = cells.concat(row.queryAll(By.css('th')).map((cell) => {
return new TableBodyCell(cell);
}));
});
return cells;
}
}
class TableFooterRow {
constructor(element) {
this.element = element;
}
get cells() {
return this.element.queryAll(By.css('th')).map((element) => {
return new TableBodyCell(element);
});
}
}
// </editor-fold>
class RecordsCount {
constructor(element) {
this.element = element;
}
get message() {
return this.element.nativeElement.textContent;
}
}
class Pager {
constructor(element, page) {
this.element = element;
this.page = page;
}
get pageButtons() {
return this.element.queryAll(By.directive(MatButtonToggle));
}
expectLeadingButtonsDisabled() {
for (let i = 0; i < 2; i++) {
expect(this.pageButtons[i].classes['mat-button-toggle-disabled'])
.toBe(true, 'First two are disabled.');
}
}
expectLeadingButtonsNotDisabled() {
for (let i = 0; i < 2; i++) {
expect(this.pageButtons[i].classes['mat-button-toggle-disabled'])
.not.toBe(true, 'First two are not disabled.');
}
}
expectTrailingButtonsDisabled() {
for (let i = this.pageButtons.length - 1; i > this.pageButtons.length - 3; i--) {
expect(this.pageButtons[i].classes['mat-button-toggle-disabled'])
.toBe(true, 'Last two are disabled.');
}
}
expectTrailingButtonsNotDisabled() {
for (let i = this.pageButtons.length - 1; i > this.pageButtons.length - 3; i--) {
expect(this.pageButtons[i].classes['mat-button-toggle-disabled'])
.not.toBe(true, 'Last two are not disabled.');
}
}
expectButtonNumbers(numbers) {
for (let i = 0; i < numbers.length; i++) {
expect(this.pageButtons[i + 2].query(By.css('.mat-button-toggle-label-content'))
.nativeElement.textContent.trim())
.toEqual(numbers[i] + '', 'Button numbers are correct');
}
}
expectButtonActive(index) {
expect(this.pageButtons[index].classes['mat-button-toggle-checked'])
.toBe(true, 'Button is active.');
}
click(index) {
click(this.pageButtons[index].query(By.css('.mat-button-toggle-label-content')).nativeElement);
this.page.detectChanges();
this.page.advanceAndDetectChanges();
}
}
function chceckSorting(page, criteria) {
page.dataTable.sorting.checkSorting(150);
page.dataTable.sorting.handles.forEach((handle, index) => {
// Tigger sort based on a handle
handle.click();
page.detectChanges();
// Check the sorting
page.dataTable.sorting.checkSorting(150, {
get: criteria[index]
});
// Tigger sort based on a handle
handle.click();
page.detectChanges();
// Check the sorting
page.dataTable.sorting.checkSorting(150, {
get: criteria[index],
descending: true
});
});
}
class DataTableDefinitionHosted extends Page {
constructor(fixture) {
super(fixture);
}
get dataTable() {
return new RiskDataTableDefinition(this.debugElement.query(By.directive(RiskDataTableComponent)), this);
}
setData(data) {
this.fixture.componentRef.setInput('data', data);
this.detectChanges();
}
setPageSize(size) {
this.fixture.componentRef.setInput('pageSize', size);
this.detectChanges();
}
}
class TestDataTableHostComponent {
constructor() {
this.data = input(TestDataTableHostComponent.defaultData, ...(ngDevMode ? [{ debugName: "data" }] : []));
this.pageSize = input(20, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
this.footer = signal(TestDataTableHostComponent.defaultData[0], ...(ngDevMode ? [{ debugName: "footer" }] : []));
this.defaultOrdering = [
{
get: (record) => {
return record.value3;
},
descending: true
},
(record) => {
return record.value2;
}
];
this.valueGetter = (record) => {
return record.value1;
};
}
static { this.defaultData = (() => {
const data = [];
for (let i = 0; i < 500; i++) {
data.push({
value1: Math.floor(secureRandom() * 20) + ' - value 1',
value2: Math.floor(secureRandom() * 20) + ' - value 2',
value3: Math.floor(secureRandom() * 20) + ' - value 3'
});
}
return data;
})(); }
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TestDataTableHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.15", type: TestDataTableHostComponent, isStandalone: true, selector: "ng-component", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
<risk-data-table [data]="data()"
[footer]="footer()"
[pageSize]="pageSize()"
[defaultOrdering]="defaultOrdering"
[striped]="true">
<risk-data-table-column title="Test Column 1"
[sortingKey]="valueGetter">
<ng-template let-record="row" risk-data-table-cell>
{{ record.value1 }}
</ng-template>
</risk-data-table-column>
<risk-data-table-column>
<ng-template risk-data-table-cell let-record="row" let-expanded="expanded">
<risk-data-table-detail-expander [expanded]="expanded"/>
</ng-template>
<ng-template risk-data-table-footer-cell let-footer="footer">
{{ footer.value1 }}
</ng-template>
</risk-data-table-column>
<!-- Sub detail -->
<risk-data-table-detail-row>
<risk-data-table-column-group>
<risk-data-table-column title="Test Detail Column 1"
[sortingKey]="valueGetter">
<ng-template risk-data-table-cell let-record="row">
{{ record.value1 }}
</ng-template>
</risk-data-table-column>
</risk-data-table-column-group>
<risk-data-table-column-group>
<risk-data-table-column title="Test Detail Column 2">
<ng-template risk-data-table-cell let-record="row">
{{ record.value2 }}
</ng-template>
</risk-data-table-column>
<risk-data-table-column title="Test Detail Column 3">
<ng-template risk-data-table-cell let-record="row">
{{ record.value3 }}
</ng-template>
</risk-data-table-column>
</risk-data-table-column-group>
<risk-data-table-column-group/>
</risk-data-table-detail-row>
</risk-data-table>`, isInline: true, dependencies: [{ kind: "directive", type: RiskDataTableColumnCellDirective, selector: "[risk-data-table-cell]" }, { kind: "directive", type: RiskDataTableColumnDirective, selector: "risk-data-table-column", inputs: ["title", "sortingKey", "tooltip", "contentAlign", "headerAlign", "footerAlign", "class"] }, { kind: "directive", type: RiskDataTableColumnFooterDirective, selector: "[risk-data-table-footer-cell]" }, { kind: "directive", type: RiskDataTableColumnGroupDirective, selector: "risk-data-table-column-group" }, { kind: "component", type: RiskDataTableComponent, selector: "risk-data-table", inputs: ["footer", "pageSize", "striped", "showFooter", "allowEmpty", "trackByRowKey", "highlighting", "stickyHeader", "stickyHeaderOffsetTopPx", "data", "defaultOrdering"], exportAs: ["dataTable"] }, { kind: "directive", type: RiskDataTableRowDetailDirective, selector: "risk-data-table-detail-row" }, { kind: "component", type: RiskDataTableRowDetailExpanderComponent, selector: "risk-data-table-detail-expander", inputs: ["expanded"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TestDataTableHostComponent, decorators: [{
type: Component,
args: [{
template: `
<risk-data-table [data]="data()"
[footer]="footer()"
[pageSize]="pageSize()"
[defaultOrdering]="defaultOrdering"
[striped]="true">
<risk-data-table-column title="Test Column 1"
[sortingKey]="valueGetter">
<ng-template let-record="row" risk-data-table-cell>
{{ record.value1 }}
</ng-template>
</risk-data-table-column>
<risk-data-table-column>
<ng-template risk-data-table-cell let-record="row" let-expanded="expanded">
<risk-data-table-detail-expander [expanded]="expanded"/>
</ng-template>
<ng-template risk-data-table-footer-cell let-footer="footer">
{{ footer.value1 }}
</ng-template>
</risk-data-table-column>
<!-- Sub detail -->
<risk-data-table-detail-row>
<risk-data-table-column-group>
<risk-data-table-column title="Test Detail Column 1"
[sortingKey]="valueGetter">
<ng-template risk-data-table-cell let-record="row">
{{ record.value1 }}
</ng-template>
</risk-data-table-column>
</risk-data-table-column-group>
<risk-data-table-column-group>
<risk-data-table-column title="Test Detail Column 2">
<ng-template risk-data-table-cell let-record="row">
{{ record.value2 }}
</ng-template>
</risk-data-table-column>
<risk-data-table-column title="Test Detail Column 3">
<ng-template risk-data-table-cell let-record="row">
{{ record.value3 }}
</ng-template>
</risk-data-table-column>
</risk-data-table-column-group>
<risk-data-table-column-group/>
</risk-data-table-detail-row>
</risk-data-table>`,
imports: [
RiskDataTableColumnCellDirective,
RiskDataTableColumnDirective,
RiskDataTableColumnFooterDirective,
RiskDataTableColumnGroupDirective,
RiskDataTableComponent,
RiskDataTableRowDetailDirective,
RiskDataTableRowDetailExpanderComponent
]
}]
}], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }] } });
class RiskHighLighterDirectiveTestComponent {
trackBy(index, row) {
return row.rowData;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RiskHighLighterDirectiveTestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: RiskHighLighterDirectiveTestComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
<div [risk-data-table-highlighter]="trackBy" [risk-data-table-highlighter-context]="context"></div>`, isInline: true, dependencies: [{ kind: "directive", type: RiskHighlighterDirective, selector: "[risk-data-table-highlighter]", inputs: ["risk-data-table-highlighter", "risk-data-table-highlighter-context"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RiskHighLighterDirectiveTestComponent, decorators: [{
type: Component,
args: [{
template: `
<div [risk-data-table-highlighter]="trackBy" [risk-data-table-highlighter-context]="context"></div>`,
imports: [RiskHighlighterDirective]
}]
}] });
class HighLighterDirectivePage extends Page {
constructor(fixture) {
super(fixture);
}
get highlightedElement() {
return this.debugElement.query(By.directive(RiskHighlighterDirective));
}
get classList() {
return this.highlightedElement.nativeElement.classList;
}
get highlighter() {
return this.highlightedElement.injector.get(RiskHighlighterDirective);
}
}
// @dynamic
class ByUtil {
static and(...predicates) {
return (val) => {
return predicates.every((predicate) => predicate(val));
};
}
static or(...predicates) {
return (val) => {
return predicates.some((predicate) => predicate(val));
};
}
static not(predicate) {
return (val) => {
return !predicate(val);
};
}
}
class RiskLayoutComponentDefinition {
constructor(debugElement, page) {
this.debugElement = debugElement;
this.page = page;
}
get component() {
return this.debugElement.componentInstance;
}
get headToolbar() {
return this.debugElement.query(ByUtil.and(By.directive(MatToolbar), By.css('.mat-elevation-z2')));
}
get logo() {
return this.headToolbar.query(By.css('.risk-layout-logo'));
}
get menu() {
return this.headToolbar.queryAll(ByUtil.or(By.css('[menu-horizontal]'), By.css('risk-layout-horizontal')));
}
get sideNavContainer() {
return this.debugElement.query(By.directive(MatSidenavContainer));
}
get sideNav() {
return this.sideNavContainer.query(By.directive(MatSidenav));
}
get sideNavMenu() {
return this.sideNav.queryAll(ByUtil.or(By.css('[menu-vertical]'), By.css('risk-layout-vertical')));
}
get content() {
return this.sideNavContainer.query(By.css('.risk-layout-content'));
}
openSideNav() {
this.sideNav.componentInstance.open();
this.page.advanceAndDetectChanges();
}
closeSideNav() {
this.sideNav.componentInstance.close();
this.page.advanceAndDetectChanges(1);
}
}
class RiskLayoutTestComponentHostPage extends Page {
get layoutComponent() {
return new RiskLayoutComponentDefinition(this.debugElement.query(By.directive(RiskLayoutComponent)), this);
}
}
class RiskLayoutTestHostComponent {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RiskLayoutTestHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: RiskLayoutTestHostComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
<risk-layout>
<!-- menu-horizontal -->
<risk-layout-horizontal>
HorizontalMenu
</risk-layout-horizontal>
<!-- menu-horizontal -->
<!-- menu-vertical -->
<risk-layout-vertical>
Vertical menu
</risk-layout-vertical>
<span>Content</span>
</risk-layout>
`, isInline: true, dependencies: [{ kind: "component", type: RiskLayoutComponent, selector: "risk-layout", inputs: ["smallScreenMenuVisible", "smallScreenWidth", "footerVisible", "toolbarBackgroundColor", "backgroundColor"] }, { kind: "directive", type: RiskLayoutHorizontalDirective, selector: "risk-layout-horizontal" }, { kind: "directive", type: RiskLayoutVerticalDirective, selector: "risk-layout-vertical" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RiskLayoutTestHostComponent, decorators: [{
type: Component,
args: [{
template: `
<risk-layout>
<!-- menu-horizontal -->
<risk-layout-horizontal>
HorizontalMenu
</risk-layout-horizontal>
<!-- menu-horizontal -->
<!-- menu-vertical -->
<risk-layout-vertical>
Vertical menu
</risk-layout-vertical>
<span>Content</span>
</risk-layout>
`,
imports: [
RiskLayoutComponent,
RiskLayoutHorizontalDirective,
RiskLayoutVerticalDirective
]
}]
}] });
class LinkDefinition {
constructor(page, link) {
this.page = page;
this.link = link;
}
get stub() {
return this.link.injector.get(RouterLink);
}
get text() {
return this.link.nativeElement.textContent.trim();
}
click() {
click(this.link.nativeElement);
this.page.advanceAndDetectChanges();
}
}
class LinkOnlyPage extends Page {
constructor(fixture) {
super(fixture);
}
get link() {
return new LinkDefinition(this, this.debugElement.query(By.directive(RouterLink)));
}
}
class LoginMenuPage extends Page {
get menuTrigger() {
return this.debugElement.query(By.directive(MatMenuTrigger));
}
clickMenuTrigger() {
click(this.menuTrigger);
this.waitForMenu();
}
waitForMenu() {
this.detectChanges(500);
}
get loginLink() {
return new LinkDefinition(this, this.debugElement.query(ByUtil.and(By.directive(MatMenuItem), (value) => value.nativeElement.textContent.trim().endsWith('Login'))));
}
get logoutLink() {
return new LinkDefinition(this, this.debugElement.query(ByUtil.and(By.directive(MatMenuItem), (value) => value.nativeElement.textContent.trim().endsWith('Logout'))));
}
}
class RiskLoginPage extends Page {
constructor(fixture) {
super(fixture);
}
get formElement() {
return this.debugElement.query(By.css('form'));
}
get usernameElement() {
return this.formElement.query(By.css('input[name=username]'));
}
set username(username) {
setNgModelValue(this.usernameElement, username);
this.fixture.detectChanges();
}
get passwordElement() {
return this.formElement.query(By.css('input[name=password]'));
}
set password(password) {
setNgModelValue(this.passwordElement, password);
this.fixture.detectChanges();
}
get loginButtonElement() {
return this.formElement.query(By.directive(MatButton));
}
get successMessage() {
const debugElement = this.debugElement.query(ByUtil.and(By.directive(RiskMessageComponent), By.css(RISK_GOOD_SELECTOR)));
if (!debugElement) {
return null;
}
return new RiskMessageComponentDef(debugElement);
}
get errorMessage() {
const debugElement = this.debugElement.query(ByUtil.or(ByUtil.and(By.directive(RiskMessageComponent), By.css(RISK_ERROR_SELECTOR)), By.css('h3')));
if (!debugElement) {
return null;
}
return new RiskMessageComponentDef(debugElement);
}
clickLogin() {
click(this.loginButtonElement.nativeElement);
tick();
this.detectChanges();
}
}
class TestMessageHostComponent {
constructor() {
this.message = 'custom message';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TestMessageHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: TestMessageHostComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
<risk-error [message]="message"/>
<risk-good [message]="message"/>
<risk-info [message]="message"/>
<risk-message [message]="message"/>
<risk-warn [message]="message"/>
<risk-initial-load [message]="message"/>
<risk-update-failed [message]="message"/>
<risk-no-data [message]="message"/>
`, isInline: true, dependencies: [{ kind: "component", type: RiskMessageComponent, selector: "risk-error, risk-good, risk-info, risk-message, risk-warn, risk-initial-load, risk-no-data, risk-update-failed", inputs: ["message"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TestMessageHostComponent, decorators: [{
type: Component,
args: [{
template: `
<risk-error [message]="message"/>
<risk-good [message]="message"/>
<risk-info [message]="message"/>
<risk-message [message]="message"/>
<risk-warn [message]="message"/>
<risk-initial-load [message]="message"/>
<risk-update-failed [message]="message"/>
<risk-no-data [message]="message"/>
`,
imports: [
RiskMessageComponent
]
}]
}] });
class MessageHostedPage extends Page {
constructor(fixture) {
super(fixture);
}
get error() {
return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_ERROR_SELECTOR)));
}
get good() {
return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_GOOD_SELECTOR)));
}
get info() {
return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_INFO_SELECTOR)));
}
get message() {
return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_MESSAGE_SELECTOR)));
}
get warn() {
return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_WARN_SELECTOR)));
}
get initialLoad() {
return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_INITIAL_LOAD_SELECTOR)));
}
get noData() {
return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_NO_DATA_SELECTOR)));
}
get updateFailed() {
return new RiskMessageComponentDef(this.debugElement.query(By.css(RISK_UPDATE_FAILED_SELECTOR)));
}
}
const storage = { authRequestedPath: null };
class AuthRoutingFlowServiceStub {
constructor() {
this.authServiceStub = inject(AUTH_PROVIDER);
this.authConfig = inject(AUTH_CONFIG);
// cleanup
storage.authRequestedPath = null;
}
get authFlow() {
return this.authConfig.flow;
}
get authorizationCodeFlow() {
return this.authFlow === AuthFlow.AUTHORIZATION_CODE;
}
get implicitFlow() {
return this.authFlow === AuthFlow.IMPLICIT;
}
get hybridFlow() {
return this.authFlow === AuthFlow.HYBRID;
}
get directFlow() {
return this.authFlow === AuthFlow.DIRECT;
}
logout() {
return this.authServiceStub.logout().pipe(defaultIfEmpty(0), map(() => true));
}
login(username, password) {
return this.authServiceStub.directLogin(username, password);
}
loginViaService() {
return this.authServiceStub.loginViaAuthService();
}
storeRequestedPath(state) {
storage.authRequestedPath = state?.url || null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AuthRoutingFlowServiceStub, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AuthRoutingFlowServiceStub }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AuthRoutingFlowServiceStub, decorators: [{
type: Injectable
}], ctorParameters: () => [] });
class AuthServiceStub {
constructor() {
this._loggedInStream = new ReplaySubjectExt(1);
}
get loggedIn() {
return defer(() => of(!!this.user));
}
get userProfile() {
return defer(() => of(this.user ? {
name: this.user
} : undefined));
}
get loggedInStream() {
return this._loggedInStream.asObservable();
}
loginViaAuthService() {
return of(true);
}
checkLocationForLoginData() {
return of(true);
}
directLogin(username, password) {
this.user = username;
this.emitLoginStatusChange(true);
return of(true);
}
logout() {
this.emitLoginStatusChange(false);
delete this.user;
return EMPTY;
}
emitLoginStatusChange(status) {
if (this._loggedInStream.lastValue !== status) {
this._loggedInStream.next(status);
}
}
refreshToken() {
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AuthServiceStub, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AuthServiceStub }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AuthServiceStub, decorators: [{
type: Injectable
}] });
const WELL_KNOWN = {
endpoints: {
auth: '/auth',
token: '/token',
logout: '/logout'
},
issuer: 'risk-auth'
};
class WellKnownServiceStub {
get wellKnown() {
return of(WELL_KNOWN);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: WellKnownServiceStub, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: WellKnownServiceStub }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: WellKnownServiceStub, decorators: [{
type: Injectable
}] });
/**
* A lightweight Router stub for testing that doesn't require actual routing infrastructure.
* Use this when you want complete isolation from the Router and only need to verify navigation calls.
*/
class RouterStub {
constructor() {
this._url = '/';
this._events$ = new BehaviorSubject(null);
}
get url() {
return this._url;
}
get events() {
return this._events$.asObservable();
}
get routerState() {
return {
snapshot: {
url: this._url,
root: {}
}
};
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
navigate(commands, _extras) {
this._url = commands.join('/');
return Promise.resolve(true);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
navigateByUrl(url, _extras) {
this._url = typeof url === 'string' ? url : url.toString();
return Promise.resolve(true);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
createUrlTree(_commands, _navigationExtras) {
return {};
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
serializeUrl(_url) {
return '';
}
// eslint-d