Skip to content

Commit 2686c09

Browse files
authored
chore(trace): split settings into ui and per-model (#37974)
1 parent 14c8908 commit 2686c09

17 files changed

+214
-125
lines changed

packages/trace-viewer/src/ui/actionList.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
limitations under the License.
1515
*/
1616

17-
import type { ActionTraceEvent, AfterActionTraceEventAttachment } from '@trace/trace';
17+
import type { ActionTraceEvent } from '@trace/trace';
1818
import { clsx, msToString } from '@web/uiUtils';
1919
import * as React from 'react';
2020
import './actionList.css';
@@ -34,11 +34,13 @@ export interface ActionListProps {
3434
selectedAction: ActionTraceEventInContext | undefined,
3535
selectedTime: Boundaries | undefined,
3636
setSelectedTime: (time: Boundaries | undefined) => void,
37+
treeState: TreeState,
38+
setTreeState: (treeState: TreeState) => void,
3739
sdkLanguage: Language | undefined;
3840
onSelected?: (action: ActionTraceEventInContext) => void,
3941
onHighlighted?: (action: ActionTraceEventInContext | undefined) => void,
4042
revealConsole?: () => void,
41-
revealAttachment(attachment: AfterActionTraceEventAttachment): void,
43+
revealActionAttachment?(callId: string): void,
4244
isLive?: boolean,
4345
}
4446

@@ -49,14 +51,15 @@ export const ActionList: React.FC<ActionListProps> = ({
4951
selectedAction,
5052
selectedTime,
5153
setSelectedTime,
54+
treeState,
55+
setTreeState,
5256
sdkLanguage,
5357
onSelected,
5458
onHighlighted,
5559
revealConsole,
56-
revealAttachment,
60+
revealActionAttachment,
5761
isLive,
5862
}) => {
59-
const [treeState, setTreeState] = React.useState<TreeState>({ expandedItems: new Map() });
6063
const { rootItem, itemMap } = React.useMemo(() => modelUtil.buildActionTree(actions), [actions]);
6164

6265
const { selectedItem } = React.useMemo(() => {
@@ -73,8 +76,9 @@ export const ActionList: React.FC<ActionListProps> = ({
7376
}, [setSelectedTime]);
7477

7578
const render = React.useCallback((item: ActionTreeItem) => {
76-
return renderAction(item.action!, { sdkLanguage, revealConsole, revealAttachment, isLive, showDuration: true, showBadges: true });
77-
}, [isLive, revealConsole, revealAttachment, sdkLanguage]);
79+
const showAttachments = !!revealActionAttachment && !!item.action?.attachments?.length;
80+
return renderAction(item.action!, { sdkLanguage, revealConsole, revealActionAttachment: () => revealActionAttachment?.(item.action!.callId), isLive, showDuration: true, showBadges: true, showAttachments });
81+
}, [isLive, revealConsole, revealActionAttachment, sdkLanguage]);
7882

7983
const isVisible = React.useCallback((item: ActionTreeItem) => {
8084
return !selectedTime || !item.action || (item.action!.startTime <= selectedTime.maximum && item.action!.endTime >= selectedTime.minimum);
@@ -111,14 +115,14 @@ export const renderAction = (
111115
options: {
112116
sdkLanguage?: Language,
113117
revealConsole?: () => void,
114-
revealAttachment?(attachment: AfterActionTraceEventAttachment): void,
118+
revealActionAttachment?(): void,
115119
isLive?: boolean,
116120
showDuration?: boolean,
117121
showBadges?: boolean,
122+
showAttachments?: boolean,
118123
}) => {
119-
const { sdkLanguage, revealConsole, revealAttachment, isLive, showDuration, showBadges } = options;
124+
const { sdkLanguage, revealConsole, revealActionAttachment, isLive, showDuration, showBadges, showAttachments } = options;
120125
const { errors, warnings } = modelUtil.stats(action);
121-
const showAttachments = !!action.attachments?.length && !!revealAttachment;
122126

123127
const locator = action.params.selector ? asLocatorDescription(sdkLanguage || 'javascript', action.params.selector) : undefined;
124128

@@ -135,7 +139,7 @@ export const renderAction = (
135139
<div className='hbox'>
136140
<span className='action-title-method' title={title}>{elements}</span>
137141
{(showDuration || showBadges || showAttachments || isSkipped) && <div className='spacer'></div>}
138-
{showAttachments && <ToolbarButton icon='attach' title='Open Attachment' onClick={() => revealAttachment(action.attachments![0])} />}
142+
{showAttachments && <ToolbarButton icon='attach' title='Open Attachment' onClick={() => revealActionAttachment?.()} />}
139143
{showDuration && !isSkipped && <div className='action-duration'>{time || <span className='codicon codicon-loading'></span>}</div>}
140144
{isSkipped && <span className={clsx('action-skipped', 'codicon', testStatusIcon('skipped'))} title='skipped'></span>}
141145
{showBadges && <div className='action-icons' onClick={() => revealConsole?.()}>

packages/trace-viewer/src/ui/attachmentsTab.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,17 @@ import { isTextualMimeType } from '@isomorphic/mimeType';
2323
import { Expandable } from '@web/components/expandable';
2424
import { linkifyText } from '@web/renderUtils';
2525
import { clsx, useFlash } from '@web/uiUtils';
26-
import { TraceModelContext } from './traceModelContext';
26+
import { useTraceModel } from './traceModelContext';
2727

2828
import type { Attachment, MultiTraceModel } from './modelUtil';
29-
import type { AfterActionTraceEventAttachment } from '@trace/trace';
3029

3130
type ExpandableAttachmentProps = {
3231
attachment: Attachment;
33-
reveal?: any;
32+
reveal: any;
3433
};
3534

3635
const ExpandableAttachment: React.FunctionComponent<ExpandableAttachmentProps> = ({ attachment, reveal }) => {
37-
const model = React.useContext(TraceModelContext);
36+
const model = useTraceModel();
3837
const [expanded, setExpanded] = React.useState(false);
3938
const [attachmentText, setAttachmentText] = React.useState<string | null>(null);
4039
const [placeholder, setPlaceholder] = React.useState<string | null>(null);
@@ -94,9 +93,9 @@ const ExpandableAttachment: React.FunctionComponent<ExpandableAttachmentProps> =
9493
};
9594

9695
export const AttachmentsTab: React.FunctionComponent<{
97-
revealedAttachment?: [AfterActionTraceEventAttachment, number],
98-
}> = ({ revealedAttachment }) => {
99-
const model = React.useContext(TraceModelContext);
96+
revealedAttachmentCallId?: { callId: string },
97+
}> = ({ revealedAttachmentCallId }) => {
98+
const model = useTraceModel();
10099
const { diffMap, screenshots, attachments } = React.useMemo(() => {
101100
const attachments = new Set(model?.visibleAttachments ?? []);
102101
const screenshots = new Set<Attachment>();
@@ -149,17 +148,13 @@ export const AttachmentsTab: React.FunctionComponent<{
149148
return <div className='attachment-item' key={attachmentKey(a, i)}>
150149
<ExpandableAttachment
151150
attachment={a}
152-
reveal={(!!revealedAttachment && isEqualAttachment(a, revealedAttachment[0])) ? revealedAttachment : undefined}
151+
reveal={!!revealedAttachmentCallId && a.callId === revealedAttachmentCallId.callId ? revealedAttachmentCallId : undefined}
153152
/>
154153
</div>;
155154
})}
156155
</div>;
157156
};
158157

159-
function isEqualAttachment(a: Attachment, b: AfterActionTraceEventAttachment): boolean {
160-
return a.name === b.name && a.path === b.path && a.sha1 === b.sha1;
161-
}
162-
163158
export function attachmentURL(model: MultiTraceModel | undefined, attachment: Attachment) {
164159
if (model && attachment.sha1)
165160
return model.createRelativeUrl(`sha1/${attachment.sha1}`) ;

packages/trace-viewer/src/ui/consoleTab.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export const ConsoleTab: React.FunctionComponent<{
130130
boundaries: Boundaries,
131131
consoleModel: ConsoleTabModel,
132132
selectedTime?: Boundaries | undefined,
133-
onEntryHovered?: (entry: ConsoleEntry | undefined) => void,
133+
onEntryHovered?: (ordinal: number | undefined) => void,
134134
onAccepted?: (entry: ConsoleEntry) => void,
135135
}> = ({ consoleModel, boundaries, onEntryHovered, onAccepted }) => {
136136
if (!consoleModel.entries.length)
@@ -140,7 +140,7 @@ export const ConsoleTab: React.FunctionComponent<{
140140
<ConsoleListView
141141
name='console'
142142
onAccepted={onAccepted}
143-
onHighlighted={onEntryHovered}
143+
onHighlighted={entry => onEntryHovered?.(entry ? consoleModel.entries.indexOf(entry) : undefined)}
144144
items={consoleModel.entries}
145145
isError={entry => entry.isError}
146146
isWarning={entry => entry.isWarning}

packages/trace-viewer/src/ui/errorsTab.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { copyPrompt, stripAnsiEscapes } from '@web/shared/prompts';
2727
import { MetadataWithCommitInfo } from '@testIsomorphic/types';
2828
import { calculateSha1 } from './sourceTab';
2929
import type { StackFrame } from '@protocol/channels';
30-
import { TraceModelContext } from './traceModelContext';
30+
import { useTraceModel } from './traceModelContext';
3131

3232
const CopyPromptButton: React.FC<{ prompt: string }> = ({ prompt }) => {
3333
return (
@@ -91,7 +91,7 @@ export const ErrorsTab: React.FunctionComponent<{
9191
revealInSource: (error: modelUtil.ErrorDescription) => void,
9292
testRunMetadata: MetadataWithCommitInfo | undefined,
9393
}> = ({ errorsModel, sdkLanguage, revealInSource, wallTime, testRunMetadata }) => {
94-
const model = React.useContext(TraceModelContext);
94+
const model = useTraceModel();
9595
const errorContext = useAsyncMemo(async () => {
9696
const attachment = model?.attachments.find(a => a.name === 'error-context');
9797
if (!attachment)

packages/trace-viewer/src/ui/filmStrip.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import type { PageEntry } from '../types/entries';
2222
import type { ActionTraceEventInContext } from './modelUtil';
2323
import { renderAction } from './actionList';
2424
import type { Language } from '@isomorphic/locatorGenerators';
25-
import { TraceModelContext } from './traceModelContext';
25+
import { useTraceModel } from './traceModelContext';
2626

2727
export type FilmStripPreviewPoint = {
2828
x: number;
@@ -39,7 +39,7 @@ export const FilmStrip: React.FunctionComponent<{
3939
boundaries: Boundaries,
4040
previewPoint?: FilmStripPreviewPoint,
4141
}> = ({ boundaries, previewPoint }) => {
42-
const model = React.useContext(TraceModelContext);
42+
const model = useTraceModel();
4343
const [measure, ref] = useMeasure<HTMLDivElement>();
4444
const lanesRef = React.useRef<HTMLDivElement>(null);
4545

@@ -90,7 +90,7 @@ const FilmStripLane: React.FunctionComponent<{
9090
page: PageEntry,
9191
width: number,
9292
}> = ({ boundaries, page, width }) => {
93-
const model = React.useContext(TraceModelContext);
93+
const model = useTraceModel();
9494
const viewportSize = { width: 0, height: 0 };
9595
const screencastFrames = page.screencastFrames;
9696
for (const frame of screencastFrames) {

packages/trace-viewer/src/ui/liveWorkbenchLoader.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import './workbenchLoader.css';
2020
import { Workbench } from './workbench';
2121

2222
import type { ContextEntry } from '../types/entries';
23-
import { TraceModelContext } from './traceModelContext';
2423

2524
export const LiveWorkbenchLoader: React.FC<{ traceJson: string }> = ({ traceJson }) => {
2625
const [model, setModel] = React.useState<MultiTraceModel | undefined>(undefined);
@@ -49,9 +48,7 @@ export const LiveWorkbenchLoader: React.FC<{ traceJson: string }> = ({ traceJson
4948
};
5049
}, [traceJson, counter]);
5150

52-
return <TraceModelContext.Provider value={model}>
53-
<Workbench isLive={true} />
54-
</TraceModelContext.Provider>;
51+
return <Workbench isLive={true} model={model} />;
5552
};
5653

5754
async function loadSingleTraceFile(traceJson: string): Promise<MultiTraceModel> {

packages/trace-viewer/src/ui/modelUtil.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export type ErrorDescription = {
5959
message: string;
6060
};
6161

62-
export type Attachment = trace.AfterActionTraceEventAttachment;
62+
export type Attachment = trace.AfterActionTraceEventAttachment & { callId: string };
6363

6464
export class MultiTraceModel {
6565
readonly startTime: number;
@@ -112,7 +112,7 @@ export class MultiTraceModel {
112112
this.hasSource = contexts.some(c => c.hasSource);
113113
this.hasStepData = contexts.some(context => context.origin === 'testRunner');
114114
this.resources = [...contexts.map(c => c.resources)].flat();
115-
this.attachments = this.actions.flatMap(action => action.attachments?.map(attachment => ({ ...attachment, traceUrl })) ?? []);
115+
this.attachments = this.actions.flatMap(action => action.attachments?.map(attachment => ({ ...attachment, callId: action.callId, traceUrl })) ?? []);
116116
this.visibleAttachments = this.attachments.filter(attachment => !attachment.name.startsWith('_'));
117117

118118
this.events.sort((a1, a2) => a1.time - a2.time);

packages/trace-viewer/src/ui/networkResourceDetails.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { getAPIRequestCodeGen } from './codegen';
2626
import type { Language } from '@isomorphic/locatorGenerators';
2727
import { msToString, useAsyncMemo } from '@web/uiUtils';
2828
import type { Entry } from '@trace/har';
29-
import { TraceModelContext } from './traceModelContext';
29+
import { useTraceModel } from './traceModelContext';
3030

3131
type RequestBody = { text: string, mimeType?: string } | null;
3232

@@ -38,7 +38,7 @@ export const NetworkResourceDetails: React.FunctionComponent<{
3838
onClose: () => void;
3939
}> = ({ resource, sdkLanguage, startTimeOffset, onClose }) => {
4040
const [selectedTab, setSelectedTab] = React.useState('request');
41-
const model = React.useContext(TraceModelContext);
41+
const model = useTraceModel();
4242

4343
const requestBody = useAsyncMemo<RequestBody>(async () => {
4444
if (model && resource.request.postData) {
@@ -147,7 +147,7 @@ const ResponseTab: React.FunctionComponent<{
147147
const BodyTab: React.FunctionComponent<{
148148
resource: ResourceSnapshot;
149149
}> = ({ resource }) => {
150-
const model = React.useContext(TraceModelContext);
150+
const model = useTraceModel();
151151
const [responseBody, setResponseBody] = React.useState<{ dataUrl?: string, text?: string, mimeType?: string, font?: BufferSource } | null>(null);
152152

153153
React.useEffect(() => {

packages/trace-viewer/src/ui/networkTab.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type NetworkTabModel = {
3434
};
3535

3636
type RenderedEntry = {
37+
ordinal: number,
3738
name: { name: string, url: string },
3839
method: string,
3940
status: { code: number, text: string },
@@ -66,15 +67,15 @@ export function useNetworkTabModel(model: MultiTraceModel | undefined, selectedT
6667
export const NetworkTab: React.FunctionComponent<{
6768
boundaries: Boundaries,
6869
networkModel: NetworkTabModel,
69-
onEntryHovered?: (entry: Entry | undefined) => void,
70+
onResourceHovered?: (ordinal: number | undefined) => void,
7071
sdkLanguage: Language,
71-
}> = ({ boundaries, networkModel, onEntryHovered, sdkLanguage }) => {
72+
}> = ({ boundaries, networkModel, onResourceHovered, sdkLanguage }) => {
7273
const [sorting, setSorting] = React.useState<Sorting | undefined>(undefined);
7374
const [selectedEntry, setSelectedEntry] = React.useState<RenderedEntry | undefined>(undefined);
7475
const [filterState, setFilterState] = React.useState(defaultFilterState);
7576

7677
const { renderedEntries } = React.useMemo(() => {
77-
const renderedEntries = networkModel.resources.map(entry => renderEntry(entry, boundaries, networkModel.contextIdMap)).filter(filterEntry(filterState));
78+
const renderedEntries = networkModel.resources.map((entry, i) => renderEntry(entry, boundaries, networkModel.contextIdMap, i)).filter(filterEntry(filterState));
7879
if (sorting)
7980
sort(renderedEntries, sorting);
8081
return { renderedEntries };
@@ -98,7 +99,7 @@ export const NetworkTab: React.FunctionComponent<{
9899
items={renderedEntries}
99100
selectedItem={selectedEntry}
100101
onSelected={item => setSelectedEntry(item)}
101-
onHighlighted={item => onEntryHovered?.(item?.resource)}
102+
onHighlighted={item => onResourceHovered?.(item?.ordinal)}
102103
columns={visibleColumns(!!selectedEntry, renderedEntries)}
103104
columnTitle={columnTitle}
104105
columnWidths={columnWidths}
@@ -261,7 +262,7 @@ function hasMultipleContexts(renderedEntries: RenderedEntry[]): boolean {
261262
return false;
262263
}
263264

264-
const renderEntry = (resource: Entry, boundaries: Boundaries, contextIdGenerator: ContextIdMap): RenderedEntry => {
265+
const renderEntry = (resource: Entry, boundaries: Boundaries, contextIdGenerator: ContextIdMap, ordinal: number): RenderedEntry => {
265266
const routeStatus = formatRouteStatus(resource);
266267
let resourceName: string;
267268
try {
@@ -280,6 +281,7 @@ const renderEntry = (resource: Entry, boundaries: Boundaries, contextIdGenerator
280281
contentType = charset[1];
281282

282283
return {
284+
ordinal,
283285
name: { name: resourceName, url: resource.request.url },
284286
method: resource.request.method,
285287
status: { code: resource.response.status, text: resource.response.statusText },

packages/trace-viewer/src/ui/sourceTab.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ import type { StackFrame } from '@protocol/channels';
2626
import { CopyToClipboard } from './copyToClipboard';
2727
import { ToolbarButton } from '@web/components/toolbarButton';
2828
import { Toolbar } from '@web/components/toolbar';
29-
import { TraceModelContext } from './traceModelContext';
29+
import { useTraceModel } from './traceModelContext';
3030

3131
function useSources(stack: StackFrame[] | undefined, selectedFrame: number, sources: Map<string, SourceModel>, rootDir?: string, fallbackLocation?: SourceLocation) {
32-
const model = React.useContext(TraceModelContext);
32+
const model = useTraceModel();
3333
return useAsyncMemo<{ source: SourceModel, targetLine?: number, fileName?: string, highlight: SourceHighlight[], location?: SourceLocation }>(async () => {
3434
const actionLocation = stack?.[selectedFrame];
3535
const location = actionLocation?.file ? actionLocation : fallbackLocation;

0 commit comments

Comments
 (0)