Skip to content

Commit a38376b

Browse files
committed
test(showcase): the Contoso detail-shapes case reads the one-column stack tier
The tabbed tier this case pinned is retired: below the 760 px view floor the record page is one column, the details then the right column's panels. The case asserts the stack and the absent tab strip, Invoices and Projects as body tables in the details, 动态 as the right column's first panel, and the Contacts panel reading its rows once scrolled into view. Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt Co-authored-by: Claude <noreply@anthropic.com>
1 parent 81683b8 commit a38376b

1 file changed

Lines changed: 38 additions & 35 deletions

File tree

‎examples/app-showcase/e2e/detail-shapes.spec.ts‎

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ import { test, expect } from '@playwright/test';
1313
* - stageField:false → NO `record:path` stepper despite a `status` field
1414
* named to bait the heuristic
1515
* - related-heavy → related lists surface by prominence (ADR-0085), in the
16-
* tabbed tier as record page v3 draws it (objectstack#27055,
17-
* Target 8): a `relatedList: 'primary'` list is a body
18-
* table in the Details tab, the rest sit behind the
19-
* Related tab and self-fetch when it is shown
16+
* one-column stack tier (objectstack#27524): a
17+
* `relatedList: 'primary'` list is a body table in the
18+
* details (objectstack#27055, Target 8), the rest are
19+
* panels of the right column below them and read their
20+
* rows when they come into view
2021
*
2122
* Runs against the console the backend serves at /_console (baseURL from
2223
* playwright.config.ts; storageState carries the admin session). The
@@ -157,56 +158,60 @@ test('stageField:false: status renders as a plain field, no stepper', async ({ p
157158
expect(errors, 'uncaught page errors on stageField:false detail').toEqual([]);
158159
});
159160

160-
test('ungrouped + related-heavy: flat details and related-list tabs on Contoso', async ({ page }) => {
161+
test('ungrouped + related-heavy: flat details and the related panels on Contoso', async ({ page }) => {
161162
// The synthesized record page lays itself out by the width of the view it
162-
// occupies (objectstack#26678): the tab strip this case opens exists only
163-
// below the two-column floor, a record view narrower than
164-
// `RECORD_PAGE_TWO_COLUMN_MIN_WIDTH` (760 px, `@object-ui/plugin-detail`).
165-
// The case stays in that tabbed tier on purpose: the column tiers are what
166-
// every other page of this smoke draws at the Desktop Chrome viewport, and
167-
// the tab strip is also what the drawer and split pane draw at any width.
163+
// occupies, by one rule (objectstack#26678, objectstack#27524): details |
164+
// the right column from `RECORD_PAGE_TWO_COLUMN_MIN_WIDTH` (760 px,
165+
// `@object-ui/plugin-detail`), and ONE column below it — the details, then
166+
// the right column's panels in their order. No tier draws a tab strip. The
167+
// case stays in that stack tier on purpose: the column tiers are what every
168+
// other page of this smoke draws at the Desktop Chrome viewport, and the
169+
// stack is also what the split pane (720 px) and a phone draw.
168170
//
169171
// The window is derived, not picked. Measured on this harness (a fresh
170172
// browser context, so no stored sidebar cookie): from a 768 px window
171173
// (`MOBILE_BREAKPOINT`, the desktop shell) to 1000 px the console sidebar
172174
// came up as its 48 px icon rail, and the record view was the window minus
173-
// that rail — two columns at 808 px (760 of view), tabs at 800 px (752).
174-
// So the desktop tabbed tier is a window of 768 to 807 px, and an expanded
175+
// that rail — two columns at 808 px (760 of view), one at 800 px (752).
176+
// So the desktop stack tier is a window of 768 to 807 px, and an expanded
175177
// sidebar (256 px) only narrows the view further. 780 sits inside it with
176178
// headroom on both sides: 732 px of view against the 760 floor, 12 px above
177179
// the mobile breakpoint. ⛔ Not 1200 px: since objectstack#27473 moved the
178-
// floor to a 760 px view, 1200 draws columns and no tablist. ⛔ Not below
179-
// 768 px: that is the mobile shell, where the off-canvas sidebar gives the
180-
// view the whole window (760 px measured as two columns).
180+
// floor to a 760 px view, 1200 draws two columns. ⛔ Not below 768 px: that
181+
// is the mobile shell, where the off-canvas sidebar gives the view the
182+
// whole window (760 px measured as two columns).
181183
await page.setViewportSize({ width: 780, height: 800 });
182184
const errors = await openRecord(page, recordUrl('showcase_account', contosoId));
183185

184186
// Heuristic stepper (account.status is a select named "status").
185187
// record:path renders a desktop + a mobile variant — assert the visible one.
186188
await expect(page.locator(PATH_STEPPER).first()).toBeVisible();
187189

190+
// One column and no tab strip: the details, then the right column.
191+
await expect(page.locator('[data-record-page-tier]')).toHaveAttribute('data-record-page-tier', 'stack');
192+
await expect(page.locator('[data-record-columns]')).toHaveAttribute('data-record-columns-shown', 'details');
193+
await expect(page.getByRole('tab', { name: /Details|Related|Invoices|Projects/i })).toHaveCount(0);
194+
188195
// Related lists surface by prominence (ADR-0085), as record page v3 draws
189196
// them (objectstack#27055, Target 8): Invoices and Projects are
190197
// `relatedList: 'primary'` (CORE relationships), so each is a body table in
191-
// the Details tab, with its rows read at open; the rest of Contoso's lists
192-
// (Contacts and the Field Zoo lookups) sit behind the Related tab.
193-
const detailsTab = page.getByRole('tab', { name: /Details/i });
194-
const relatedTab = page.getByRole('tab', { name: /^Related/ });
195-
await expect(detailsTab).toBeVisible();
196-
await expect(detailsTab).toHaveAttribute('aria-selected', 'true');
197-
await expect(relatedTab).toBeVisible();
198-
await expect(page.getByRole('tab', { name: /Invoices|Projects/i })).toHaveCount(0);
199-
const detailsPanel = page.getByRole('tabpanel', { name: /Details/i });
200-
const invoicesTable = detailsPanel
198+
// the details, with its rows read at open; the rest of Contoso's lists
199+
// (Contacts and the Field Zoo lookups) are panels of the right column,
200+
// after its first panel, 动态 (objectstack#27524, Target 5).
201+
const details = page.locator('[data-record-column="details"]');
202+
const invoicesTable = details
201203
.getByRole('table')
202204
.filter({ has: page.getByRole('columnheader', { name: 'Invoice Number' }) });
203-
const projectsTable = detailsPanel
205+
const projectsTable = details
204206
.getByRole('table')
205207
.filter({ has: page.getByRole('columnheader', { name: 'Project Name' }) });
206208
await expect(invoicesTable.getByRole('cell', { name: 'INV-1002', exact: true })).toBeVisible();
207209
await expect(projectsTable.getByRole('cell', { name: 'Data Platform', exact: true })).toBeVisible();
210+
const rightColumn = page.locator('[data-record-column="right"]');
211+
await expect(rightColumn.locator(':scope > *').first()).toHaveAttribute('data-record-timeline-panel', '');
212+
await expect(rightColumn.getByRole('region', { name: /^(Invoices|Projects)$/ })).toHaveCount(0);
208213

209-
// Ungrouped shape: the Details body is flat — no fieldGroup section cards.
214+
// Ungrouped shape: the details body is flat — no fieldGroup section cards.
210215
// (Contoso declares no fieldGroups; spot-check a body field label.)
211216
await expect(page.getByText('Basics', { exact: true })).toHaveCount(0);
212217

@@ -220,13 +225,11 @@ test('ungrouped + related-heavy: flat details and related-list tabs on Contoso',
220225
expect(footerText, 'footer dangles "Created by" without an actor').not.toMatch(/Created by/);
221226
expect(footerText).toMatch(/Created/);
222227

223-
// The non-primary lists self-fetch lazily when their tab is shown: the
224-
// Related tab's Contacts list reads Contoso's seeded contacts.
225-
await relatedTab.click();
226-
await expect(relatedTab).toHaveAttribute('aria-selected', 'true');
227-
const relatedPanel = page.getByRole('tabpanel', { name: /^Related/ });
228-
await expect(relatedPanel).toBeVisible();
229-
await expect(relatedPanel.getByRole('cell', { name: 'Carl Fox', exact: true })).toBeVisible();
228+
// A panel reads its rows once it is in view: below the details here, so
229+
// the Contacts panel reads Contoso's seeded contacts when scrolled to.
230+
const contactsPanel = rightColumn.getByRole('region', { name: 'Contacts', exact: true });
231+
await contactsPanel.scrollIntoViewIfNeeded();
232+
await expect(contactsPanel.locator('[data-related-card-row]').filter({ hasText: 'Carl Fox' })).toBeVisible();
230233

231234
expect(errors, 'uncaught page errors on related-heavy detail').toEqual([]);
232235
});

0 commit comments

Comments
 (0)