diff --git a/examples/todomvc/tests/integration.spec.ts b/examples/todomvc/tests/integration.spec.ts index 007896bb2c..52dd855575 100644 --- a/examples/todomvc/tests/integration.spec.ts +++ b/examples/todomvc/tests/integration.spec.ts @@ -36,6 +36,7 @@ test.describe('New Todo', () => { await expect(page.getByTestId('todo-title')).toHaveText([ TODO_ITEMS[0], TODO_ITEMS[1], + "faux" ]); await checkNumberOfTodosInLocalStorage(page, 2); diff --git a/packages/trace-viewer/src/ui/attachmentsTab.tsx b/packages/trace-viewer/src/ui/attachmentsTab.tsx index 7a636a83b0..4536fd4325 100644 --- a/packages/trace-viewer/src/ui/attachmentsTab.tsx +++ b/packages/trace-viewer/src/ui/attachmentsTab.tsx @@ -165,7 +165,7 @@ function isEqualAttachment(a: Attachment, b: AfterActionTraceEventAttachment): b return a.name === b.name && a.path === b.path && a.sha1 === b.sha1; } -function attachmentURL(attachment: Attachment, queryParams: Record = {}) { +export function attachmentURL(attachment: Attachment, queryParams: Record = {}) { const params = new URLSearchParams(queryParams); if (attachment.sha1) { params.set('trace', attachment.traceUrl); diff --git a/packages/trace-viewer/src/ui/copyToClipboard.tsx b/packages/trace-viewer/src/ui/copyToClipboard.tsx index 1eb989d08e..b58c23f2a3 100644 --- a/packages/trace-viewer/src/ui/copyToClipboard.tsx +++ b/packages/trace-viewer/src/ui/copyToClipboard.tsx @@ -21,8 +21,9 @@ import './copyToClipboard.css'; export const CopyToClipboard: React.FunctionComponent<{ value: string | (() => Promise), description?: string, -}> = ({ value, description }) => { - const [icon, setIcon] = React.useState('copy'); + copyIcon?: string; +}> = ({ value, description, copyIcon = 'copy' }) => { + const [icon, setIcon] = React.useState(copyIcon); const handleCopy = React.useCallback(() => { const valuePromise = typeof value === 'function' ? value() : Promise.resolve(value); @@ -30,7 +31,7 @@ export const CopyToClipboard: React.FunctionComponent<{ navigator.clipboard.writeText(value).then(() => { setIcon('check'); setTimeout(() => { - setIcon('copy'); + setIcon(copyIcon); }, 3000); }, () => { setIcon('close'); @@ -39,8 +40,8 @@ export const CopyToClipboard: React.FunctionComponent<{ setIcon('close'); }); - }, [value]); - return ; + }, [value, copyIcon]); + return ; }; export const CopyToClipboardTextButton: React.FunctionComponent<{ diff --git a/packages/trace-viewer/src/ui/errorsTab.tsx b/packages/trace-viewer/src/ui/errorsTab.tsx index acf5bf838e..5adca212ca 100644 --- a/packages/trace-viewer/src/ui/errorsTab.tsx +++ b/packages/trace-viewer/src/ui/errorsTab.tsx @@ -21,6 +21,33 @@ import { PlaceholderPanel } from './placeholderPanel'; import { renderAction } from './actionList'; import type { Language } from '@isomorphic/locatorGenerators'; import type { StackFrame } from '@protocol/channels'; +import { CopyToClipboard } from './copyToClipboard'; +import { attachmentURL } from './attachmentsTab'; +import { fixTestPrompt } from '@web/components/prompts'; + +const PromptButton: React.FC<{ + error: string; + actions: modelUtil.ActionTraceEventInContext[]; +}> = ({ error, actions }) => { + const [pageSnapshot, setPageSnapshot] = React.useState(); + + React.useEffect(( )=> { + for (const action of actions) { + for (const attachment of action.attachments ?? []) { + if (attachment.name === 'pageSnapshot') { + fetch(attachmentURL({ ...attachment, traceUrl: action.context.traceUrl })).then(async response => { + setPageSnapshot(await response.text()); + }); + return; + } + } + } + }, [actions]); + + const prompt = React.useMemo(() => fixTestPrompt(error, undefined, pageSnapshot), [error, pageSnapshot]); + + return ; +}; export type ErrorDescription = { action?: modelUtil.ActionTraceEventInContext; @@ -44,9 +71,10 @@ export function useErrorsTabModel(model: modelUtil.MultiTraceModel | undefined): export const ErrorsTab: React.FunctionComponent<{ errorsModel: ErrorsTabModel, + actions: modelUtil.ActionTraceEventInContext[], sdkLanguage: Language, revealInSource: (error: ErrorDescription) => void, -}> = ({ errorsModel, sdkLanguage, revealInSource }) => { +}> = ({ errorsModel, sdkLanguage, revealInSource, actions }) => { if (!errorsModel.errors.size) return ; @@ -72,7 +100,11 @@ export const ErrorsTab: React.FunctionComponent<{ {location &&
@ revealInSource(error)}>{location}
} + + + + ; })} diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index de59892772..25d01098ed 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -199,7 +199,7 @@ export const Workbench: React.FunctionComponent<{ else setRevealedError(error); selectPropertiesTab('source'); - }} /> + }} actions={model?.actions ?? []} /> }; // Fallback location w/o action stands for file / test.