chore: split error to client and server sides (#27672)

This commit is contained in:
Pavel Feldman 2023-10-17 21:34:02 -07:00 committed by GitHub
parent 8eab375c1d
commit 3aa147914c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 110 additions and 65 deletions

View file

@ -34,7 +34,7 @@ import { spawn } from 'child_process';
import { wrapInASCIIBox, isLikelyNpxGlobal, assert, gracefullyProcessExitDoNotHang, getPackageManagerExecCommand } from '../utils';
import type { Executable } from '../server';
import { registry, writeDockerVersion } from '../server';
import { isTargetClosedError } from '../common/errors';
import { isTargetClosedError } from '../client/errors';
const packageJSON = require('../../package.json');

View file

@ -27,7 +27,7 @@ import { TimeoutSettings } from '../common/timeoutSettings';
import { Waiter } from './waiter';
import { EventEmitter } from 'events';
import { Connection } from './connection';
import { isTargetClosedError, TargetClosedError } from '../common/errors';
import { isTargetClosedError, TargetClosedError } from './errors';
import { raceAgainstDeadline } from '../utils/timeoutRunner';
import type { AndroidServerLauncherImpl } from '../androidServerImpl';

View file

@ -29,7 +29,7 @@ export { Locator, FrameLocator } from './locator';
export { ElementHandle } from './elementHandle';
export { FileChooser } from './fileChooser';
export type { Logger } from './types';
export { TimeoutError } from '../common/errors';
export { TimeoutError } from './errors';
export { Frame } from './frame';
export { Keyboard, Mouse, Touchscreen } from './input';
export { JSHandle } from './jsHandle';

View file

@ -21,7 +21,7 @@ import type { Page } from './page';
import { ChannelOwner } from './channelOwner';
import { Events } from './events';
import type { LaunchOptions, BrowserContextOptions, HeadersArray } from './types';
import { isTargetClosedError } from '../common/errors';
import { isTargetClosedError } from './errors';
import type * as api from '../../types/types';
import { CDPSession } from './cdpSession';
import type { BrowserType } from './browserType';

View file

@ -43,8 +43,7 @@ import { HarRouter } from './harRouter';
import { ConsoleMessage } from './consoleMessage';
import { Dialog } from './dialog';
import { WebError } from './webError';
import { parseError } from '../protocol/serializers';
import { TargetClosedError } from '../common/errors';
import { TargetClosedError, parseError } from './errors';
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext {
_pages = new Set<Page>();

View file

@ -25,7 +25,7 @@ import { Request, Response, Route, WebSocket } from './network';
import { Page, BindingCall } from './page';
import { Worker } from './worker';
import { Dialog } from './dialog';
import { parseError } from '../protocol/serializers';
import { parseError, TargetClosedError } from './errors';
import { CDPSession } from './cdpSession';
import { Playwright } from './playwright';
import { Electron, ElectronApplication } from './electron';
@ -44,7 +44,6 @@ import { Tracing } from './tracing';
import { findValidator, ValidationError, type ValidatorContext } from '../protocol/validator';
import { createInstrumentation } from './clientInstrumentation';
import type { ClientInstrumentation } from './clientInstrumentation';
import { TargetClosedError } from '../common/errors';
import { formatCallLog, rewriteErrorMessage } from '../utils';
class Root extends ChannelOwner<channels.RootChannel> {

View file

@ -28,7 +28,7 @@ import { JSHandle, parseResult, serializeArgument } from './jsHandle';
import type { Page } from './page';
import type { Env, WaitForEventOptions, Headers, BrowserContextOptions } from './types';
import { Waiter } from './waiter';
import { TargetClosedError } from '../common/errors';
import { TargetClosedError } from './errors';
type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'|'extraHTTPHeaders'|'recordHar'|'colorScheme'|'acceptDownloads'> & {
env?: Env,

View file

@ -0,0 +1,59 @@
/**
* 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 type { SerializedError } from '@protocol/channels';
import { isError } from '../utils';
import { parseSerializedValue, serializeValue } from '../protocol/serializers';
export class TimeoutError extends Error {}
export class TargetClosedError extends Error {
constructor(cause?: string) {
super(cause || 'Target page, context or browser has been closed');
}
}
export function isTargetClosedError(error: Error) {
return error instanceof TargetClosedError;
}
export function serializeError(e: any): SerializedError {
if (isError(e))
return { error: { message: e.message, stack: e.stack, name: e.name } };
return { value: serializeValue(e, value => ({ fallThrough: value })) };
}
export function parseError(error: SerializedError): Error {
if (!error.error) {
if (error.value === undefined)
throw new Error('Serialized error must have either an error or a value');
return parseSerializedValue(error.value, undefined);
}
if (error.error.name === 'TimeoutError') {
const e = new TimeoutError(error.error.message);
e.stack = error.error.stack || '';
return e;
}
if (error.error.name === 'TargetClosedError') {
const e = new TargetClosedError(error.error.message);
e.stack = error.error.stack || '';
return e;
}
const e = new Error(error.error.message);
e.stack = error.error.stack || '';
e.name = error.error.name;
return e;
}

View file

@ -28,7 +28,7 @@ import { RawHeaders } from './network';
import type { FilePayload, Headers, StorageState } from './types';
import type { Playwright } from './playwright';
import { Tracing } from './tracing';
import { isTargetClosedError } from '../common/errors';
import { isTargetClosedError } from './errors';
export type FetchOptions = {
params?: { [key: string]: string; },

View file

@ -19,11 +19,10 @@ import fs from 'fs';
import path from 'path';
import type * as structs from '../../types/structs';
import type * as api from '../../types/types';
import { isTargetClosedError, TargetClosedError } from '../common/errors';
import { serializeError, isTargetClosedError, TargetClosedError } from './errors';
import { urlMatches } from '../utils/network';
import { TimeoutSettings } from '../common/timeoutSettings';
import type * as channels from '@protocol/channels';
import { serializeError } from '../protocol/serializers';
import { assert, headersObjectToArray, isObject, isRegExp, isString, LongStandingScope, urlMatchesEqual } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import { Accessibility } from './accessibility';

View file

@ -15,7 +15,7 @@
*/
import type * as channels from '@protocol/channels';
import { TimeoutError } from '../common/errors';
import { TimeoutError } from './errors';
import { Android } from './android';
import { BrowserType } from './browserType';
import { ChannelOwner } from './channelOwner';

View file

@ -16,7 +16,7 @@
import type { EventEmitter } from 'events';
import { rewriteErrorMessage } from '../utils/stackTrace';
import { TimeoutError } from '../common/errors';
import { TimeoutError } from './errors';
import { createGuid } from '../utils';
import type * as channels from '@protocol/channels';
import type { ChannelOwner } from './channelOwner';

View file

@ -23,7 +23,7 @@ import type { BrowserContext } from './browserContext';
import type * as api from '../../types/types';
import type * as structs from '../../types/structs';
import { LongStandingScope } from '../utils';
import { TargetClosedError } from '../common/errors';
import { TargetClosedError } from './errors';
export class Worker extends ChannelOwner<channels.WorkerChannel> implements api.Worker {
_page: Page | undefined; // Set for web workers.

View file

@ -14,36 +14,7 @@
* limitations under the License.
*/
import { TargetClosedError, TimeoutError } from '../common/errors';
import type { SerializedError, SerializedValue } from '@protocol/channels';
export function serializeError(e: any): SerializedError {
if (isError(e))
return { error: { message: e.message, stack: e.stack, name: e.name } };
return { value: serializeValue(e, value => ({ fallThrough: value })) };
}
export function parseError(error: SerializedError): Error {
if (!error.error) {
if (error.value === undefined)
throw new Error('Serialized error must have either an error or a value');
return parseSerializedValue(error.value, undefined);
}
if (error.error.name === 'TimeoutError') {
const e = new TimeoutError(error.error.message);
e.stack = error.error.stack || '';
return e;
}
if (error.error.name === 'TargetClosedError') {
const e = new TargetClosedError(error.error.message);
e.stack = error.error.stack || '';
return e;
}
const e = new Error(error.error.message);
e.stack = error.error.stack || '';
e.name = error.error.name;
return e;
}
import type { SerializedValue } from '@protocol/channels';
export function parseSerializedValue(value: SerializedValue, handles: any[] | undefined): any {
return innerParseSerializedValue(value, handles, new Map());

View file

@ -18,7 +18,7 @@ import fs from 'fs';
import { assert } from '../utils';
import { ManualPromise } from '../utils/manualPromise';
import { SdkObject } from './instrumentation';
import { TargetClosedError } from '../common/errors';
import { TargetClosedError } from './errors';
type SaveCallback = (localPath: string, error?: Error) => Promise<void>;
type CancelCallback = () => Promise<void>;

View file

@ -45,7 +45,7 @@ import { platformToFontFamilies } from './defaultFontFamilies';
import type { Protocol } from './protocol';
import { VideoRecorder } from './videoRecorder';
import { BrowserContext } from '../browserContext';
import { TargetClosedError } from '../../common/errors';
import { TargetClosedError } from '../errors';
const UTILITY_WORLD_NAME = '__playwright_utility_world__';

View file

@ -37,7 +37,7 @@ import { DialogDispatcher } from './dialogDispatcher';
import type { Page } from '../page';
import type { Dialog } from '../dialog';
import type { ConsoleMessage } from '../console';
import { serializeError } from '../../protocol/serializers';
import { serializeError } from '../errors';
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextChannel, DispatcherScope> implements channels.BrowserContextChannel {

View file

@ -16,10 +16,9 @@
import { EventEmitter } from 'events';
import type * as channels from '@protocol/channels';
import { serializeError } from '../../protocol/serializers';
import { findValidator, ValidationError, createMetadataValidator, type ValidatorContext } from '../../protocol/validator';
import { assert, isUnderTest, monotonicTime, rewriteErrorMessage } from '../../utils';
import { TargetClosedError, isTargetClosedError } from '../../common/errors';
import { TargetClosedError, isTargetClosedError, serializeError } from '../errors';
import type { CallMetadata } from '../instrumentation';
import { SdkObject } from '../instrumentation';
import type { PlaywrightDispatcher } from './playwrightDispatcher';

View file

@ -17,7 +17,7 @@
import type * as channels from '@protocol/channels';
import { Dispatcher } from './dispatcher';
import { createGuid } from '../../utils';
import { serializeError } from '../../protocol/serializers';
import { serializeError } from '../errors';
import type { LocalUtilsDispatcher } from './localUtilsDispatcher';
export class JsonPipeDispatcher extends Dispatcher<{ guid: string }, channels.JsonPipeChannel, LocalUtilsDispatcher> implements channels.JsonPipeChannel {

View file

@ -19,7 +19,7 @@ import type { Frame } from '../frames';
import { Page, Worker } from '../page';
import type * as channels from '@protocol/channels';
import { Dispatcher, existingDispatcher } from './dispatcher';
import { parseError } from '../../protocol/serializers';
import { parseError } from '../errors';
import { FrameDispatcher } from './frameDispatcher';
import { RequestDispatcher } from './networkDispatchers';
import { ResponseDispatcher } from './networkDispatchers';

View file

@ -1,6 +1,5 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
* 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.
@ -15,23 +14,43 @@
* limitations under the License.
*/
import type { SerializedError } from '@protocol/channels';
import { isError } from '../utils';
import { parseSerializedValue, serializeValue } from '../protocol/serializers';
class CustomError extends Error {
constructor(message: string) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
export class TimeoutError extends CustomError {}
export class TargetClosedError extends Error {
export class TargetClosedError extends CustomError {
constructor(cause?: string, logs?: string) {
super((cause || 'Target page, context or browser has been closed') + (logs || ''));
this.name = this.constructor.name;
}
}
export function isTargetClosedError(error: Error) {
return error instanceof TargetClosedError || error.name === 'TargetClosedError';
}
export function serializeError(e: any): SerializedError {
if (isError(e))
return { error: { message: e.message, stack: e.stack, name: e.name } };
return { value: serializeValue(e, value => ({ fallThrough: value })) };
}
export function parseError(error: SerializedError): Error {
if (!error.error) {
if (error.value === undefined)
throw new Error('Serialized error must have either an error or a value');
return parseSerializedValue(error.value, undefined);
}
const e = new Error(error.error.message);
e.stack = error.error.stack || '';
e.name = error.error.name;
return e;
}

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { TargetClosedError } from '../../common/errors';
import { TargetClosedError } from '../errors';
import { assert } from '../../utils';
import type { BrowserOptions } from '../browser';
import { Browser } from '../browser';

View file

@ -35,7 +35,7 @@ import { splitErrorMessage } from '../../utils/stackTrace';
import { debugLogger } from '../../common/debugLogger';
import { ManualPromise } from '../../utils/manualPromise';
import { BrowserContext } from '../browserContext';
import { TargetClosedError } from '../../common/errors';
import { TargetClosedError } from '../errors';
export const UTILITY_WORLD_NAME = '__playwright_utility_world__';

View file

@ -41,7 +41,7 @@ import type { ScreenshotOptions } from './screenshotter';
import type { InputFilesItems } from './dom';
import { asLocator } from '../utils/isomorphic/locatorGenerators';
import { FrameSelectors } from './frameSelectors';
import { TimeoutError } from '../common/errors';
import { TimeoutError } from './errors';
type ContextData = {
contextPromise: ManualPromise<dom.FrameExecutionContext | { destroyedReason: string }>;

View file

@ -43,7 +43,7 @@ import type { TimeoutOptions } from '../common/types';
import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
import { parseEvaluationResultValue, source } from './isomorphic/utilityScriptSerializers';
import type { SerializedValue } from './isomorphic/utilityScriptSerializers';
import { TargetClosedError } from '../common/errors';
import { TargetClosedError } from './errors';
export interface PageDelegate {
readonly rawMouse: input.RawMouse;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { TimeoutError } from '../common/errors';
import { TimeoutError } from './errors';
import { assert, monotonicTime } from '../utils';
import type { LogName } from '../common/debugLogger';
import type { CallMetadata, Instrumentation, SdkObject } from './instrumentation';

View file

@ -30,7 +30,7 @@ import type { Protocol } from './protocol';
import type { PageProxyMessageReceivedPayload } from './wkConnection';
import { kPageProxyMessageReceived, WKConnection, WKSession } from './wkConnection';
import { WKPage } from './wkPage';
import { TargetClosedError } from '../../common/errors';
import { TargetClosedError } from '../errors';
import type { SdkObject } from '../instrumentation';
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15';

View file

@ -45,7 +45,7 @@ import { WKWorkers } from './wkWorkers';
import { debugLogger } from '../../common/debugLogger';
import { ManualPromise } from '../../utils/manualPromise';
import { BrowserContext } from '../browserContext';
import { TargetClosedError } from '../../common/errors';
import { TargetClosedError } from '../errors';
const UTILITY_WORLD_NAME = '__playwright_utility_world__';