chore: move urlMatch to isomorphic (#32142)

To be reused in injected code.
This commit is contained in:
Dmitry Gozman 2024-08-13 12:47:02 -07:00 committed by GitHub
parent 6cc53cfce6
commit f8eef3897c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 83 additions and 93 deletions

View file

@ -28,9 +28,8 @@ import { Worker } from './worker';
import { Events } from './events';
import { TimeoutSettings } from '../common/timeoutSettings';
import { Waiter } from './waiter';
import type { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions } from './types';
import { headersObjectToArray, isRegExp, isString, urlMatchesEqual } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import type { Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions } from './types';
import { type URLMatch, headersObjectToArray, isRegExp, isString, urlMatchesEqual, mkdirIfNeeded } from '../utils';
import type * as api from '../../types/types';
import type * as structs from '../../types/structs';
import { CDPSession } from './cdpSession';

View file

@ -30,9 +30,9 @@ import type { Page } from './page';
import { EventEmitter } from 'events';
import { Waiter } from './waiter';
import { Events } from './events';
import type { LifecycleEvent, URLMatch, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions, StrictOptions } from './types';
import type { LifecycleEvent, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions, StrictOptions } from './types';
import { kLifecycleEvents } from './types';
import { urlMatches } from '../utils/network';
import { type URLMatch, urlMatches } from '../utils';
import type * as api from '../../types/types';
import type * as structs from '../../types/structs';
import { addSourceUrlToScript } from './clientHelper';

View file

@ -18,7 +18,7 @@ import { debugLogger } from '../utils/debugLogger';
import type { BrowserContext } from './browserContext';
import type { LocalUtils } from './localUtils';
import type { Route } from './network';
import type { URLMatch } from './types';
import type { URLMatch } from '../utils';
import type { Page } from './page';
type HarNotFoundAction = 'abort' | 'fallback';

View file

@ -28,9 +28,8 @@ import { Events } from './events';
import type { Page } from './page';
import { Waiter } from './waiter';
import type * as api from '../../types/types';
import type { HeadersArray, URLMatch } from '../common/types';
import { urlMatches } from '../utils/network';
import { MultiMap } from '../utils/multimap';
import type { HeadersArray } from '../common/types';
import { MultiMap, urlMatches, type URLMatch } from '../utils';
import { APIResponse } from './fetch';
import type { Serializable } from '../../types/structs';
import type { BrowserContext } from './browserContext';

View file

@ -20,11 +20,9 @@ import path from 'path';
import type * as structs from '../../types/structs';
import type * as api from '../../types/types';
import { serializeError, isTargetClosedError, TargetClosedError } from './errors';
import { urlMatches } from '../utils/network';
import { TimeoutSettings } from '../common/timeoutSettings';
import type * as channels from '@protocol/channels';
import { assert, headersObjectToArray, isObject, isRegExp, isString, LongStandingScope, urlMatchesEqual } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import { assert, headersObjectToArray, isObject, isRegExp, isString, LongStandingScope, urlMatches, urlMatchesEqual, mkdirIfNeeded, trimStringWithEllipsis, type URLMatch } from '../utils';
import { Accessibility } from './accessibility';
import { Artifact } from './artifact';
import type { BrowserContext } from './browserContext';
@ -42,9 +40,8 @@ import { Keyboard, Mouse, Touchscreen } from './input';
import { assertMaxArguments, JSHandle, parseResult, serializeArgument } from './jsHandle';
import type { FrameLocator, Locator, LocatorOptions } from './locator';
import type { ByRoleOptions } from '../utils/isomorphic/locatorUtils';
import { trimStringWithEllipsis } from '../utils/isomorphic/stringUtils';
import { type RouteHandlerCallback, type Request, Response, Route, RouteHandler, validateHeaders, WebSocket } from './network';
import type { FilePayload, Headers, LifecycleEvent, SelectOption, SelectOptionOptions, Size, URLMatch, WaitForEventOptions, WaitForFunctionOptions } from './types';
import type { FilePayload, Headers, LifecycleEvent, SelectOption, SelectOptionOptions, Size, WaitForEventOptions, WaitForFunctionOptions } from './types';
import { Video } from './video';
import { Waiter } from './waiter';
import { Worker } from './worker';

View file

@ -17,7 +17,7 @@
import type * as channels from '@protocol/channels';
import type { Size } from '../common/types';
export type { Size, Point, Rect, Quad, URLMatch, TimeoutOptions, HeadersArray } from '../common/types';
export type { Size, Point, Rect, Quad, TimeoutOptions, HeadersArray } from '../common/types';
type LoggerSeverity = 'verbose' | 'info' | 'warning' | 'error';
export interface Logger {

View file

@ -18,7 +18,6 @@ export type Size = { width: number, height: number };
export type Point = { x: number, y: number };
export type Rect = Size & Point;
export type Quad = [ Point, Point, Point, Point ];
export type URLMatch = string | RegExp | ((url: URL) => boolean);
export type TimeoutOptions = { timeout?: number };
export type NameValue = { name: string, value: string };
export type HeadersArray = NameValue[];

View file

@ -19,7 +19,7 @@ import { createPlaywright, DispatcherConnection, RootDispatcher, PlaywrightDispa
import { Connection } from './client/connection';
import { BrowserServerLauncherImpl } from './browserServerImpl';
import { AndroidServerLauncherImpl } from './androidServerImpl';
import type { Language } from './utils/isomorphic/locatorGenerators';
import type { Language } from './utils';
export function createInProcessPlaywright(): PlaywrightAPI {
const playwright = createPlaywright({ sdkLanguage: (process.env.PW_LANG_NAME as Language | undefined) || 'javascript' });

View file

@ -23,8 +23,7 @@ import type { InstrumentationListener } from './instrumentation';
import type { Playwright } from './playwright';
import { Recorder } from './recorder';
import { EmptyRecorderApp } from './recorder/recorderApp';
import { asLocator } from '../utils/isomorphic/locatorGenerators';
import type { Language } from '../utils/isomorphic/locatorGenerators';
import { asLocator, type Language } from '../utils';
const internalMetadata = serverSideCallMetadata();

View file

@ -20,7 +20,7 @@ import { stringifySelector, type ParsedSelector, splitSelectorByFrame, InvalidSe
import type { FrameExecutionContext, ElementHandle } from './dom';
import type { JSHandle } from './javascript';
import type { InjectedScript } from './injected/injectedScript';
import { asLocator } from '../utils/isomorphic/locatorGenerators';
import { asLocator } from '../utils';
export type SelectorInfo = {
parsed: ParsedSelector,

View file

@ -29,7 +29,7 @@ import * as types from './types';
import { BrowserContext } from './browserContext';
import type { Progress } from './progress';
import { ProgressController } from './progress';
import { LongStandingScope, assert, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime } from '../utils';
import { LongStandingScope, assert, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime, asLocator } from '../utils';
import { ManualPromise } from '../utils/manualPromise';
import { debugLogger } from '../utils/debugLogger';
import type { CallMetadata } from './instrumentation';
@ -38,7 +38,6 @@ import type { InjectedScript, ElementStateWithoutStable, FrameExpectParams } fro
import { isSessionClosedError } from './protocolError';
import { type ParsedSelector, isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
import type { ScreenshotOptions } from './screenshotter';
import { asLocator } from '../utils/isomorphic/locatorGenerators';
import { FrameSelectors } from './frameSelectors';
import { TimeoutError } from './errors';
import { prepareFilesForUpload } from './fileUploadUtils';

View file

@ -27,11 +27,9 @@ import type { RegisteredListener } from '../../utils/eventsHelper';
import { eventsHelper } from '../../utils/eventsHelper';
import { mime } from '../../utilsBundle';
import { ManualPromise } from '../../utils/manualPromise';
import { getPlaywrightVersion } from '../../utils/userAgent';
import { urlMatches } from '../../utils/network';
import { getPlaywrightVersion, isTextualMimeType, urlMatches } from '../../utils';
import { Frame } from '../frames';
import type { HeadersArray, LifecycleEvent } from '../types';
import { isTextualMimeType } from '../../utils/isomorphic/mimeType';
const FALLBACK_HTTP_VERSION = 'HTTP/1.1';

View file

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

View file

@ -24,10 +24,9 @@ import { Selectors } from './selectors';
import { WebKit } from './webkit/webkit';
import type { CallMetadata } from './instrumentation';
import { createInstrumentation, SdkObject } from './instrumentation';
import { debugLogger } from '../utils/debugLogger';
import { debugLogger, type Language } from '../utils';
import type { Page } from './page';
import { DebugController } from './debugController';
import type { Language } from '../utils/isomorphic/locatorGenerators';
import type { BrowserType } from './browserType';
type PlaywrightOptions = {

View file

@ -43,8 +43,7 @@ import { EventEmitter } from 'events';
import { raceAgainstDeadline } from '../utils/timeoutRunner';
import type { Language, LanguageGenerator } from './recorder/language';
import { locatorOrSelectorAsSelector } from '../utils/isomorphic/locatorParser';
import { quoteCSSAttributeValue } from '../utils/isomorphic/stringUtils';
import { eventsHelper, type RegisteredListener } from './../utils/eventsHelper';
import { quoteCSSAttributeValue, eventsHelper, type RegisteredListener } from '../utils';
import type { Dialog } from './dialog';
type BindingSource = { frame: Frame, page: Page };

View file

@ -21,9 +21,8 @@ import type { ActionInContext } from './codeGenerator';
import type { Action } from './recorderActions';
import type { MouseClickOptions } from './utils';
import { toModifiers } from './utils';
import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
import { escapeWithQuotes, asLocator } from '../../utils';
import { deviceDescriptors } from '../deviceDescriptors';
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
type CSharpLanguageMode = 'library' | 'mstest' | 'nunit';

View file

@ -23,8 +23,7 @@ import type { MouseClickOptions } from './utils';
import { toModifiers } from './utils';
import { deviceDescriptors } from '../deviceDescriptors';
import { JavaScriptFormatter } from './javascript';
import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
import { escapeWithQuotes, asLocator } from '../../utils';
type JavaLanguageMode = 'library' | 'junit';

View file

@ -22,8 +22,7 @@ import type { Action } from './recorderActions';
import type { MouseClickOptions } from './utils';
import { toModifiers } from './utils';
import { deviceDescriptors } from '../deviceDescriptors';
import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
import { escapeWithQuotes, asLocator } from '../../utils';
export class JavaScriptLanguageGenerator implements LanguageGenerator {
id: string;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
import { asLocator } from '../../utils';
import type { ActionInContext } from './codeGenerator';
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './language';

View file

@ -15,10 +15,10 @@
*/
import type { BrowserContextOptions, LaunchOptions } from '../../..';
import type { Language } from '../../utils/isomorphic/locatorGenerators';
import type { Language } from '../../utils';
import type { ActionInContext } from './codeGenerator';
import type { Action, DialogSignal, DownloadSignal, PopupSignal } from './recorderActions';
export type { Language } from '../../utils/isomorphic/locatorGenerators';
export type { Language } from '../../utils';
export type LanguageGeneratorOptions = {
browserName: string;

View file

@ -21,9 +21,8 @@ import type { ActionInContext } from './codeGenerator';
import type { Action } from './recorderActions';
import type { MouseClickOptions } from './utils';
import { toModifiers } from './utils';
import { escapeWithQuotes, toSnakeCase } from '../../utils/isomorphic/stringUtils';
import { escapeWithQuotes, toSnakeCase, asLocator } from '../../utils';
import { deviceDescriptors } from '../deviceDescriptors';
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
export class PythonLanguageGenerator implements LanguageGenerator {
id: string;

View file

@ -16,7 +16,7 @@
*/
import type { Size, Point, TimeoutOptions, HeadersArray } from '../common/types';
export type { Size, Point, Rect, Quad, URLMatch, TimeoutOptions, HeadersArray } from '../common/types';
export type { Size, Point, Rect, Quad, TimeoutOptions, HeadersArray } from '../common/types';
import type * as channels from '@protocol/channels';
export type StrictOptions = {

View file

@ -18,15 +18,18 @@ export * from './ascii';
export * from './comparators';
export * from './crypto';
export * from './debug';
export * from './debugLogger';
export * from './env';
export * from './eventsHelper';
export * from './fileUtils';
export * from './glob';
export * from './headers';
export * from './hostPlatform';
export * from './httpServer';
export * from './manualPromise';
export * from './isomorphic/locatorGenerators';
export * from './isomorphic/mimeType';
export * from './isomorphic/stringUtils';
export * from './isomorphic/urlMatch';
export * from './multimap';
export * from './network';
export * from './processLauncher';
@ -43,5 +46,3 @@ export * from './userAgent';
export * from './wsServer';
export * from './zipFile';
export * from './zones';
export * from './isomorphic/locatorGenerators';
export * from './isomorphic/stringUtils';

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { escapeForAttributeSelector, escapeForTextSelector } from '../../utils/isomorphic/stringUtils';
import { escapeForAttributeSelector, escapeForTextSelector } from './stringUtils';
import { asLocators } from './locatorGenerators';
import type { Language, Quote } from './locatorGenerators';
import { parseSelector } from './selectorParser';

View file

@ -14,6 +14,8 @@
* limitations under the License.
*/
import { isString } from './stringUtils';
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
const escapedChars = new Set(['$', '^', '+', '.', '*', '(', ')', '|', '\\', '?', '{', '}', '[', ']']);
@ -79,3 +81,52 @@ export function globToRegex(glob: string): RegExp {
tokens.push('$');
return new RegExp(tokens.join(''));
}
function isRegExp(obj: any): obj is RegExp {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
}
export type URLMatch = string | RegExp | ((url: URL) => boolean);
export function urlMatchesEqual(match1: URLMatch, match2: URLMatch) {
if (isRegExp(match1) && isRegExp(match2))
return match1.source === match2.source && match1.flags === match2.flags;
return match1 === match2;
}
export function urlMatches(baseURL: string | undefined, urlString: string, match: URLMatch | undefined): boolean {
if (match === undefined || match === '')
return true;
if (isString(match) && !match.startsWith('*'))
match = constructURLBasedOnBaseURL(baseURL, match);
if (isString(match))
match = globToRegex(match);
if (isRegExp(match))
return match.test(urlString);
if (typeof match === 'string' && match === urlString)
return true;
const url = parsedURL(urlString);
if (!url)
return false;
if (typeof match === 'string')
return url.pathname === match;
if (typeof match !== 'function')
throw new Error('url parameter should be string, RegExp or function');
return match(url);
}
function parsedURL(url: string): URL | null {
try {
return new URL(url);
} catch (e) {
return null;
}
}
export function constructURLBasedOnBaseURL(baseURL: string | undefined, givenURL: string): string {
try {
return (new URL(givenURL, baseURL)).toString();
} catch (e) {
return givenURL;
}
}

View file

@ -22,9 +22,6 @@ import type net from 'net';
import { getProxyForUrl } from '../utilsBundle';
import { HttpsProxyAgent } from '../utilsBundle';
import url from 'url';
import type { URLMatch } from '../common/types';
import { isString, isRegExp } from './rtti';
import { globToRegex } from './glob';
import { httpHappyEyeballsAgent, httpsHappyEyeballsAgent } from './happy-eyeballs';
export type HTTPRequestParams = {
@ -111,49 +108,6 @@ export function fetchData(params: HTTPRequestParams, onError?: (params: HTTPRequ
});
}
export function urlMatchesEqual(match1: URLMatch, match2: URLMatch) {
if (isRegExp(match1) && isRegExp(match2))
return match1.source === match2.source && match1.flags === match2.flags;
return match1 === match2;
}
export function urlMatches(baseURL: string | undefined, urlString: string, match: URLMatch | undefined): boolean {
if (match === undefined || match === '')
return true;
if (isString(match) && !match.startsWith('*'))
match = constructURLBasedOnBaseURL(baseURL, match);
if (isString(match))
match = globToRegex(match);
if (isRegExp(match))
return match.test(urlString);
if (typeof match === 'string' && match === urlString)
return true;
const url = parsedURL(urlString);
if (!url)
return false;
if (typeof match === 'string')
return url.pathname === match;
if (typeof match !== 'function')
throw new Error('url parameter should be string, RegExp or function');
return match(url);
}
function parsedURL(url: string): URL | null {
try {
return new URL(url);
} catch (e) {
return null;
}
}
export function constructURLBasedOnBaseURL(baseURL: string | undefined, givenURL: string): string {
try {
return (new URL(givenURL, baseURL)).toString();
} catch (e) {
return givenURL;
}
}
export function createHttpServer(requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void): http.Server;
export function createHttpServer(options: http.ServerOptions, requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void): http.Server;
export function createHttpServer(...args: any[]): http.Server {

View file

@ -16,7 +16,7 @@
*/
import { test as it, expect } from './pageTest';
import { globToRegex } from '../../packages/playwright-core/lib/utils/glob';
import { globToRegex } from '../../packages/playwright-core/lib/utils/isomorphic/urlMatch';
import vm from 'vm';
it('should work with navigation @smoke', async ({ page, server }) => {