mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-10-20 18:56:30 +00:00
Accounts, Contacts and Sidebar specs: follow the current table layout, run them
The three suites were skipped since the table rework. What changed under them: - An account or contact spans three rows (main, total balance, details), marked from isFirst; the helpers paired rows by two and filtered on a row class the rows no longer carry. A collapsed details row is hidden, so rows are read including hidden ones. - Accounts render one table per account group; the page object reads the rows of every group table in order, and the sorting fixture keeps its three accounts in one group, since sorting applies within a group. - The expand toggle is the row's expand column, not a row-toggle element. - useAccountInfo reads ownership from the keyring context, which the test page did not provide, so saving a name did nothing; the page now renders the real KeyringCtxRoot. The mock api gains consts.system.ss58Prefix. - Badge popups (react-tooltip v6) render only while hovered; the specs hover the badge first. The multisig badge needs the isMultisig flag. - Addresses are shown in full and shortened by css; the fixtures with shortened addresses are gone. - The total balance, transferable and locked cards are always shown, as placeholders without accounts; the specs check that instead of their absence. - Checks that needed matchers the runner lacks (not.toHaveLength, toHaveProperty, toThrow with a contained message) are written against the DOM directly.
This commit is contained in:
1 parent
61c681567d
commit
3767c819e3
11 files changed
+130
-101
No files matched your search
@@ -9,15 +9,14 @@ import type { u32 } from '@pezkuwi/types';
|
||||
import type { AccountId, Multisig, ProxyDefinition, Timepoint, Voting, VotingDelegating } from '@pezkuwi/types/interfaces';
|
||||
import type { AccountRow } from '../../test/pageElements/AccountRow.js';
|
||||
|
||||
import { fireEvent, screen, within } from '@testing-library/react';
|
||||
import { fireEvent, screen, waitFor, within } from '@testing-library/react';
|
||||
|
||||
import { PEZKUWI_GENESIS } from '@pezkuwi/apps-config';
|
||||
import i18next from '@pezkuwi/react-components/i18n';
|
||||
import { toShortAddress } from '@pezkuwi/react-components/util';
|
||||
import { anAccountWithBalance, anAccountWithBalanceAndMeta, anAccountWithInfo, anAccountWithInfoAndMeta, anAccountWithMeta, anAccountWithStaking } from '@pezkuwi/test-support/creation/account';
|
||||
import { makeStakingLedger as ledger } from '@pezkuwi/test-support/creation/staking';
|
||||
import { alice, bob, MemoryStore } from '@pezkuwi/test-support/keyring';
|
||||
import { assertHasClass, assertTextContent, balance, mockApiHooks, showBalance } from '@pezkuwi/test-support/utils';
|
||||
import { assertHasClass, assertNotHasClass, assertTextContent, balance, mockApiHooks, showBalance } from '@pezkuwi/test-support/utils';
|
||||
import { TypeRegistry } from '@pezkuwi/types/create';
|
||||
import { keyring } from '@pezkuwi/ui-keyring';
|
||||
import { BN } from '@pezkuwi/util';
|
||||
@@ -28,9 +27,10 @@ await mockAccountsPageHooks();
|
||||
|
||||
const { AccountsPage } = await import('../../test/pages/accountsPage.js');
|
||||
|
||||
// FIXME isSplit Table
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
describe.skip('Accounts page', () => {
|
||||
// the totals always shown; bonded, redeemable and unbonding only when non-zero
|
||||
const SUMMARY_TOTALS = ['card-summary:total balance', 'card-summary:total transferable', 'card-summary:total locked'];
|
||||
|
||||
describe('Accounts page', () => {
|
||||
let accountsPage: InstanceType<typeof AccountsPage>;
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -75,10 +75,11 @@ describe.skip('Accounts page', () => {
|
||||
await accountsTable.assertText(noAccountsMessage);
|
||||
});
|
||||
|
||||
it('no summary is displayed', () => {
|
||||
const summaries = screen.queryAllByTestId(/card-summary:total \w+/i);
|
||||
it('the summary shows placeholders, and no staking totals', async () => {
|
||||
const summaries = await screen.findAllByTestId(/card-summary:/i);
|
||||
|
||||
expect(summaries).toHaveLength(0);
|
||||
expect(summaries.map((s) => s.dataset.testid)).toEqual(SUMMARY_TOTALS);
|
||||
summaries.forEach((s) => assertHasClass(s.querySelector('.ui--FormatBalance'), '--tmp'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -120,9 +121,7 @@ describe.skip('Accounts page', () => {
|
||||
{ amount: balance(150), name: 'reserved' }]);
|
||||
});
|
||||
|
||||
// FIXME multiple tables
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('derived account displays parent account info', async () => {
|
||||
it('derived account displays parent account info', async () => {
|
||||
accountsPage.renderAccountsWithDefaultAddresses(
|
||||
anAccountWithMeta({ isInjected: true, name: 'ALICE', whenCreated: 200 }),
|
||||
anAccountWithMeta({ name: 'ALICE_CHILD', parentAddress: alice, whenCreated: 300 })
|
||||
@@ -134,26 +133,25 @@ describe.skip('Accounts page', () => {
|
||||
await accountRows[1].assertParentAccountName('ALICE');
|
||||
});
|
||||
|
||||
// FIXME broken after column rework
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('a separate column for parent account is not displayed', async () => {
|
||||
it('parent and type are not separate columns, the type is in the row details', async () => {
|
||||
accountsPage.renderDefaultAccounts(1);
|
||||
const accountsTable = await accountsPage.getTable();
|
||||
const rows = await accountsTable.getRows();
|
||||
|
||||
accountsTable.assertColumnNotExist('parent');
|
||||
accountsTable.assertColumnExists('type');
|
||||
accountsTable.assertColumnNotExist('type');
|
||||
assertTextContent(rows[0].detailsRow, /account type ?qr/);
|
||||
});
|
||||
|
||||
it('account rows display the shorted address', async () => {
|
||||
it('account rows display the address', async () => {
|
||||
accountsPage.renderAccountsForAddresses(
|
||||
alice
|
||||
);
|
||||
const accountRows = await accountsPage.getAccountRows();
|
||||
|
||||
expect(accountRows).toHaveLength(1);
|
||||
const aliceShortAddress = toShortAddress(alice);
|
||||
|
||||
await accountRows[0].assertShortAddress(aliceShortAddress);
|
||||
// shown in full, shortened by css
|
||||
await accountRows[0].assertShortAddress(alice);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
@@ -181,20 +179,23 @@ describe.skip('Accounts page', () => {
|
||||
|
||||
assertHasClass(row.detailsRow, 'isCollapsed');
|
||||
|
||||
await row.expand();
|
||||
row.expand();
|
||||
|
||||
assertHasClass(row.detailsRow, 'isExpanded');
|
||||
});
|
||||
|
||||
it('displays some summary', () => {
|
||||
it('displays the summary totals, not placeholders', async () => {
|
||||
accountsPage.renderAccountsWithDefaultAddresses(
|
||||
anAccountWithBalance({ freeBalance: balance(500) }),
|
||||
anAccountWithBalance({ freeBalance: balance(200), reservedBalance: balance(150) })
|
||||
);
|
||||
|
||||
const summaries = screen.queryAllByTestId(/card-summary:total \w+/i);
|
||||
await waitFor(async () => {
|
||||
const summaries = await screen.findAllByTestId(/card-summary:total \w+/i);
|
||||
|
||||
expect(summaries).not.toHaveLength(0);
|
||||
expect(summaries.map((s) => s.dataset.testid)).toEqual(SUMMARY_TOTALS);
|
||||
summaries.forEach((s) => assertNotHasClass(s.querySelector('.ui--FormatBalance'), '--tmp'));
|
||||
});
|
||||
});
|
||||
|
||||
it('displays balance summary', async () => {
|
||||
@@ -306,21 +307,14 @@ describe.skip('Accounts page', () => {
|
||||
await accountsTable.assertRowsOrder([3, 1, 2]);
|
||||
});
|
||||
|
||||
// FIXME multiple tables now
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
describe.skip('when sorting is used', () => {
|
||||
// the page sorts within each account group, so all three share a group
|
||||
describe('when sorting is used', () => {
|
||||
let accountsTable: Table;
|
||||
|
||||
beforeEach(async () => {
|
||||
accountsPage.renderAccountsWithDefaultAddresses(
|
||||
anAccountWithBalanceAndMeta({ freeBalance: balance(1) }, { isInjected: true, name: 'bbb', whenCreated: 200 }),
|
||||
anAccountWithBalanceAndMeta({ freeBalance: balance(2) }, {
|
||||
hardwareType: 'ledger',
|
||||
isHardware: true,
|
||||
name: 'bb',
|
||||
parentAddress: alice,
|
||||
whenCreated: 300
|
||||
}),
|
||||
anAccountWithBalanceAndMeta({ freeBalance: balance(2) }, { isInjected: true, name: 'bb', parentAddress: alice, whenCreated: 300 }),
|
||||
anAccountWithBalanceAndMeta({ freeBalance: balance(3) }, { isInjected: true, name: 'aaa', whenCreated: 100 })
|
||||
);
|
||||
|
||||
@@ -468,22 +462,20 @@ describe.skip('Accounts page', () => {
|
||||
describe('show popups', () => {
|
||||
beforeEach(async () => {
|
||||
accountsPage.renderAccountsWithDefaultAddresses(
|
||||
anAccountWithInfoAndMeta({ flags: { isDevelopment: true } as AddressFlags }, { name: 'alice', who: [] })
|
||||
anAccountWithInfoAndMeta({ flags: { isDevelopment: true, isMultisig: true } as AddressFlags }, { name: 'alice', who: [] })
|
||||
);
|
||||
accountRows = await accountsPage.getAccountRows();
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('development', async () => {
|
||||
await accountRows[0].assertBadge('wrench-badge');
|
||||
const badgePopup = getPopupById(/wrench-badge-hover.*/);
|
||||
const badgePopup = await openBadgePopup('wrench-badge');
|
||||
|
||||
await within(badgePopup).findByText('This is a development account derived from the known development seed. Do not use for any funds on a non-development network.');
|
||||
});
|
||||
|
||||
it('multisig approvals', async () => {
|
||||
await accountRows[0].assertBadge('file-signature-badge');
|
||||
const badgePopup = getPopupById(/file-signature-badge-hover.*/);
|
||||
const badgePopup = await openBadgePopup('file-signature-badge');
|
||||
const approvalsModalToggle = await within(badgePopup).findByText('View pending approvals');
|
||||
|
||||
fireEvent.click(approvalsModalToggle);
|
||||
@@ -494,8 +486,7 @@ describe.skip('Accounts page', () => {
|
||||
});
|
||||
|
||||
it('delegate democracy vote', async () => {
|
||||
await accountRows[0].assertBadge('calendar-check-badge');
|
||||
const badgePopup = getPopupById(/calendar-check-badge-hover.*/);
|
||||
const badgePopup = await openBadgePopup('calendar-check-badge');
|
||||
const delegateModalToggle = await within(badgePopup).findByText('Manage delegation');
|
||||
|
||||
fireEvent.click(delegateModalToggle);
|
||||
@@ -506,8 +497,7 @@ describe.skip('Accounts page', () => {
|
||||
});
|
||||
|
||||
it('proxy overview', async () => {
|
||||
await accountRows[0].assertBadge('sitemap-badge');
|
||||
const badgePopup = getPopupById(/sitemap-badge-hover.*/);
|
||||
const badgePopup = await openBadgePopup('sitemap-badge');
|
||||
const proxyOverviewToggle = await within(badgePopup).findByText('Manage proxies');
|
||||
|
||||
fireEvent.click(proxyOverviewToggle);
|
||||
@@ -522,14 +512,21 @@ describe.skip('Accounts page', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function getPopupById (popupId: RegExp): HTMLElement {
|
||||
const badgePopup = accountsPage.getById(popupId);
|
||||
// a badge renders its popup while hovered
|
||||
async function openBadgePopup (badgeTestId: string): Promise<HTMLElement> {
|
||||
const badge = await within(accountRows[0].primaryRow).findByTestId(badgeTestId);
|
||||
|
||||
fireEvent.mouseEnter(badge);
|
||||
|
||||
return waitFor(() => {
|
||||
const badgePopup = accountsPage.getById(new RegExp(`^${badgeTestId}-hover-`));
|
||||
|
||||
if (!badgePopup) {
|
||||
throw new Error('badge popup should be found');
|
||||
throw new Error(`${badgeTestId} popup should be found`);
|
||||
}
|
||||
|
||||
return badgePopup;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -23,16 +23,21 @@ settings.set({ ...settings.get(), storage: 'on' });
|
||||
export class AccountsPage extends Page {
|
||||
constructor () {
|
||||
super(
|
||||
<AccountOverview onStatusChange={NOOP_CHANGE} />,
|
||||
'Account-'
|
||||
<AccountOverview onStatusChange={NOOP_CHANGE} />
|
||||
);
|
||||
}
|
||||
|
||||
/** The rows of every account group table, in the order shown */
|
||||
async getAccountRows (): Promise<AccountRow[]> {
|
||||
const table = await this.getTable();
|
||||
const rows = await table.getRows();
|
||||
const rows: AccountRow[] = [];
|
||||
|
||||
return rows.map((row) => new AccountRow(row.primaryRow, row.detailsRow));
|
||||
for (const table of await this.getTables()) {
|
||||
for (const row of await table.getRows()) {
|
||||
rows.push(new AccountRow(row.primaryRow, row.balanceRow, row.detailsRow));
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
async reverseSortingOrder (): Promise<void> {
|
||||
|
||||
@@ -19,9 +19,7 @@ await mockAddressesPageHooks();
|
||||
|
||||
const { AddressesPage } = await import('../../test/pages/addressesPage.js');
|
||||
|
||||
// FIXME isSplit Table
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
describe.skip('Addresses page', () => {
|
||||
describe('Addresses page', () => {
|
||||
let addressesPage: InstanceType<typeof AddressesPage>;
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -133,7 +131,7 @@ describe.skip('Addresses page', () => {
|
||||
|
||||
assertHasClass(row.detailsRow, 'isCollapsed');
|
||||
|
||||
await row.expand();
|
||||
row.expand();
|
||||
|
||||
assertHasClass(row.detailsRow, 'isExpanded');
|
||||
});
|
||||
|
||||
@@ -16,8 +16,7 @@ const NOOP_CHANGE = () => undefined;
|
||||
export class AddressesPage extends Page {
|
||||
constructor () {
|
||||
super(
|
||||
<AddressOverview onStatusChange={NOOP_CHANGE} />,
|
||||
'Address-'
|
||||
<AddressOverview onStatusChange={NOOP_CHANGE} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { fireEvent, screen, waitFor, within } from '@testing-library/react';
|
||||
|
||||
import i18next from '@pezkuwi/react-components/i18n';
|
||||
import { anAccount, anAccountWithInfo, anAccountWithMeta } from '@pezkuwi/test-support/creation/account';
|
||||
import { alice, bob, MemoryStore } from '@pezkuwi/test-support/keyring';
|
||||
import { alice, bob, charlie, ferdie, MemoryStore } from '@pezkuwi/test-support/keyring';
|
||||
import { assertHasClass, assertTextContent, mockApiHooks } from '@pezkuwi/test-support/utils';
|
||||
import { keyring } from '@pezkuwi/ui-keyring';
|
||||
|
||||
@@ -19,12 +19,10 @@ import { mockAccountsPageHooks } from '../../../page-accounts/test/mockAccountsP
|
||||
|
||||
await mockAccountsPageHooks();
|
||||
|
||||
const { charlieShortAddress, ferdieShortAddress, mockRegistration, registrars } = await import('@pezkuwi/test-support/mockData');
|
||||
const { mockRegistration, registrars } = await import('@pezkuwi/test-support/mockData');
|
||||
const { AccountsPage } = await import('../../../page-accounts/test/pages/accountsPage.js');
|
||||
|
||||
// FIXME: these all need to be wrapped in waitFor ....
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
describe.skip('Sidebar', () => {
|
||||
describe('Sidebar', () => {
|
||||
let accountsPage: InstanceType<typeof AccountsPage>;
|
||||
let sideBar: Sidebar;
|
||||
|
||||
@@ -45,7 +43,6 @@ describe.skip('Sidebar', () => {
|
||||
const initialName = 'INITIAL_NAME';
|
||||
const newName = 'NEW_NAME';
|
||||
const defaultTag = 'Default';
|
||||
const nameInputNotFoundError = 'Unable to find an element by: [data-testid="name-input"]';
|
||||
|
||||
describe('changes name', () => {
|
||||
beforeEach(async () => {
|
||||
@@ -83,8 +80,7 @@ describe.skip('Sidebar', () => {
|
||||
sideBar = await accountsPage.openSidebarForRow(0);
|
||||
await sideBar.clickByText('none');
|
||||
expect(sideBar.queryByRole('combobox')).toBeFalsy();
|
||||
|
||||
await expect(sideBar.typeAccountName(newName)).rejects.toThrow(nameInputNotFoundError);
|
||||
expect(sideBar.queryByTestId('name-input')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('when isEditable is false account name is not editable', async () => {
|
||||
@@ -94,7 +90,7 @@ describe.skip('Sidebar', () => {
|
||||
sideBar = await accountsPage.openSidebarForRow(0);
|
||||
sideBar.edit();
|
||||
|
||||
await expect(sideBar.typeAccountName(newName)).rejects.toThrow(nameInputNotFoundError);
|
||||
expect(sideBar.queryByTestId('name-input')).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('on edit cancel', () => {
|
||||
@@ -137,7 +133,7 @@ describe.skip('Sidebar', () => {
|
||||
await sideBar.typeAccountName(newName);
|
||||
await sideBar.selectTag(defaultTag);
|
||||
|
||||
fireEvent.click(await screen.findByText('accounts'));
|
||||
fireEvent.click(await screen.findByTestId('sort-by-section'));
|
||||
|
||||
await sideBar.assertTags('none');
|
||||
await sideBar.assertAccountName('ALICE');
|
||||
@@ -236,13 +232,13 @@ describe.skip('Sidebar', () => {
|
||||
it('singular registrar', async () => {
|
||||
const judgementTag = await sideBar.getJudgement('1 Known good');
|
||||
|
||||
await judgementTag.assertRegistrars([charlieShortAddress]);
|
||||
await judgementTag.assertRegistrars([charlie]);
|
||||
});
|
||||
|
||||
it('multiple registrars', async () => {
|
||||
const judgementTag = await sideBar.getJudgement('2 Reasonable');
|
||||
|
||||
await judgementTag.assertRegistrars(['BOB', ferdieShortAddress]);
|
||||
await judgementTag.assertRegistrars(['BOB', ferdie]);
|
||||
});
|
||||
|
||||
it('opens clicked registrar in sidebar and closes popup', async () => {
|
||||
|
||||
@@ -41,7 +41,3 @@ export const charlieRegistrar: Registrar = { address: charlie, index: 1 };
|
||||
export const ferdieRegistrar: Registrar = { address: ferdie, index: 3 };
|
||||
|
||||
export const registrars: Registrar[] = [bobRegistrar, charlieRegistrar, ferdieRegistrar];
|
||||
|
||||
export const bobShortAddress = '5FHneW…M694ty';
|
||||
export const charlieShortAddress = '5DAAnr…3PTXFy';
|
||||
export const ferdieShortAddress = '5CiPPs…SK2DjL';
|
||||
@@ -16,6 +16,7 @@ import { ThemeProvider } from 'styled-components';
|
||||
import { PEZKUWI_GENESIS } from '@pezkuwi/apps-config';
|
||||
import { AccountSidebar, lightTheme } from '@pezkuwi/react-components';
|
||||
import { ApiCtx } from '@pezkuwi/react-hooks/ctx/Api';
|
||||
import { KeyringCtxRoot } from '@pezkuwi/react-hooks/ctx/Keyring';
|
||||
import { QueueCtx } from '@pezkuwi/react-hooks/ctx/Queue';
|
||||
import { TypeRegistry } from '@pezkuwi/types/create';
|
||||
import { keyring } from '@pezkuwi/ui-keyring';
|
||||
@@ -34,9 +35,8 @@ export abstract class Page {
|
||||
private renderResult?: RenderResult;
|
||||
protected readonly defaultAddresses = [alice, bob, charlie, ferdie];
|
||||
|
||||
protected constructor (private readonly overview: React.ReactElement, private readonly rowClassName: string) {
|
||||
protected constructor (private readonly overview: React.ReactElement) {
|
||||
this.overview = overview;
|
||||
this.rowClassName = rowClassName;
|
||||
}
|
||||
|
||||
render (accounts: [string, AccountOverrides][]): void {
|
||||
@@ -59,6 +59,9 @@ export abstract class Page {
|
||||
proxy: {
|
||||
proxyDepositBase: new BN(1),
|
||||
proxyDepositFactor: new BN(1)
|
||||
},
|
||||
system: {
|
||||
ss58Prefix: new BN(42)
|
||||
}
|
||||
},
|
||||
createType: () => ({
|
||||
@@ -141,9 +144,11 @@ export abstract class Page {
|
||||
<MemoryRouter>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApiCtx.Provider value={mockApi}>
|
||||
<KeyringCtxRoot>
|
||||
<AccountSidebar>
|
||||
{React.cloneElement(this.overview, { onStatusChange: noop }) }
|
||||
</AccountSidebar>
|
||||
</KeyringCtxRoot>
|
||||
</ApiCtx.Provider>
|
||||
</ThemeProvider>
|
||||
</MemoryRouter>
|
||||
@@ -156,7 +161,14 @@ export abstract class Page {
|
||||
async getTable (): Promise<Table> {
|
||||
this.assertRendered();
|
||||
|
||||
return new Table(await screen.findByRole('table'), this.rowClassName);
|
||||
return new Table(await screen.findByRole('table'));
|
||||
}
|
||||
|
||||
/** All tables, in document order, for a page that splits its items over several */
|
||||
async getTables (): Promise<Table[]> {
|
||||
this.assertRendered();
|
||||
|
||||
return (await screen.findAllByRole('table')).map((table) => new Table(table));
|
||||
}
|
||||
|
||||
clearAccounts (): void {
|
||||
|
||||
@@ -14,10 +14,12 @@ import { Sidebar } from './Sidebar.js';
|
||||
// utility wrapper over an account item in accounts table, serves basic assertions about an account row
|
||||
export class Row {
|
||||
public primaryRow: HTMLElement;
|
||||
public balanceRow: HTMLElement;
|
||||
public detailsRow: HTMLElement;
|
||||
|
||||
constructor (primaryRow: HTMLElement, detailsRow: HTMLElement) {
|
||||
constructor (primaryRow: HTMLElement, balanceRow: HTMLElement, detailsRow: HTMLElement) {
|
||||
this.primaryRow = primaryRow;
|
||||
this.balanceRow = balanceRow;
|
||||
this.detailsRow = detailsRow;
|
||||
}
|
||||
|
||||
@@ -29,7 +31,7 @@ export class Row {
|
||||
}
|
||||
|
||||
async getBalanceSummary (): Promise<HTMLElement> {
|
||||
return within(this.primaryRow).findByTestId('balance-summary');
|
||||
return within(this.balanceRow).findByTestId('balance-summary');
|
||||
}
|
||||
|
||||
async assertAccountName (expectedName: string): Promise<void> {
|
||||
@@ -64,8 +66,13 @@ export class Row {
|
||||
assertTextContent(actualShortAddress, expectedShortAddress);
|
||||
}
|
||||
|
||||
async expand (): Promise<void> {
|
||||
const toggle = await within(this.primaryRow).findByTestId('row-toggle');
|
||||
expand (): void {
|
||||
// the toggle is the row's expand column (Table.Column.Expand)
|
||||
const toggle = this.primaryRow.querySelector('td.ui--Table-Column-Expand');
|
||||
|
||||
if (!toggle) {
|
||||
throw new Error('Expected an expand toggle in the row');
|
||||
}
|
||||
|
||||
fireEvent.click(toggle);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright 2017-2026 @pezkuwi/test-supports authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/* global expect */
|
||||
|
||||
import { fireEvent, screen, within } from '@testing-library/react';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import { assertTextContent } from '../utils/domAssertions.js';
|
||||
import { JudgementTag } from './JudgementTag.js';
|
||||
@@ -42,7 +41,7 @@ export class Sidebar {
|
||||
async assertAccountInput (expectedInput: string): Promise<void> {
|
||||
const nameInput = await this.findByTestId('name-input');
|
||||
|
||||
expect(nameInput).toHaveProperty('value', expectedInput);
|
||||
assert.equal((nameInput as HTMLInputElement).value, expectedInput);
|
||||
}
|
||||
|
||||
async assertAccountName (expectedAccountName: string): Promise<void> {
|
||||
|
||||
@@ -10,9 +10,8 @@ import { assertTextContent } from '../utils/domAssertions.js';
|
||||
import { Row } from './Row.js';
|
||||
|
||||
export class Table {
|
||||
constructor (private readonly table: HTMLElement, private readonly rowClassName: string) {
|
||||
constructor (private readonly table: HTMLElement) {
|
||||
this.table = table;
|
||||
this.rowClassName = rowClassName;
|
||||
}
|
||||
|
||||
async assertRowsOrder (balancesExpectedOrder: number[]): Promise<void> {
|
||||
@@ -26,18 +25,28 @@ export class Table {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An item (account, contact) spans the rows from one marked isFirst up to
|
||||
* the next: the main row, the total balance, and the expandable details
|
||||
*/
|
||||
async getRows (): Promise<Row[]> {
|
||||
const htmlRows = await this.getFilteredHtmlRows();
|
||||
const collapsibleRows: Row[] = [];
|
||||
const items: HTMLElement[][] = [];
|
||||
|
||||
for (let rowIdx = 0; rowIdx < htmlRows.length; rowIdx = rowIdx + 2) {
|
||||
const primaryRow = htmlRows[rowIdx];
|
||||
const detailsRow = htmlRows[rowIdx + 1];
|
||||
|
||||
collapsibleRows.push(new Row(primaryRow, detailsRow));
|
||||
for (const htmlRow of await this.getBodyRows()) {
|
||||
if (htmlRow.classList.contains('isFirst')) {
|
||||
items.push([htmlRow]);
|
||||
} else if (items.length) {
|
||||
items[items.length - 1].push(htmlRow);
|
||||
}
|
||||
}
|
||||
|
||||
return collapsibleRows;
|
||||
return items.map((rows) => {
|
||||
if (rows.length !== 3) {
|
||||
throw new Error(`Expected an item to span 3 rows, found ${rows.length}`);
|
||||
}
|
||||
|
||||
return new Row(rows[0], rows[1], rows[2]);
|
||||
});
|
||||
}
|
||||
|
||||
assertColumnNotExist (columnName: string): void {
|
||||
@@ -52,15 +61,16 @@ export class Table {
|
||||
return within(this.table).findByText(text);
|
||||
}
|
||||
|
||||
private async getFilteredHtmlRows (): Promise<HTMLElement[]> {
|
||||
const htmlRows = await this.getAllHtmlRows();
|
||||
|
||||
return htmlRows.filter((row) => row.className.startsWith(this.rowClassName));
|
||||
}
|
||||
|
||||
private async getAllHtmlRows (): Promise<HTMLElement[]> {
|
||||
private async getBodyRows (): Promise<HTMLElement[]> {
|
||||
const tableBody = this.table.getElementsByTagName('tbody')[0];
|
||||
|
||||
return within(tableBody).findAllByRole('row');
|
||||
if (!tableBody) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// a collapsed details row is hidden, and still part of its item; rows of
|
||||
// tables nested in a cell (e.g. a badge popup) are not items
|
||||
return (await within(tableBody).findAllByRole('row', { hidden: true }))
|
||||
.filter((row) => row.parentElement === tableBody);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,16 @@ export function assertHasClass (node: Node | null, className: string): void {
|
||||
assert.ok(classList.contains(className), `Expected class "${className}" on ${describeElement(node)}`);
|
||||
}
|
||||
|
||||
/** As not.toHaveClass: the element is there, without the class */
|
||||
export function assertNotHasClass (node: Node | null, className: string): void {
|
||||
assert.ok(node, `Expected an element without class "${className}", found none`);
|
||||
|
||||
const classList = (node as Partial<Element>).classList;
|
||||
|
||||
assert.ok(classList, `Expected an element without class "${className}", found ${describeElement(node)}`);
|
||||
assert.ok(!classList.contains(className), `Expected no class "${className}" on ${describeElement(node)}`);
|
||||
}
|
||||
|
||||
/** As toHaveTextContent: the whitespace-normalized text contains the string, or matches the pattern */
|
||||
export function assertTextContent (element: Node | null, expected: string | RegExp): void {
|
||||
assert.ok(element, `Expected an element with text ${String(expected)}, found none`);
|
||||
|
||||
Reference in new issue
Block a user