- Update copy to clipboard button. - Reveal test source in the Source tab instead of external editor. - New button to reveal in the external editor in the Source tab. - Move the Pick Locator button next to snapshot tabs.
This commit is contained in:
parent
64e4a9b0eb
commit
468b9b1e7a
|
|
@ -55,16 +55,16 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
max-height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.call-line .copy-icon {
|
.call-line:not(:hover) .toolbar-button.copy {
|
||||||
display: none;
|
display: none;
|
||||||
margin-left: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.call-line:hover .copy-icon {
|
.call-line .toolbar-button.copy {
|
||||||
display: block;
|
margin-left: 5px;
|
||||||
cursor: pointer;
|
transform: scale(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.call-value {
|
.call-value {
|
||||||
|
|
|
||||||
|
|
@ -15,23 +15,24 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||||
|
|
||||||
export const CopyToClipboard: React.FunctionComponent<{
|
export const CopyToClipboard: React.FunctionComponent<{
|
||||||
value: string,
|
value: string,
|
||||||
description?: string,
|
description?: string,
|
||||||
}> = ({ value, description }) => {
|
}> = ({ value, description }) => {
|
||||||
const [iconClassName, setIconClassName] = React.useState('codicon-clippy');
|
const [icon, setIcon] = React.useState('copy');
|
||||||
|
|
||||||
const handleCopy = React.useCallback(() => {
|
const handleCopy = React.useCallback(() => {
|
||||||
navigator.clipboard.writeText(value).then(() => {
|
navigator.clipboard.writeText(value).then(() => {
|
||||||
setIconClassName('codicon-check');
|
setIcon('check');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setIconClassName('codicon-clippy');
|
setIcon('copy');
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}, () => {
|
}, () => {
|
||||||
setIconClassName('codicon-close');
|
setIcon('close');
|
||||||
});
|
});
|
||||||
|
|
||||||
}, [value]);
|
}, [value]);
|
||||||
return <span title={description ? description : 'Copy'} className={`copy-icon codicon ${iconClassName}`} onClick={handleCopy}/>;
|
return <ToolbarButton title={description ? description : 'Copy'} icon={icon} onClick={handleCopy}/>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,8 @@ const eventsSymbol = Symbol('events');
|
||||||
export type SourceLocation = {
|
export type SourceLocation = {
|
||||||
file: string;
|
file: string;
|
||||||
line: number;
|
line: number;
|
||||||
source: SourceModel;
|
column: number;
|
||||||
|
source?: SourceModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SourceModel = {
|
export type SourceModel = {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@
|
||||||
background-color: var(--vscode-sideBar-background);
|
background-color: var(--vscode-sideBar-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.snapshot-tab .toolbar .pick-locator {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.snapshot-controls {
|
.snapshot-controls {
|
||||||
flex: none;
|
flex: none;
|
||||||
background-color: var(--vscode-sideBar-background);
|
background-color: var(--vscode-sideBar-background);
|
||||||
|
|
@ -102,29 +106,6 @@ iframe.snapshot-visible[name=snapshot] {
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popout-icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
color: var(--vscode-sideBarTitle-foreground);
|
|
||||||
font-size: 14px;
|
|
||||||
z-index: 100;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popout-icon:not(.popout-disabled):hover {
|
|
||||||
color: var(--vscode-foreground);
|
|
||||||
}
|
|
||||||
|
|
||||||
.popout-icon.popout-disabled {
|
|
||||||
opacity: var(--vscode-disabledForeground);
|
|
||||||
}
|
|
||||||
|
|
||||||
.snapshot-tab .cm-wrapper {
|
.snapshot-tab .cm-wrapper {
|
||||||
line-height: 23px;
|
line-height: 23px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ export const SnapshotTab: React.FunctionComponent<{
|
||||||
iframe={iframeRef1.current}
|
iframe={iframeRef1.current}
|
||||||
iteration={loadingRef.current.iteration} />
|
iteration={loadingRef.current.iteration} />
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
|
<ToolbarButton className='pick-locator' title='Pick locator' icon='target' toggled={isInspecting} onClick={() => setIsInspecting(!isInspecting)} />
|
||||||
{['action', 'before', 'after'].map(tab => {
|
{['action', 'before', 'after'].map(tab => {
|
||||||
return <TabbedPaneTab
|
return <TabbedPaneTab
|
||||||
id={tab}
|
id={tab}
|
||||||
|
|
|
||||||
|
|
@ -23,21 +23,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.source-tab-file-name {
|
.source-tab-file-name {
|
||||||
height: 24px;
|
padding-left: 8px;
|
||||||
margin-left: 8px;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: var(--vscode-breadcrumb-background);
|
flex: 1 1 auto;
|
||||||
box-shadow: var(--vscode-scrollbar-shadow) 0 6px 6px -6px;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.source-tab-file-name .copy-icon.codicon {
|
|
||||||
display: block;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.source-copy-to-clipboard {
|
|
||||||
display: block;
|
|
||||||
padding-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
@ -24,6 +24,8 @@ import type { SourceHighlight } from '@web/components/codeMirrorWrapper';
|
||||||
import type { SourceLocation, SourceModel } from './modelUtil';
|
import type { SourceLocation, SourceModel } from './modelUtil';
|
||||||
import type { StackFrame } from '@protocol/channels';
|
import type { StackFrame } from '@protocol/channels';
|
||||||
import { CopyToClipboard } from './copyToClipboard';
|
import { CopyToClipboard } from './copyToClipboard';
|
||||||
|
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||||
|
import { Toolbar } from '@web/components/toolbar';
|
||||||
|
|
||||||
export const SourceTab: React.FunctionComponent<{
|
export const SourceTab: React.FunctionComponent<{
|
||||||
stack: StackFrame[] | undefined,
|
stack: StackFrame[] | undefined,
|
||||||
|
|
@ -31,7 +33,8 @@ export const SourceTab: React.FunctionComponent<{
|
||||||
sources: Map<string, SourceModel>,
|
sources: Map<string, SourceModel>,
|
||||||
rootDir?: string,
|
rootDir?: string,
|
||||||
fallbackLocation?: SourceLocation,
|
fallbackLocation?: SourceLocation,
|
||||||
}> = ({ stack, sources, rootDir, fallbackLocation, stackFrameLocation }) => {
|
onOpenExternally?: (location: SourceLocation) => void,
|
||||||
|
}> = ({ stack, sources, rootDir, fallbackLocation, stackFrameLocation, onOpenExternally }) => {
|
||||||
const [lastStack, setLastStack] = React.useState<StackFrame[] | undefined>();
|
const [lastStack, setLastStack] = React.useState<StackFrame[] | undefined>();
|
||||||
const [selectedFrame, setSelectedFrame] = React.useState<number>(0);
|
const [selectedFrame, setSelectedFrame] = React.useState<number>(0);
|
||||||
|
|
||||||
|
|
@ -42,7 +45,7 @@ export const SourceTab: React.FunctionComponent<{
|
||||||
}
|
}
|
||||||
}, [stack, lastStack, setLastStack, setSelectedFrame]);
|
}, [stack, lastStack, setLastStack, setSelectedFrame]);
|
||||||
|
|
||||||
const { source, highlight, targetLine, fileName } = useAsyncMemo<{ source: SourceModel, targetLine?: number, fileName?: string, highlight: SourceHighlight[] }>(async () => {
|
const { source, highlight, targetLine, fileName, location } = useAsyncMemo<{ source: SourceModel, targetLine?: number, fileName?: string, highlight: SourceHighlight[], location?: SourceLocation }>(async () => {
|
||||||
const actionLocation = stack?.[selectedFrame];
|
const actionLocation = stack?.[selectedFrame];
|
||||||
const shouldUseFallback = !actionLocation?.file;
|
const shouldUseFallback = !actionLocation?.file;
|
||||||
if (shouldUseFallback && !fallbackLocation)
|
if (shouldUseFallback && !fallbackLocation)
|
||||||
|
|
@ -56,6 +59,7 @@ export const SourceTab: React.FunctionComponent<{
|
||||||
sources.set(file, source);
|
sources.set(file, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const location = shouldUseFallback ? fallbackLocation! : actionLocation;
|
||||||
const targetLine = shouldUseFallback ? fallbackLocation?.line || source.errors[0]?.line || 0 : actionLocation.line;
|
const targetLine = shouldUseFallback ? fallbackLocation?.line || source.errors[0]?.line || 0 : actionLocation.line;
|
||||||
const fileName = rootDir && file.startsWith(rootDir) ? file.substring(rootDir.length + 1) : file;
|
const fileName = rootDir && file.startsWith(rootDir) ? file.substring(rootDir.length + 1) : file;
|
||||||
const highlight: SourceHighlight[] = source.errors.map(e => ({ type: 'error', line: e.line, message: e.message }));
|
const highlight: SourceHighlight[] = source.errors.map(e => ({ type: 'error', line: e.line, message: e.message }));
|
||||||
|
|
@ -76,21 +80,29 @@ export const SourceTab: React.FunctionComponent<{
|
||||||
source.content = `<Unable to read "${file}">`;
|
source.content = `<Unable to read "${file}">`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { source, highlight, targetLine, fileName };
|
return { source, highlight, targetLine, fileName, location };
|
||||||
}, [stack, selectedFrame, rootDir, fallbackLocation], { source: { errors: [], content: 'Loading\u2026' }, highlight: [] });
|
}, [stack, selectedFrame, rootDir, fallbackLocation], { source: { errors: [], content: 'Loading\u2026' }, highlight: [] });
|
||||||
|
|
||||||
|
const openExternally = React.useCallback(() => {
|
||||||
|
if (!location)
|
||||||
|
return;
|
||||||
|
if (onOpenExternally) {
|
||||||
|
onOpenExternally(location);
|
||||||
|
} else {
|
||||||
|
// This should open an external protocol handler instead of actually navigating away.
|
||||||
|
window.location.href = `vscode://file//${location.file}:${location.line}`;
|
||||||
|
}
|
||||||
|
}, [onOpenExternally, location]);
|
||||||
|
|
||||||
const showStackFrames = (stack?.length ?? 0) > 1;
|
const showStackFrames = (stack?.length ?? 0) > 1;
|
||||||
|
|
||||||
return <SplitView sidebarSize={200} orientation={stackFrameLocation === 'bottom' ? 'vertical' : 'horizontal'} sidebarHidden={!showStackFrames}>
|
return <SplitView sidebarSize={200} orientation={stackFrameLocation === 'bottom' ? 'vertical' : 'horizontal'} sidebarHidden={!showStackFrames}>
|
||||||
<div className='vbox' data-testid='source-code'>
|
<div className='vbox' data-testid='source-code'>
|
||||||
{fileName && (
|
{ fileName && <Toolbar>
|
||||||
<div className='source-tab-file-name'>
|
<span className='source-tab-file-name'>{fileName}</span>
|
||||||
{fileName}
|
<CopyToClipboard description='Copy filename' value={getFileName(fileName, targetLine)}/>
|
||||||
<span className='source-copy-to-clipboard'>
|
{location && <ToolbarButton icon='link-external' title='Open in VS Code' onClick={openExternally}></ToolbarButton>}
|
||||||
<CopyToClipboard description='Copy filename' value={getFileName(fileName, targetLine)}/>
|
</Toolbar> }
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<CodeMirrorWrapper text={source.content || ''} language='javascript' highlight={highlight} revealLine={targetLine} readOnly={true} lineNumbers={true} />
|
<CodeMirrorWrapper text={source.content || ''} language='javascript' highlight={highlight} revealLine={targetLine} readOnly={true} lineNumbers={true} />
|
||||||
</div>
|
</div>
|
||||||
<StackTraceView stack={stack} selectedFrame={selectedFrame} setSelectedFrame={setSelectedFrame} />
|
<StackTraceView stack={stack} selectedFrame={selectedFrame} setSelectedFrame={setSelectedFrame} />
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,9 @@ export const TestListView: React.FC<{
|
||||||
isLoading?: boolean,
|
isLoading?: boolean,
|
||||||
onItemSelected: (item: { treeItem?: TreeItem, testCase?: reporterTypes.TestCase, testFile?: SourceLocation }) => void,
|
onItemSelected: (item: { treeItem?: TreeItem, testCase?: reporterTypes.TestCase, testFile?: SourceLocation }) => void,
|
||||||
requestedCollapseAllCount: number,
|
requestedCollapseAllCount: number,
|
||||||
setFilterText: (text: string) => void;
|
setFilterText: (text: string) => void,
|
||||||
}> = ({ filterText, testModel, testServerConnection, testTree, runTests, runningState, watchAll, watchedTreeIds, setWatchedTreeIds, isLoading, onItemSelected, requestedCollapseAllCount, setFilterText }) => {
|
onRevealSource: () => void,
|
||||||
|
}> = ({ filterText, testModel, testServerConnection, testTree, runTests, runningState, watchAll, watchedTreeIds, setWatchedTreeIds, isLoading, onItemSelected, requestedCollapseAllCount, setFilterText, onRevealSource }) => {
|
||||||
const [treeState, setTreeState] = React.useState<TreeState>({ expandedItems: new Map() });
|
const [treeState, setTreeState] = React.useState<TreeState>({ expandedItems: new Map() });
|
||||||
const [selectedTreeItemId, setSelectedTreeItemId] = React.useState<string | undefined>();
|
const [selectedTreeItemId, setSelectedTreeItemId] = React.useState<string | undefined>();
|
||||||
const [collapseAllCount, setCollapseAllCount] = React.useState(requestedCollapseAllCount);
|
const [collapseAllCount, setCollapseAllCount] = React.useState(requestedCollapseAllCount);
|
||||||
|
|
@ -91,17 +92,7 @@ export const TestListView: React.FC<{
|
||||||
if (!testModel)
|
if (!testModel)
|
||||||
return { selectedTreeItem: undefined };
|
return { selectedTreeItem: undefined };
|
||||||
const selectedTreeItem = selectedTreeItemId ? testTree.treeItemById(selectedTreeItemId) : undefined;
|
const selectedTreeItem = selectedTreeItemId ? testTree.treeItemById(selectedTreeItemId) : undefined;
|
||||||
let testFile: SourceLocation | undefined;
|
const testFile = itemLocation(selectedTreeItem, testModel);
|
||||||
if (selectedTreeItem) {
|
|
||||||
testFile = {
|
|
||||||
file: selectedTreeItem.location.file,
|
|
||||||
line: selectedTreeItem.location.line,
|
|
||||||
source: {
|
|
||||||
errors: testModel.loadErrors.filter(e => e.location?.file === selectedTreeItem.location.file).map(e => ({ line: e.location!.line, message: e.message! })),
|
|
||||||
content: undefined,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let selectedTest: reporterTypes.TestCase | undefined;
|
let selectedTest: reporterTypes.TestCase | undefined;
|
||||||
if (selectedTreeItem?.kind === 'test')
|
if (selectedTreeItem?.kind === 'test')
|
||||||
selectedTest = selectedTreeItem.test;
|
selectedTest = selectedTreeItem.test;
|
||||||
|
|
@ -164,7 +155,7 @@ export const TestListView: React.FC<{
|
||||||
{!!treeItem.duration && treeItem.status !== 'skipped' && <div className='ui-mode-list-item-time'>{msToString(treeItem.duration)}</div>}
|
{!!treeItem.duration && treeItem.status !== 'skipped' && <div className='ui-mode-list-item-time'>{msToString(treeItem.duration)}</div>}
|
||||||
<Toolbar noMinHeight={true} noShadow={true}>
|
<Toolbar noMinHeight={true} noShadow={true}>
|
||||||
<ToolbarButton icon='play' title='Run' onClick={() => runTreeItem(treeItem)} disabled={!!runningState}></ToolbarButton>
|
<ToolbarButton icon='play' title='Run' onClick={() => runTreeItem(treeItem)} disabled={!!runningState}></ToolbarButton>
|
||||||
<ToolbarButton icon='go-to-file' title='Open in VS Code' onClick={() => testServerConnection?.openNoReply({ location: treeItem.location })} style={(treeItem.kind === 'group' && treeItem.subKind === 'folder') ? { visibility: 'hidden' } : {}}></ToolbarButton>
|
<ToolbarButton icon='go-to-file' title='Show source' onClick={onRevealSource} style={(treeItem.kind === 'group' && treeItem.subKind === 'folder') ? { visibility: 'hidden' } : {}}></ToolbarButton>
|
||||||
{!watchAll && <ToolbarButton icon='eye' title='Watch' onClick={() => {
|
{!watchAll && <ToolbarButton icon='eye' title='Watch' onClick={() => {
|
||||||
if (watchedTreeIds.value.has(treeItem.id))
|
if (watchedTreeIds.value.has(treeItem.id))
|
||||||
watchedTreeIds.value.delete(treeItem.id);
|
watchedTreeIds.value.delete(treeItem.id);
|
||||||
|
|
@ -187,3 +178,17 @@ export const TestListView: React.FC<{
|
||||||
autoExpandDepth={filterText ? 5 : 1}
|
autoExpandDepth={filterText ? 5 : 1}
|
||||||
noItemsMessage={isLoading ? 'Loading\u2026' : 'No tests'} />;
|
noItemsMessage={isLoading ? 'Loading\u2026' : 'No tests'} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function itemLocation(item: TreeItem | undefined, model: TestModel | undefined): SourceLocation | undefined {
|
||||||
|
if (!item || !model)
|
||||||
|
return;
|
||||||
|
return {
|
||||||
|
file: item.location.file,
|
||||||
|
line: item.location.line,
|
||||||
|
column: item.location.column,
|
||||||
|
source: {
|
||||||
|
errors: model.loadErrors.filter(e => e.location?.file === item.location.file).map(e => ({ line: e.location!.line, message: e.message! })),
|
||||||
|
content: undefined,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,9 @@ export const TraceView: React.FC<{
|
||||||
showRouteActionsSetting: Setting<boolean>,
|
showRouteActionsSetting: Setting<boolean>,
|
||||||
item: { treeItem?: TreeItem, testFile?: SourceLocation, testCase?: reporterTypes.TestCase },
|
item: { treeItem?: TreeItem, testFile?: SourceLocation, testCase?: reporterTypes.TestCase },
|
||||||
rootDir?: string,
|
rootDir?: string,
|
||||||
}> = ({ showRouteActionsSetting, item, rootDir }) => {
|
onOpenExternally?: (location: SourceLocation) => void,
|
||||||
|
revealSource?: boolean,
|
||||||
|
}> = ({ showRouteActionsSetting, item, rootDir, onOpenExternally, revealSource }) => {
|
||||||
const [model, setModel] = React.useState<{ model: MultiTraceModel, isLive: boolean } | undefined>();
|
const [model, setModel] = React.useState<{ model: MultiTraceModel, isLive: boolean } | undefined>();
|
||||||
const [counter, setCounter] = React.useState(0);
|
const [counter, setCounter] = React.useState(0);
|
||||||
const pollTimer = React.useRef<NodeJS.Timeout | null>(null);
|
const pollTimer = React.useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
@ -97,7 +99,10 @@ export const TraceView: React.FC<{
|
||||||
onSelectionChanged={onSelectionChanged}
|
onSelectionChanged={onSelectionChanged}
|
||||||
fallbackLocation={item.testFile}
|
fallbackLocation={item.testFile}
|
||||||
isLive={model?.isLive}
|
isLive={model?.isLive}
|
||||||
status={item.treeItem?.status} />;
|
status={item.treeItem?.status}
|
||||||
|
onOpenExternally={onOpenExternally}
|
||||||
|
revealSource={revealSource}
|
||||||
|
/>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const outputDirForTestCase = (testCase: reporterTypes.TestCase): string | undefined => {
|
const outputDirForTestCase = (testCase: reporterTypes.TestCase): string | undefined => {
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,8 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
const [testServerConnection, setTestServerConnection] = React.useState<TestServerConnection>();
|
const [testServerConnection, setTestServerConnection] = React.useState<TestServerConnection>();
|
||||||
const [settingsVisible, setSettingsVisible] = React.useState(false);
|
const [settingsVisible, setSettingsVisible] = React.useState(false);
|
||||||
const [testingOptionsVisible, setTestingOptionsVisible] = React.useState(false);
|
const [testingOptionsVisible, setTestingOptionsVisible] = React.useState(false);
|
||||||
|
const [revealSource, setRevealSource] = React.useState(false);
|
||||||
|
const onRevealSource = React.useCallback(() => setRevealSource(true), [setRevealSource]);
|
||||||
|
|
||||||
const [runWorkers, setRunWorkers] = React.useState(queryParams.workers);
|
const [runWorkers, setRunWorkers] = React.useState(queryParams.workers);
|
||||||
const singleWorkerSetting = React.useMemo(() => {
|
const singleWorkerSetting = React.useMemo(() => {
|
||||||
|
|
@ -435,7 +437,13 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
<XtermWrapper source={xtermDataSource}></XtermWrapper>
|
<XtermWrapper source={xtermDataSource}></XtermWrapper>
|
||||||
</div>
|
</div>
|
||||||
<div className={'vbox' + (isShowingOutput ? ' hidden' : '')}>
|
<div className={'vbox' + (isShowingOutput ? ' hidden' : '')}>
|
||||||
<TraceView showRouteActionsSetting={showRouteActionsSetting} item={selectedItem} rootDir={testModel?.config?.rootDir} />
|
<TraceView
|
||||||
|
showRouteActionsSetting={showRouteActionsSetting}
|
||||||
|
item={selectedItem}
|
||||||
|
rootDir={testModel?.config?.rootDir}
|
||||||
|
revealSource={revealSource}
|
||||||
|
onOpenExternally={location => testServerConnection?.openNoReply({ location: { file: location.file, line: location.line, column: location.column } })}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='vbox ui-mode-sidebar'>
|
<div className='vbox ui-mode-sidebar'>
|
||||||
|
|
@ -487,6 +495,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
requestedCollapseAllCount={collapseAllCount}
|
requestedCollapseAllCount={collapseAllCount}
|
||||||
setFilterText={setFilterText}
|
setFilterText={setFilterText}
|
||||||
|
onRevealSource={onRevealSource}
|
||||||
/>
|
/>
|
||||||
<Toolbar noShadow={true} noMinHeight={true} className='settings-toolbar' onClick={() => setTestingOptionsVisible(!testingOptionsVisible)}>
|
<Toolbar noShadow={true} noMinHeight={true} className='settings-toolbar' onClick={() => setTestingOptionsVisible(!testingOptionsVisible)}>
|
||||||
<span
|
<span
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,9 @@ export const Workbench: React.FunctionComponent<{
|
||||||
inert?: boolean,
|
inert?: boolean,
|
||||||
showRouteActionsSetting?: Setting<boolean>,
|
showRouteActionsSetting?: Setting<boolean>,
|
||||||
openPage?: (url: string, target?: string) => Window | any,
|
openPage?: (url: string, target?: string) => Window | any,
|
||||||
}> = ({ showRouteActionsSetting, model, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive, status, inert, openPage }) => {
|
onOpenExternally?: (location: modelUtil.SourceLocation) => void,
|
||||||
|
revealSource?: boolean,
|
||||||
|
}> = ({ showRouteActionsSetting, model, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive, status, inert, openPage, onOpenExternally, revealSource }) => {
|
||||||
const [selectedAction, setSelectedActionImpl] = React.useState<ActionTraceEventInContext | undefined>(undefined);
|
const [selectedAction, setSelectedActionImpl] = React.useState<ActionTraceEventInContext | undefined>(undefined);
|
||||||
const [revealedStack, setRevealedStack] = React.useState<StackFrame[] | undefined>(undefined);
|
const [revealedStack, setRevealedStack] = React.useState<StackFrame[] | undefined>(undefined);
|
||||||
const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEventInContext | undefined>();
|
const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEventInContext | undefined>();
|
||||||
|
|
@ -63,7 +65,7 @@ export const Workbench: React.FunctionComponent<{
|
||||||
const [highlightedConsoleMessage, setHighlightedConsoleMessage] = React.useState<ConsoleEntry | undefined>();
|
const [highlightedConsoleMessage, setHighlightedConsoleMessage] = React.useState<ConsoleEntry | undefined>();
|
||||||
const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState<string>('actions');
|
const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState<string>('actions');
|
||||||
const [selectedPropertiesTab, setSelectedPropertiesTab] = useSetting<string>('propertiesTab', showSourcesFirst ? 'source' : 'call');
|
const [selectedPropertiesTab, setSelectedPropertiesTab] = useSetting<string>('propertiesTab', showSourcesFirst ? 'source' : 'call');
|
||||||
const [isInspecting, setIsInspecting] = React.useState(false);
|
const [isInspecting, setIsInspectingState] = React.useState(false);
|
||||||
const [highlightedLocator, setHighlightedLocator] = React.useState<string>('');
|
const [highlightedLocator, setHighlightedLocator] = React.useState<string>('');
|
||||||
const activeAction = model ? highlightedAction || selectedAction : undefined;
|
const activeAction = model ? highlightedAction || selectedAction : undefined;
|
||||||
const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>();
|
const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>();
|
||||||
|
|
@ -87,6 +89,7 @@ export const Workbench: React.FunctionComponent<{
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setSelectedTime(undefined);
|
setSelectedTime(undefined);
|
||||||
|
setRevealedStack(undefined);
|
||||||
}, [model]);
|
}, [model]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|
@ -118,14 +121,25 @@ export const Workbench: React.FunctionComponent<{
|
||||||
const selectPropertiesTab = React.useCallback((tab: string) => {
|
const selectPropertiesTab = React.useCallback((tab: string) => {
|
||||||
setSelectedPropertiesTab(tab);
|
setSelectedPropertiesTab(tab);
|
||||||
if (tab !== 'inspector')
|
if (tab !== 'inspector')
|
||||||
setIsInspecting(false);
|
setIsInspectingState(false);
|
||||||
}, [setSelectedPropertiesTab]);
|
}, [setSelectedPropertiesTab]);
|
||||||
|
|
||||||
|
const setIsInspecting = React.useCallback((value: boolean) => {
|
||||||
|
if (!isInspecting && value)
|
||||||
|
selectPropertiesTab('inspector');
|
||||||
|
setIsInspectingState(value);
|
||||||
|
}, [setIsInspectingState, selectPropertiesTab, isInspecting]);
|
||||||
|
|
||||||
const locatorPicked = React.useCallback((locator: string) => {
|
const locatorPicked = React.useCallback((locator: string) => {
|
||||||
setHighlightedLocator(locator);
|
setHighlightedLocator(locator);
|
||||||
selectPropertiesTab('inspector');
|
selectPropertiesTab('inspector');
|
||||||
}, [selectPropertiesTab]);
|
}, [selectPropertiesTab]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (revealSource)
|
||||||
|
selectPropertiesTab('source');
|
||||||
|
}, [revealSource, selectPropertiesTab]);
|
||||||
|
|
||||||
const consoleModel = useConsoleTabModel(model, selectedTime);
|
const consoleModel = useConsoleTabModel(model, selectedTime);
|
||||||
const networkModel = useNetworkTabModel(model, selectedTime);
|
const networkModel = useNetworkTabModel(model, selectedTime);
|
||||||
const errorsModel = useErrorsTabModel(model);
|
const errorsModel = useErrorsTabModel(model);
|
||||||
|
|
@ -174,7 +188,9 @@ export const Workbench: React.FunctionComponent<{
|
||||||
sources={sources}
|
sources={sources}
|
||||||
rootDir={rootDir}
|
rootDir={rootDir}
|
||||||
stackFrameLocation={sidebarLocation === 'bottom' ? 'right' : 'bottom'}
|
stackFrameLocation={sidebarLocation === 'bottom' ? 'right' : 'bottom'}
|
||||||
fallbackLocation={fallbackLocation} />
|
fallbackLocation={fallbackLocation}
|
||||||
|
onOpenExternally={onOpenExternally}
|
||||||
|
/>
|
||||||
};
|
};
|
||||||
const consoleTab: TabbedPaneTabModel = {
|
const consoleTab: TabbedPaneTabModel = {
|
||||||
id: 'console',
|
id: 'console',
|
||||||
|
|
@ -302,13 +318,6 @@ export const Workbench: React.FunctionComponent<{
|
||||||
tabs={tabs}
|
tabs={tabs}
|
||||||
selectedTab={selectedPropertiesTab}
|
selectedTab={selectedPropertiesTab}
|
||||||
setSelectedTab={selectPropertiesTab}
|
setSelectedTab={selectPropertiesTab}
|
||||||
leftToolbar={[
|
|
||||||
<ToolbarButton title='Pick locator' icon='target' toggled={isInspecting} onClick={() => {
|
|
||||||
if (!isInspecting)
|
|
||||||
selectPropertiesTab('inspector');
|
|
||||||
setIsInspecting(!isInspecting);
|
|
||||||
}} />
|
|
||||||
]}
|
|
||||||
rightToolbar={[
|
rightToolbar={[
|
||||||
sidebarLocation === 'bottom' ?
|
sidebarLocation === 'bottom' ?
|
||||||
<ToolbarButton title='Dock to right' icon='layout-sidebar-right-off' onClick={() => {
|
<ToolbarButton title='Dock to right' icon='layout-sidebar-right-off' onClick={() => {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ export interface ToolbarButtonProps {
|
||||||
onClick: (e: React.MouseEvent) => void,
|
onClick: (e: React.MouseEvent) => void,
|
||||||
style?: React.CSSProperties,
|
style?: React.CSSProperties,
|
||||||
testId?: string,
|
testId?: string,
|
||||||
|
className?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ToolbarButton: React.FC<React.PropsWithChildren<ToolbarButtonProps>> = ({
|
export const ToolbarButton: React.FC<React.PropsWithChildren<ToolbarButtonProps>> = ({
|
||||||
|
|
@ -37,8 +38,9 @@ export const ToolbarButton: React.FC<React.PropsWithChildren<ToolbarButtonProps>
|
||||||
onClick = () => {},
|
onClick = () => {},
|
||||||
style,
|
style,
|
||||||
testId,
|
testId,
|
||||||
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
let className = `toolbar-button ${icon}`;
|
className = (className || '') + ` toolbar-button ${icon}`;
|
||||||
if (toggled)
|
if (toggled)
|
||||||
className += ' toggled';
|
className += ' toggled';
|
||||||
return <button
|
return <button
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,8 @@ test('should update test locations', async ({ runUITest, writeFiles }) => {
|
||||||
|
|
||||||
const passesItemLocator = page.getByRole('listitem').filter({ hasText: 'passes' });
|
const passesItemLocator = page.getByRole('listitem').filter({ hasText: 'passes' });
|
||||||
await passesItemLocator.hover();
|
await passesItemLocator.hover();
|
||||||
await passesItemLocator.getByTitle('Open in VS Code').click();
|
await passesItemLocator.getByTitle('Show source').click();
|
||||||
|
await page.getByTitle('Open in VS Code').click();
|
||||||
|
|
||||||
expect(messages).toEqual([{
|
expect(messages).toEqual([{
|
||||||
method: 'open',
|
method: 'open',
|
||||||
|
|
@ -247,7 +248,8 @@ test('should update test locations', async ({ runUITest, writeFiles }) => {
|
||||||
|
|
||||||
messages.length = 0;
|
messages.length = 0;
|
||||||
await passesItemLocator.hover();
|
await passesItemLocator.hover();
|
||||||
await passesItemLocator.getByTitle('Open in VS Code').click();
|
await passesItemLocator.getByTitle('Show source').click();
|
||||||
|
await page.getByTitle('Open in VS Code').click();
|
||||||
|
|
||||||
expect(messages).toEqual([{
|
expect(messages).toEqual([{
|
||||||
method: 'open',
|
method: 'open',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue