2021-01-07 16:15:34 -08:00
|
|
|
/*
|
|
|
|
Copyright (c) Microsoft Corporation.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2022-03-25 13:12:00 -08:00
|
|
|
import { SplitView } from '@web/components/splitView';
|
|
|
|
import * as React from 'react';
|
2021-01-07 16:15:34 -08:00
|
|
|
import { ActionList } from './actionList';
|
2021-07-02 14:33:38 -07:00
|
|
|
import { CallTab } from './callTab';
|
2021-07-01 14:31:20 -07:00
|
|
|
import { ConsoleTab } from './consoleTab';
|
2023-07-10 12:56:56 -07:00
|
|
|
import type * as modelUtil from './modelUtil';
|
2023-05-19 15:18:18 -07:00
|
|
|
import type { ActionTraceEventInContext, MultiTraceModel } from './modelUtil';
|
2022-03-25 13:12:00 -08:00
|
|
|
import { NetworkTab } from './networkTab';
|
|
|
|
import { SnapshotTab } from './snapshotTab';
|
|
|
|
import { SourceTab } from './sourceTab';
|
2023-02-17 11:19:53 -08:00
|
|
|
import { TabbedPane } from '@web/components/tabbedPane';
|
2023-03-07 14:24:50 -08:00
|
|
|
import type { TabbedPaneTabModel } from '@web/components/tabbedPane';
|
2022-03-25 13:12:00 -08:00
|
|
|
import { Timeline } from './timeline';
|
2023-03-06 12:25:00 -08:00
|
|
|
import { MetadataView } from './metadataView';
|
2023-05-09 17:53:01 -07:00
|
|
|
import { AttachmentsTab } from './attachmentsTab';
|
2023-08-16 16:30:17 -07:00
|
|
|
import type { Boundaries } from '../geometry';
|
2023-08-18 17:53:03 -07:00
|
|
|
import { InspectorTab } from './inspectorTab';
|
|
|
|
import { ToolbarButton } from '@web/components/toolbarButton';
|
2023-08-21 19:40:44 -07:00
|
|
|
import { useSetting } from '@web/uiUtils';
|
2021-01-07 16:15:34 -08:00
|
|
|
|
2023-02-16 07:59:21 -08:00
|
|
|
export const Workbench: React.FunctionComponent<{
|
2023-03-07 12:43:16 -08:00
|
|
|
model?: MultiTraceModel,
|
2023-03-10 22:52:31 -08:00
|
|
|
hideStackFrames?: boolean,
|
2023-03-11 11:43:33 -08:00
|
|
|
showSourcesFirst?: boolean,
|
2023-03-19 12:04:19 -07:00
|
|
|
rootDir?: string,
|
2023-05-08 18:51:27 -07:00
|
|
|
fallbackLocation?: modelUtil.SourceLocation,
|
2023-05-19 15:18:18 -07:00
|
|
|
initialSelection?: ActionTraceEventInContext,
|
|
|
|
onSelectionChanged?: (action: ActionTraceEventInContext) => void,
|
2023-05-18 15:52:44 -07:00
|
|
|
isLive?: boolean,
|
2023-08-18 17:53:03 -07:00
|
|
|
}> = ({ model, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive }) => {
|
2023-05-19 15:18:18 -07:00
|
|
|
const [selectedAction, setSelectedAction] = React.useState<ActionTraceEventInContext | undefined>(undefined);
|
|
|
|
const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEventInContext | undefined>();
|
2023-02-16 07:59:21 -08:00
|
|
|
const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState<string>('actions');
|
2023-03-11 11:43:33 -08:00
|
|
|
const [selectedPropertiesTab, setSelectedPropertiesTab] = React.useState<string>(showSourcesFirst ? 'source' : 'call');
|
2023-08-18 17:53:03 -07:00
|
|
|
const [isInspecting, setIsInspecting] = React.useState(false);
|
|
|
|
const [highlightedLocator, setHighlightedLocator] = React.useState<string>('');
|
2023-03-07 12:43:16 -08:00
|
|
|
const activeAction = model ? highlightedAction || selectedAction : undefined;
|
2023-08-16 16:30:17 -07:00
|
|
|
const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>();
|
2023-08-21 19:40:44 -07:00
|
|
|
const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom');
|
2021-10-22 07:00:34 -08:00
|
|
|
|
2023-03-19 12:04:19 -07:00
|
|
|
const sources = React.useMemo(() => model?.sources || new Map(), [model]);
|
|
|
|
|
2023-08-20 14:47:18 -07:00
|
|
|
React.useEffect(() => {
|
|
|
|
setSelectedTime(undefined);
|
|
|
|
}, [model]);
|
|
|
|
|
2023-03-10 17:01:30 -08:00
|
|
|
React.useEffect(() => {
|
2023-03-11 11:43:33 -08:00
|
|
|
if (selectedAction && model?.actions.includes(selectedAction))
|
2023-03-10 17:01:30 -08:00
|
|
|
return;
|
|
|
|
const failedAction = model?.actions.find(a => a.error);
|
2023-03-31 18:34:51 -07:00
|
|
|
if (initialSelection && model?.actions.includes(initialSelection))
|
|
|
|
setSelectedAction(initialSelection);
|
|
|
|
else if (failedAction)
|
2023-03-10 17:01:30 -08:00
|
|
|
setSelectedAction(failedAction);
|
2023-03-11 11:43:33 -08:00
|
|
|
else if (model?.actions.length)
|
|
|
|
setSelectedAction(model.actions[model.actions.length - 1]);
|
2023-08-21 10:59:49 -07:00
|
|
|
}, [model, selectedAction, setSelectedAction, initialSelection]);
|
2023-03-31 18:34:51 -07:00
|
|
|
|
2023-05-19 15:18:18 -07:00
|
|
|
const onActionSelected = React.useCallback((action: ActionTraceEventInContext) => {
|
2023-03-31 18:34:51 -07:00
|
|
|
setSelectedAction(action);
|
|
|
|
onSelectionChanged?.(action);
|
|
|
|
}, [setSelectedAction, onSelectionChanged]);
|
2023-03-10 17:01:30 -08:00
|
|
|
|
2023-08-21 10:59:49 -07:00
|
|
|
const selectPropertiesTab = React.useCallback((tab: string) => {
|
|
|
|
setSelectedPropertiesTab(tab);
|
|
|
|
if (tab !== 'inspector')
|
|
|
|
setIsInspecting(false);
|
|
|
|
}, []);
|
|
|
|
|
2023-08-18 17:53:03 -07:00
|
|
|
const locatorPicked = React.useCallback((locator: string) => {
|
|
|
|
setHighlightedLocator(locator);
|
2023-08-21 10:59:49 -07:00
|
|
|
selectPropertiesTab('inspector');
|
|
|
|
}, [selectPropertiesTab]);
|
2023-08-18 17:53:03 -07:00
|
|
|
|
2023-03-07 12:43:16 -08:00
|
|
|
const sdkLanguage = model?.sdkLanguage || 'javascript';
|
2021-07-01 20:46:56 -07:00
|
|
|
|
2023-08-18 17:53:03 -07:00
|
|
|
const inspectorTab: TabbedPaneTabModel = {
|
|
|
|
id: 'inspector',
|
|
|
|
title: 'Locator',
|
|
|
|
render: () => <InspectorTab
|
|
|
|
sdkLanguage={sdkLanguage}
|
|
|
|
setIsInspecting={setIsInspecting}
|
|
|
|
highlightedLocator={highlightedLocator}
|
|
|
|
setHighlightedLocator={setHighlightedLocator} />,
|
|
|
|
};
|
2023-03-11 11:43:33 -08:00
|
|
|
const callTab: TabbedPaneTabModel = {
|
|
|
|
id: 'call',
|
2023-05-12 19:15:31 -07:00
|
|
|
title: 'Call',
|
2023-03-11 11:43:33 -08:00
|
|
|
render: () => <CallTab action={activeAction} sdkLanguage={sdkLanguage} />
|
|
|
|
};
|
|
|
|
const sourceTab: TabbedPaneTabModel = {
|
|
|
|
id: 'source',
|
|
|
|
title: 'Source',
|
2023-03-19 12:04:19 -07:00
|
|
|
render: () => <SourceTab
|
|
|
|
action={activeAction}
|
|
|
|
sources={sources}
|
|
|
|
hideStackFrames={hideStackFrames}
|
|
|
|
rootDir={rootDir}
|
2023-05-08 18:51:27 -07:00
|
|
|
fallbackLocation={fallbackLocation} />
|
2023-03-11 11:43:33 -08:00
|
|
|
};
|
|
|
|
const consoleTab: TabbedPaneTabModel = {
|
|
|
|
id: 'console',
|
|
|
|
title: 'Console',
|
2023-08-19 16:13:42 -07:00
|
|
|
render: () => <ConsoleTab model={model} boundaries={boundaries} selectedTime={selectedTime} />
|
2023-03-11 11:43:33 -08:00
|
|
|
};
|
|
|
|
const networkTab: TabbedPaneTabModel = {
|
|
|
|
id: 'network',
|
|
|
|
title: 'Network',
|
2023-08-16 16:30:17 -07:00
|
|
|
render: () => <NetworkTab model={model} selectedTime={selectedTime} />
|
2023-03-11 11:43:33 -08:00
|
|
|
};
|
2023-05-09 17:53:01 -07:00
|
|
|
const attachmentsTab: TabbedPaneTabModel = {
|
|
|
|
id: 'attachments',
|
|
|
|
title: 'Attachments',
|
2023-07-10 12:56:56 -07:00
|
|
|
render: () => <AttachmentsTab model={model} />
|
2023-05-09 17:53:01 -07:00
|
|
|
};
|
2021-10-23 10:23:39 -08:00
|
|
|
|
2023-03-11 11:43:33 -08:00
|
|
|
const tabs: TabbedPaneTabModel[] = showSourcesFirst ? [
|
2023-08-18 17:53:03 -07:00
|
|
|
inspectorTab,
|
2023-03-11 11:43:33 -08:00
|
|
|
sourceTab,
|
|
|
|
consoleTab,
|
|
|
|
networkTab,
|
|
|
|
callTab,
|
2023-05-09 17:53:01 -07:00
|
|
|
attachmentsTab,
|
2023-03-11 11:43:33 -08:00
|
|
|
] : [
|
2023-08-18 17:53:03 -07:00
|
|
|
inspectorTab,
|
2023-03-11 11:43:33 -08:00
|
|
|
callTab,
|
|
|
|
consoleTab,
|
|
|
|
networkTab,
|
|
|
|
sourceTab,
|
2023-05-09 17:53:01 -07:00
|
|
|
attachmentsTab,
|
2023-03-11 11:43:33 -08:00
|
|
|
];
|
2021-10-23 10:23:39 -08:00
|
|
|
|
2023-08-16 16:30:17 -07:00
|
|
|
const { boundaries } = React.useMemo(() => {
|
|
|
|
const boundaries = { minimum: model?.startTime || 0, maximum: model?.endTime || 30000 };
|
|
|
|
if (boundaries.minimum > boundaries.maximum) {
|
|
|
|
boundaries.minimum = 0;
|
|
|
|
boundaries.maximum = 30000;
|
|
|
|
}
|
|
|
|
// Leave some nice free space on the right hand side.
|
|
|
|
boundaries.maximum += (boundaries.maximum - boundaries.minimum) / 20;
|
|
|
|
return { boundaries };
|
|
|
|
}, [model]);
|
|
|
|
|
|
|
|
return <div className='vbox workbench'>
|
2023-03-06 21:37:39 -08:00
|
|
|
<Timeline
|
|
|
|
model={model}
|
2023-08-16 16:30:17 -07:00
|
|
|
boundaries={boundaries}
|
2023-03-31 18:34:51 -07:00
|
|
|
onSelected={onActionSelected}
|
2023-06-16 17:56:11 +02:00
|
|
|
sdkLanguage={sdkLanguage}
|
2023-08-16 16:30:17 -07:00
|
|
|
selectedTime={selectedTime}
|
|
|
|
setSelectedTime={setSelectedTime}
|
2023-03-06 21:37:39 -08:00
|
|
|
/>
|
2023-08-19 16:13:42 -07:00
|
|
|
<SplitView sidebarSize={250} orientation='horizontal' sidebarIsFirst={true}>
|
2023-08-21 19:40:44 -07:00
|
|
|
<SplitView sidebarSize={250} orientation={sidebarLocation === 'bottom' ? 'vertical' : 'horizontal'} settingName='propertiesSidebar'>
|
2023-08-18 17:53:03 -07:00
|
|
|
<SnapshotTab
|
|
|
|
action={activeAction}
|
|
|
|
sdkLanguage={sdkLanguage}
|
|
|
|
testIdAttributeName={model?.testIdAttributeName || 'data-testid'}
|
|
|
|
isInspecting={isInspecting}
|
|
|
|
setIsInspecting={setIsInspecting}
|
|
|
|
highlightedLocator={highlightedLocator}
|
|
|
|
setHighlightedLocator={locatorPicked} />
|
|
|
|
<TabbedPane
|
|
|
|
tabs={tabs}
|
|
|
|
selectedTab={selectedPropertiesTab}
|
2023-08-21 10:59:49 -07:00
|
|
|
setSelectedTab={selectPropertiesTab}
|
2023-08-18 17:53:03 -07:00
|
|
|
leftToolbar={[
|
2023-08-23 08:31:43 -07:00
|
|
|
<ToolbarButton title='Pick locator' icon='target' toggled={isInspecting} onClick={() => {
|
2023-08-21 10:59:49 -07:00
|
|
|
if (!isInspecting)
|
|
|
|
selectPropertiesTab('inspector');
|
2023-08-18 17:53:03 -07:00
|
|
|
setIsInspecting(!isInspecting);
|
2023-08-23 08:31:43 -07:00
|
|
|
}} />
|
2023-08-21 19:40:44 -07:00
|
|
|
]}
|
|
|
|
rightToolbar={[
|
2023-08-23 08:31:43 -07:00
|
|
|
sidebarLocation === 'bottom' ?
|
|
|
|
<ToolbarButton title='Dock to right' icon='layout-sidebar-right-off' onClick={() => {
|
|
|
|
setSidebarLocation('right');
|
|
|
|
}} /> :
|
|
|
|
<ToolbarButton title='Dock to bottom' icon='layout-panel-off' onClick={() => {
|
|
|
|
setSidebarLocation('bottom');
|
|
|
|
}} />
|
2023-08-18 17:53:03 -07:00
|
|
|
]}
|
|
|
|
/>
|
2021-03-11 11:22:59 -08:00
|
|
|
</SplitView>
|
2023-08-18 17:53:03 -07:00
|
|
|
<TabbedPane
|
|
|
|
tabs={[
|
|
|
|
{
|
|
|
|
id: 'actions',
|
|
|
|
title: 'Actions',
|
|
|
|
component: <ActionList
|
|
|
|
sdkLanguage={sdkLanguage}
|
|
|
|
actions={model?.actions || []}
|
|
|
|
selectedAction={model ? selectedAction : undefined}
|
|
|
|
selectedTime={selectedTime}
|
|
|
|
onSelected={onActionSelected}
|
|
|
|
onHighlighted={setHighlightedAction}
|
2023-08-21 10:59:49 -07:00
|
|
|
revealConsole={() => selectPropertiesTab('console')}
|
2023-08-18 17:53:03 -07:00
|
|
|
isLive={isLive}
|
|
|
|
/>
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'metadata',
|
|
|
|
title: 'Metadata',
|
|
|
|
component: <MetadataView model={model}/>
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
selectedTab={selectedNavigatorTab} setSelectedTab={setSelectedNavigatorTab}/>
|
2021-04-06 11:27:57 +08:00
|
|
|
</SplitView>
|
2021-01-07 16:15:34 -08:00
|
|
|
</div>;
|
|
|
|
};
|