From 9ec5605590c7dd60ae5a2d6b5f53f5a7d73d309a Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 14 Oct 2025 09:47:24 -0400 Subject: [PATCH] feat(material/table): add harness for "no data" row Adds a harness for the "no data" row to make it easier to interact with in tests. --- goldens/material/table/testing/index.api.md | 15 +++++++++++ src/material/table/testing/cell-harness.ts | 15 +++++++++++ src/material/table/testing/row-harness.ts | 24 ++++++++++++++++++ .../table/testing/table-harness.spec.ts | 25 ++++++++++++++++--- src/material/table/testing/table-harness.ts | 6 +++++ 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/goldens/material/table/testing/index.api.md b/goldens/material/table/testing/index.api.md index 80f429d6c34d..6ebe4ec17923 100644 --- a/goldens/material/table/testing/index.api.md +++ b/goldens/material/table/testing/index.api.md @@ -58,6 +58,20 @@ export class MatHeaderRowHarness extends _MatRowHarnessBase(this: ComponentHarnessConstructor, options?: RowHarnessFilters): HarnessPredicate; } +// @public +export class MatNoDataCellHarness extends _MatCellHarnessBase { + static hostSelector: string; + static with(options?: CellHarnessFilters): HarnessPredicate; +} + +// @public +export class MatNoDataRowHarness extends _MatRowHarnessBase { + // (undocumented) + protected _cellHarness: typeof MatNoDataCellHarness; + static hostSelector: string; + static with(this: ComponentHarnessConstructor, options?: RowHarnessFilters): HarnessPredicate; +} + // @public export class MatRowHarness extends _MatRowHarnessBase { // (undocumented) @@ -89,6 +103,7 @@ export class MatTableHarness extends ContentContainerComponentHarness { getCellTextByIndex(): Promise; getFooterRows(filter?: RowHarnessFilters): Promise; getHeaderRows(filter?: RowHarnessFilters): Promise; + getNoDataRow(filter?: RowHarnessFilters): Promise; getRows(filter?: RowHarnessFilters): Promise; // (undocumented) _headerRowHarness: typeof MatHeaderRowHarness; diff --git a/src/material/table/testing/cell-harness.ts b/src/material/table/testing/cell-harness.ts index 47fa5ccc92f5..2cae26f52665 100644 --- a/src/material/table/testing/cell-harness.ts +++ b/src/material/table/testing/cell-harness.ts @@ -99,3 +99,18 @@ export class MatFooterCellHarness extends _MatCellHarnessBase { return _MatCellHarnessBase._getCellPredicate(this, options); } } + +/** Harness for interacting with an Angular Material table cell inside a "no data" row. */ +export class MatNoDataCellHarness extends _MatCellHarnessBase { + /** The selector for the host element of a `MatNoDataCellHarness` instance. */ + static hostSelector = '.mat-no-data-cell'; + + /** + * Gets a `HarnessPredicate` that can be used to search for a table cell with specific attributes. + * @param options Options for narrowing the search + * @return a `HarnessPredicate` configured with the given options. + */ + static with(options: CellHarnessFilters = {}): HarnessPredicate { + return _MatCellHarnessBase._getCellPredicate(this, options); + } +} diff --git a/src/material/table/testing/row-harness.ts b/src/material/table/testing/row-harness.ts index 5db30792c7ce..605aec6538fb 100644 --- a/src/material/table/testing/row-harness.ts +++ b/src/material/table/testing/row-harness.ts @@ -17,6 +17,7 @@ import { MatCellHarness, MatFooterCellHarness, MatHeaderCellHarness, + MatNoDataCellHarness, } from './cell-harness'; import {CellHarnessFilters, RowHarnessFilters} from './table-harness-filters'; @@ -122,3 +123,26 @@ export class MatFooterRowHarness extends _MatRowHarnessBase< return new HarnessPredicate(this, options); } } + +/** Harness for interacting with an Angular Material table "no data" row. */ +export class MatNoDataRowHarness extends _MatRowHarnessBase< + typeof MatHeaderCellHarness, + MatHeaderCellHarness +> { + /** The selector for the host element of a `MatNoDataRowHarness` instance. */ + static hostSelector = '.mat-mdc-no-data-row'; + protected _cellHarness = MatNoDataCellHarness; + + /** + * Gets a `HarnessPredicate` that can be used to search for a table header row with specific + * attributes. + * @param options Options for narrowing the search + * @return a `HarnessPredicate` configured with the given options. + */ + static with( + this: ComponentHarnessConstructor, + options: RowHarnessFilters = {}, + ): HarnessPredicate { + return new HarnessPredicate(this, options); + } +} diff --git a/src/material/table/testing/table-harness.spec.ts b/src/material/table/testing/table-harness.spec.ts index 723b39af7a44..e0e3dd46516d 100644 --- a/src/material/table/testing/table-harness.spec.ts +++ b/src/material/table/testing/table-harness.spec.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, signal} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader, parallel} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; @@ -176,11 +176,24 @@ describe('MatTableHarness', () => { symbol: 'H', }); }); + + it('should be able to get the "no data" row', async () => { + const table = await loader.getHarness(MatTableHarness); + expect(await table.getNoDataRow()).toBe(null); + + fixture.componentInstance.dataSource.set([]); + const row = await table.getNoDataRow(); + const cells = await row?.getCells(); + + expect(row).toBeTruthy(); + expect(cells?.length).toBe(1); + expect(await cells?.[0].getText()).toBe('No data'); + }); }); @Component({ template: ` - +
@@ -208,13 +221,17 @@ describe('MatTableHarness', () => { + + + +
No. {{element.position}}
No data
`, imports: [MatTableModule], }) class TableHarnessTest { displayedColumns: string[] = ['position', 'name', 'weight', 'symbol']; - dataSource = [ + dataSource = signal([ {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'}, {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'}, {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'}, @@ -225,5 +242,5 @@ class TableHarnessTest { {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'}, {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'}, {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'}, - ]; + ]); } diff --git a/src/material/table/testing/table-harness.ts b/src/material/table/testing/table-harness.ts index 0b8cd7b81a1b..4ca8a40ac541 100644 --- a/src/material/table/testing/table-harness.ts +++ b/src/material/table/testing/table-harness.ts @@ -15,6 +15,7 @@ import { import { MatFooterRowHarness, MatHeaderRowHarness, + MatNoDataRowHarness, MatRowHarness, MatRowHarnessColumnsText, } from './row-harness'; @@ -64,6 +65,11 @@ export class MatTableHarness extends ContentContainerComponentHarness { return this.locatorForAll(this._footerRowHarness.with(filter))(); } + /** Gets the "no data" row in the table, if any. */ + async getNoDataRow(filter: RowHarnessFilters = {}): Promise { + return this.locatorForOptional(MatNoDataRowHarness.with(filter))(); + } + /** Gets the text inside the entire table organized by rows. */ async getCellTextByIndex(): Promise { const rows = await this.getRows();