Skip to content

Commit 55c0b3c

Browse files
committed
test(app-shell): the one-request record open pin names the 动态 panel's own reads
#27713 landed a pin that a record open sends the page request alone. It measured jsdom's unmeasured width, which drew the tabbed tier, where the stream waited for its tab. This branch lays every width out with the 动态 panel first in the right column, so the stream is on screen at open and reads its sources beside the page request, in the same wave: the record's comment thread, its activity and, once per view, the mention list. On main the same reads already went out at open wherever a column tier drew the stream. The pin keeps every page-part assertion as it was and pins the stream's reads on their own: which they are, that they share the page's wave, and that an in-place step reads only the next record's two. Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt Co-authored-by: Claude <noreply@anthropic.com>
1 parent 92b6992 commit 55c0b3c

1 file changed

Lines changed: 28 additions & 2 deletions

File tree

‎ui/packages/app-shell/src/views/RecordDetailView.recordPagePayload-27627.test.tsx‎

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
* approval / lock / record read, one wave of data requests; the badges draw the
2222
* payload's totals; and the record a drawer step shows next is asked ahead, so stepping to it
2323
* asks nothing for the record it opens.
24+
*
25+
* objectstack#27524: the 动态 panel leads the page's right column in every
26+
* tier, so the stream is on screen at open and reads its own sources beside
27+
* the page request, in the same wave: the record's comment thread, its
28+
* activity and, once per view, the mention list. None of them is a part of the page the payload answers;
29+
* they are pinned on their own below, not counted as page requests.
2430
*/
2531
import * as React from 'react';
2632
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
@@ -154,7 +160,17 @@ async function runWaves(held: Held[], state: { wave: number }) {
154160

155161
const isMeta = (h: Held) => h.path.startsWith('/api/v1/meta/');
156162
const isPage = (h: Held) => h.method === 'POST' && /\/api\/v1\/data\/[^/?]+\/[^/?]+\/page$/.test(h.path);
157-
const dataRequests = (held: Held[]) => held.filter((h) => !isMeta(h));
163+
/** The 动态 stream's own reads (objectstack#27524): its sources, not the page's parts. */
164+
const STREAM_SOURCES = ['sys_comment', 'sys_activity', 'sys_user'];
165+
const isStreamRead = (h: Held) => h.method === 'GET' && STREAM_SOURCES.some((o) => h.path.startsWith(`/api/v1/data/${o}?`));
166+
/** A stream read as `<object> <record>`, or `<object>` for the mention list, which names no record. */
167+
const streamRead = (h: Held) => {
168+
const object = /\/api\/v1\/data\/([^/?]+)\?/.exec(h.path)![1];
169+
const rec = new RegExp(`${OBJECT}:([^"]+)"|"record_id","=","([^"]+)"`).exec(h.path);
170+
return rec ? `${object} ${rec[1] ?? rec[2]}` : object;
171+
};
172+
const streamReads = (held: Held[]) => held.filter(isStreamRead).map(streamRead).sort();
173+
const dataRequests = (held: Held[]) => held.filter((h) => !isMeta(h) && !isStreamRead(h));
158174

159175
beforeEach(() => {
160176
cleanup();
@@ -222,6 +238,10 @@ describe('objectstack#27627 — one request opens a record page', () => {
222238

223239
// Every badge's total is the payload's.
224240
for (const c of CHILDREN) expect(RelatedCountStore.get(c, 'contact', 'c1')).toBe(TOTAL[c]);
241+
242+
// The 动态 panel's own reads go out beside the page request, in its wave.
243+
expect(streamReads(held)).toEqual(['sys_activity c1', 'sys_comment c1', 'sys_user']);
244+
expect(held.filter(isStreamRead).every((h) => h.wave === 1)).toBe(true);
225245
});
226246

227247
it('a warm open of another record sends one page request too', async () => {
@@ -233,7 +253,9 @@ describe('objectstack#27627 — one request opens a record page', () => {
233253
state.wave = 1;
234254
open('c2');
235255
await runWaves(held, state);
236-
expect(held.map((h) => `${h.method} ${h.path}`)).toEqual([`POST /api/v1/data/${OBJECT}/c2/page`]);
256+
expect(dataRequests(held).map((h) => `${h.method} ${h.path}`)).toEqual([`POST /api/v1/data/${OBJECT}/c2/page`]);
257+
expect(held.filter(isMeta)).toEqual([]);
258+
expect(streamReads(held)).toEqual(['sys_activity c2', 'sys_comment c2', 'sys_user']);
237259
});
238260

239261
it("asks the record a drawer step shows next ahead, so stepping to it sends nothing", async () => {
@@ -254,6 +276,8 @@ describe('objectstack#27627 — one request opens a record page', () => {
254276
// The step's own page is the prefetched answer; only the next one is asked.
255277
expect(held.filter(isPage).map((h) => h.path)).toEqual([`/api/v1/data/${OBJECT}/c3/page`]);
256278
expect(dataRequests(held).filter((h) => !isPage(h))).toEqual([]);
279+
// What the step does read is its 动态 stream, the payload's to answer no part of.
280+
expect(streamReads(held)).toEqual(['sys_activity c2', 'sys_comment c2', 'sys_user']);
257281
});
258282
// Merge resolution with objectstack#27056 (#27636): in a drawer, that card
259283
// warms the next record's related counts; the payload's prefetch carries
@@ -299,6 +323,8 @@ describe('objectstack#27627 — one request opens a record page', () => {
299323
`/api/v1/data/${OBJECT}/c2/page`,
300324
]);
301325
expect(dataRequests(held).filter((h) => !isPage(h))).toEqual([]);
326+
// The swapped-in record's 动态 stream; the mention list is the view's, read once.
327+
expect(streamReads(held)).toEqual(['sys_activity c1', 'sys_comment c1']);
302328
});
303329

304330
it('in a drawer, an adapter without the payload keeps the count warm-up: each of the next record\'s counts is asked once', async () => {

0 commit comments

Comments
 (0)