Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates to table page objects #2308

Merged
merged 12 commits into from
Jul 31, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Export mapping column page object; extend the table page object in Angular to have an async function to wait for data to render when using the `data$` property of the table directive",
"packageName": "@ni/nimble-angular",
"email": "20542556+mollykreis@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Add function to the table page object for getting the text content of a column header",
"packageName": "@ni/nimble-components",
"email": "20542556+mollykreis@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "../../../../../../node_modules/ng-packagr/ng-package.schema.json",
"lib": {
"entryFile": "public-api.ts"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './table-column-mapping.pageobject';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { TableColumnMappingPageObject } from '@ni/nimble-components/dist/esm/table-column/mapping/testing/table-column-mapping.pageobject';

export { TableColumnMappingPageObject };
Original file line number Diff line number Diff line change
@@ -1,3 +1,35 @@
import { TablePageObject } from '@ni/nimble-components/dist/esm/table/testing/table.pageobject';
import { TablePageObject as NimbleComponentsTablePageObject } from '@ni/nimble-components/dist/esm/table/testing/table.pageobject';
import { waitForUpdatesAsync } from '@ni/nimble-angular';
import type { Table, TableRecord } from '@ni/nimble-angular/table';

export { TablePageObject };
/**
* The page object for the `nimble-table` component to provide consistent ways of querying
* and interacting with the component during tests.
*
* This Angular version of the page object extends the nimble-components version to add the ability
* to wait for data updates to be applied to the table since the timing isn't easily observable
* when using the `data$` property.
*/
export class TablePageObject<T extends TableRecord> extends NimbleComponentsTablePageObject<T> {
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
private mostRecentSetDataPromise?: Promise<void>;

public constructor(tableElement: Table<T>) {
super(tableElement);

// Cache the most recent promise returned from calls to setData() on the nimble table
// so that we can appropriately wait for them to complete to know when the Angular
// data$ Observable has been applied to the table.
const originalSetDataFn = tableElement.setData.bind(tableElement);
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
tableElement.setData = async (...args): Promise<void> => {
this.mostRecentSetDataPromise = originalSetDataFn(...args);
return this.mostRecentSetDataPromise;
};
}

public async waitForDataUpdatesToRender(): Promise<void> {
if (this.mostRecentSetDataPromise) {
await this.mostRecentSetDataPromise;
}
await waitForUpdatesAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Subject } from 'rxjs';
import { NimbleTableModule, Table, TableRecord } from '@ni/nimble-angular/table';
import { NimbleTableColumnTextModule } from '@ni/nimble-angular/table-column/text';
import { TablePageObject } from '../table.pageobject';

describe('Table page object', () => {
describe('data updates', () => {
interface SimpleRecord extends TableRecord {
field1: string;
field2: string;
}

@Component({
template: `
<nimble-table #table [data$]="data$">
<nimble-table-column-text field-name="field1">Column 1</nimble-table-column-text>
<nimble-table-column-text field-name="field2">Column 2</nimble-table-column-text>
</nimble-table>
`
})
class TestHostComponent {
@ViewChild('table', { read: ElementRef }) public tableElement: ElementRef<Table<SimpleRecord>>;
public dataSubject = new Subject<SimpleRecord[]>();
public data$ = this.dataSubject.asObservable();
}

let fixture: ComponentFixture<TestHostComponent>;
let pageObject: TablePageObject<SimpleRecord>;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestHostComponent],
imports: [
NimbleTableModule,
NimbleTableColumnTextModule
]
});
fixture = TestBed.createComponent(TestHostComponent);
fixture.detectChanges();

pageObject = new TablePageObject<SimpleRecord>(fixture.componentInstance.tableElement.nativeElement);
});

it('can await rendering to be done after emitting a new value to data$ observable', async () => {
expect(pageObject.getRenderedRowCount()).toBe(0);

const data: SimpleRecord[] = [{
field1: 'foo',
field2: 'bar'
}];
fixture.componentInstance.dataSubject.next(data);
expect(pageObject.getRenderedRowCount()).toBe(0);

await pageObject.waitForDataUpdatesToRender();
expect(pageObject.getRenderedRowCount()).toBe(1);
});

it('can await multiple data updates', async () => {
let data: SimpleRecord[] = [{
field1: 'foo',
field2: 'bar'
}];
fixture.componentInstance.dataSubject.next(data);

await pageObject.waitForDataUpdatesToRender();
expect(pageObject.getRenderedRowCount()).toBe(1);
expect(pageObject.getRenderedCellTextContent(0, 0)).toBe('foo');
expect(pageObject.getRenderedCellTextContent(0, 1)).toBe('bar');

data = [{
field1: 'hello',
field2: 'world'
}];
fixture.componentInstance.dataSubject.next(data);

await pageObject.waitForDataUpdatesToRender();
expect(pageObject.getRenderedRowCount()).toBe(1);
expect(pageObject.getRenderedCellTextContent(0, 0)).toBe('hello');
expect(pageObject.getRenderedCellTextContent(0, 1)).toBe('world');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ export class TablePageObject<T extends TableRecord> {
);
}

public getHeaderTextContent(columnIndex: number): string {
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
return (
this.getHeaderContent(
columnIndex
)?.firstChild?.textContent?.trim() ?? ''
);
}

public dispatchEventToHeader(
columnIndex: number,
event: Event
Expand Down
6 changes: 2 additions & 4 deletions packages/nimble-components/src/table/tests/table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,12 @@ describe('Table', () => {
await element.setData(simpleTableData);
await waitForUpdatesAsync();

let headerContent = pageObject.getHeaderContent(0)!.firstChild;
expect(headerContent?.textContent).toEqual('stringData');
expect(pageObject.getHeaderTextContent(0)).toEqual('stringData');
mollykreis marked this conversation as resolved.
Show resolved Hide resolved

element.columns[0]!.textContent = 'foo';
await waitForUpdatesAsync();

headerContent = pageObject.getHeaderContent(0)!.firstChild;
expect(headerContent?.textContent).toEqual('foo');
expect(pageObject.getHeaderTextContent(0)).toEqual('foo');
});

it('sets title when header text is ellipsized', async () => {
Expand Down