/** * 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. */ import { ErrorMessage } from '@web/components/errorMessage'; import * as React from 'react'; import type * as modelUtil from './modelUtil'; import { PlaceholderPanel } from './placeholderPanel'; import { renderAction } from './actionList'; import type { Language } from '@isomorphic/locatorGenerators'; import type { StackFrame } from '@protocol/channels'; import { CopyToClipboardTextButton } from './copyToClipboard'; import { attachmentURL } from './attachmentsTab'; import { fixTestPrompt } from '@web/components/prompts'; import type { GitCommitInfo } from '@testIsomorphic/types'; const GitCommitInfoContext = React.createContext(undefined); export function GitCommitInfoProvider({ children, gitCommitInfo }: React.PropsWithChildren<{ gitCommitInfo: GitCommitInfo }>) { return {children}; } export function useGitCommitInfo() { return React.useContext(GitCommitInfoContext); } 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 gitCommitInfo = useGitCommitInfo(); const prompt = React.useMemo( () => fixTestPrompt( error, gitCommitInfo?.['pull.diff'] ?? gitCommitInfo?.['revision.diff'], pageSnapshot ), [error, gitCommitInfo, pageSnapshot] ); return ( Copied } style={{ width: '90px', justifyContent: 'center' }} /> ); }; export type ErrorDescription = { action?: modelUtil.ActionTraceEventInContext; stack?: StackFrame[]; }; type ErrorsTabModel = { errors: Map; }; export function useErrorsTabModel(model: modelUtil.MultiTraceModel | undefined): ErrorsTabModel { return React.useMemo(() => { if (!model) return { errors: new Map() }; const errors = new Map(); for (const error of model.errorDescriptors) errors.set(error.message, error); return { errors }; }, [model]); } export const ErrorsTab: React.FunctionComponent<{ errorsModel: ErrorsTabModel, actions: modelUtil.ActionTraceEventInContext[], sdkLanguage: Language, revealInSource: (error: ErrorDescription) => void, }> = ({ errorsModel, sdkLanguage, revealInSource, actions }) => { if (!errorsModel.errors.size) return ; return
{[...errorsModel.errors.entries()].map(([message, error]) => { let location: string | undefined; let longLocation: string | undefined; const stackFrame = error.stack?.[0]; if (stackFrame) { const file = stackFrame.file.replace(/.*[/\\](.*)/, '$1'); location = file + ':' + stackFrame.line; longLocation = stackFrame.file + ':' + stackFrame.line; } return
{error.action && renderAction(error.action, { sdkLanguage })} {location &&
@ revealInSource(error)}>{location}
}
; })}
; };