Applied no-unnecessary-condition rule
This commit is contained in:
parent
b77146632b
commit
e029e78ebe
|
|
@ -14,6 +14,9 @@ module.exports = {
|
|||
settings: {
|
||||
react: { version: "18" }
|
||||
},
|
||||
ignorePatterns: [
|
||||
'/packages/playwright/bundles/expect/third_party/**/*'
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ['./examples/**/*'],
|
||||
|
|
@ -34,7 +37,7 @@ module.exports = {
|
|||
rules: {
|
||||
"@typescript-eslint/no-unused-vars": [2, {args: "none"}],
|
||||
"@typescript-eslint/consistent-type-imports": [2, {disallowTypeAnnotations: false}],
|
||||
// "@typescript-eslint/no-unnecessary-condition": [2],
|
||||
"@typescript-eslint/no-unnecessary-condition": [2],
|
||||
|
||||
/**
|
||||
* Enforced rules
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export const ReportView: React.FC<{
|
|||
return <div className='htmlreport vbox px-4 pb-4'>
|
||||
<main>
|
||||
{report?.json() && <HeaderView stats={report.json().stats} filterText={filterText} setFilterText={setFilterText}></HeaderView>}
|
||||
{report?.json().metadata && <MetadataView {...report?.json().metadata as Metainfo} />}
|
||||
{report?.json().metadata && <MetadataView {...report.json().metadata as Metainfo} />}
|
||||
<Route predicate={testFilesRoutePredicate}>
|
||||
<TestFilesHeader report={report?.json()} filteredStats={filteredStats} />
|
||||
<TestFilesView
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export const TestCaseView: React.FC<{
|
|||
}, [test]);
|
||||
|
||||
const visibleAnnotations = React.useMemo(() => {
|
||||
return test?.annotations?.filter(annotation => !annotation.type.startsWith('_')) || [];
|
||||
return test?.annotations.filter(annotation => !annotation.type.startsWith('_')) || [];
|
||||
}, [test?.annotations]);
|
||||
|
||||
return <div className='test-case-column vbox'>
|
||||
|
|
@ -58,10 +58,10 @@ export const TestCaseView: React.FC<{
|
|||
<div style={{ width: 10 }}></div>
|
||||
<div className={clsx(!next && 'hidden')}><Link href={testResultHref({ test: next }) + filterParam}>next »</Link></div>
|
||||
</div>}
|
||||
{test && <div className='test-case-title'>{test?.title}</div>}
|
||||
{test && <div className='test-case-title'>{test.title}</div>}
|
||||
{test && <div className='hbox'>
|
||||
<div className='test-case-location'>
|
||||
<CopyToClipboardContainer value={`${test?.location.file}:${test?.location.line}`}>
|
||||
<CopyToClipboardContainer value={`${test.location.file}:${test.location.line}`}>
|
||||
{test.location.file}:{test.location.line}
|
||||
</CopyToClipboardContainer>
|
||||
</div>
|
||||
|
|
@ -69,7 +69,7 @@ export const TestCaseView: React.FC<{
|
|||
<div className='test-case-duration'>{msToString(test.duration)}</div>
|
||||
</div>}
|
||||
{test && (!!test.projectName || labels) && <div className='test-case-project-labels-row'>
|
||||
{test && !!test.projectName && <ProjectLink projectNames={projectNames} projectName={test.projectName}></ProjectLink>}
|
||||
{!!test.projectName && <ProjectLink projectNames={projectNames} projectName={test.projectName}></ProjectLink>}
|
||||
{labels && <LabelsLinkView labels={labels} />}
|
||||
</div>}
|
||||
{!!visibleAnnotations.length && <AutoChip header='Annotations'>
|
||||
|
|
@ -80,7 +80,7 @@ export const TestCaseView: React.FC<{
|
|||
id: String(index),
|
||||
title: <div style={{ display: 'flex', alignItems: 'center' }}>{statusIcon(result.status)} {retryLabel(index)}</div>,
|
||||
render: () => <TestResultView test={test!} result={result} />
|
||||
})) || []} selectedTab={String(selectedResultIndex)} setSelectedTab={id => setSelectedResultIndex(+id)} />}
|
||||
}))} selectedTab={String(selectedResultIndex)} setSelectedTab={id => setSelectedResultIndex(+id)} />}
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -86,9 +86,9 @@ function videoBadge(test: TestCaseSummary): JSX.Element | undefined {
|
|||
return resultWithVideo ? <Link href={testResultHref({ test, result: resultWithVideo, anchor: 'attachment-video' })} title='View video' className='test-file-badge'>{video()}</Link> : undefined;
|
||||
}
|
||||
|
||||
function traceBadge(test: TestCaseSummary): JSX.Element | undefined {
|
||||
function traceBadge(test: TestCaseSummary): JSX.Element {
|
||||
const firstTraces = test.results.map(result => result.attachments.filter(attachment => attachment.name === 'trace')).filter(traces => traces.length > 0)[0];
|
||||
return firstTraces ? <Link href={generateTraceUrl(firstTraces)} title='View trace' className='test-file-badge'>{trace()}</Link> : undefined;
|
||||
return <Link href={generateTraceUrl(firstTraces)} title='View trace' className='test-file-badge'>{trace()}</Link>;
|
||||
}
|
||||
|
||||
const LabelsClickView: React.FC<React.PropsWithChildren<{
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ export const TestFilesHeader: React.FC<{
|
|||
{report.projectNames.length === 1 && !!report.projectNames[0] && <div data-testid='project-name' style={{ color: 'var(--color-fg-subtle)' }}>Project: {report.projectNames[0]}</div>}
|
||||
{filteredStats && <div data-testid='filtered-tests-count' style={{ color: 'var(--color-fg-subtle)', padding: '0 10px' }}>Filtered: {filteredStats.total} {!!filteredStats.total && ('(' + msToString(filteredStats.duration) + ')')}</div>}
|
||||
<div style={{ flex: 'auto' }}></div>
|
||||
<div data-testid='overall-time' style={{ color: 'var(--color-fg-subtle)', marginRight: '10px' }}>{report ? new Date(report.startTime).toLocaleString() : ''}</div>
|
||||
<div data-testid='overall-duration' style={{ color: 'var(--color-fg-subtle)' }}>Total time: {msToString(report.duration ?? 0)}</div>
|
||||
<div data-testid='overall-time' style={{ color: 'var(--color-fg-subtle)', marginRight: '10px' }}>{new Date(report.startTime).toLocaleString()}</div>
|
||||
<div data-testid='overall-duration' style={{ color: 'var(--color-fg-subtle)' }}>Total time: {msToString(report.duration)}</div>
|
||||
</div>
|
||||
{!!report.errors.length && <AutoChip header='Errors' dataTestId='report-errors'>
|
||||
{report.errors.map((error, index) => <TestErrorView key={'test-report-error-message-' + index} error={error}></TestErrorView>)}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export const TestResultView: React.FC<{
|
|||
result: TestResult,
|
||||
}> = ({ test, result }) => {
|
||||
const { screenshots, videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors } = React.useMemo(() => {
|
||||
const attachments = result?.attachments || [];
|
||||
const attachments = result.attachments;
|
||||
const screenshots = new Set(attachments.filter(a => a.contentType.startsWith('image/')));
|
||||
const screenshotAnchors = [...screenshots].map(a => `attachment-${a.name}`);
|
||||
const videos = attachments.filter(a => a.contentType.startsWith('video/'));
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
|
|||
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
|
||||
const playwright = createPlaywright({ sdkLanguage: 'javascript', isServer: true });
|
||||
// TODO: enable socks proxy once ipv6 is supported.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const socksProxy = false ? new SocksProxy() : undefined;
|
||||
playwright.options.socksProxyPort = await socksProxy?.listen(0);
|
||||
|
||||
|
|
|
|||
|
|
@ -116,9 +116,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
const pageObject = Page.from(page);
|
||||
const parsedError = parseError(error);
|
||||
this.emit(Events.BrowserContext.WebError, new WebError(pageObject, parsedError));
|
||||
if (pageObject) {
|
||||
pageObject.emit(Events.Page.PageError, parsedError);
|
||||
}
|
||||
pageObject.emit(Events.Page.PageError, parsedError);
|
||||
});
|
||||
this._channel.on('dialog', ({ dialog }) => {
|
||||
const dialogObject = Dialog.from(dialog);
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
|||
let closeError: string | undefined;
|
||||
const onPipeClosed = (reason?: string) => {
|
||||
// Emulate all pages, contexts and the browser closing upon disconnect.
|
||||
for (const context of browser.contexts() || []) {
|
||||
for (const context of browser.contexts()) {
|
||||
for (const page of context.pages()) {
|
||||
page._onClose();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ export class Connection extends EventEmitter {
|
|||
}
|
||||
const location = frames[0] ? { file: frames[0].file, line: frames[0].line, column: frames[0].column } : undefined;
|
||||
const metadata: channels.Metadata = { apiName, location, internal: !apiName, stepId };
|
||||
if (this._tracingCount && frames && type !== 'LocalUtils') {
|
||||
if (this._tracingCount && type !== 'LocalUtils') {
|
||||
this._localUtils?._channel.addStackToTracingNoReply({ callData: { stack: frames, id } }).catch(() => {});
|
||||
}
|
||||
// We need to exit zones before calling into the server, otherwise
|
||||
|
|
@ -217,7 +217,7 @@ export class Connection extends EventEmitter {
|
|||
|
||||
private _tChannelImplFromWire(names: '*' | string[], arg: any, path: string, context: ValidatorContext) {
|
||||
if (arg && typeof arg === 'object' && typeof arg.guid === 'string') {
|
||||
const object = this._objects.get(arg.guid)!;
|
||||
const object = this._objects.get(arg.guid);
|
||||
if (!object) {
|
||||
throw new Error(`Object with guid ${arg.guid} was not bound in the connection`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export class ConsoleMessage implements api.ConsoleMessage {
|
|||
private _event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent;
|
||||
|
||||
constructor(event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent) {
|
||||
this._page = ('page' in event && event.page) ? Page.from(event.page) : null;
|
||||
this._page = 'page' in event ? Page.from(event.page) : null;
|
||||
this._event = event;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ export function convertSelectOptionValues(values: string | api.ElementHandle | S
|
|||
return {};
|
||||
}
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
assert(values[i] !== null, `options[${i}]: expected object, got null`);
|
||||
}
|
||||
if (values[0] instanceof ElementHandle) {
|
||||
|
|
@ -262,6 +263,7 @@ export function convertSelectOptionValues(values: string | api.ElementHandle | S
|
|||
type SetInputFilesFiles = Pick<channels.ElementHandleSetInputFilesParams, 'payloads' | 'localPaths' | 'localDirectory' | 'streams' | 'directoryStream'>;
|
||||
|
||||
function filePayloadExceedsSizeLimit(payloads: FilePayload[]) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= fileUploadSizeLimit;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ export class EventEmitter implements EventEmitterType {
|
|||
}
|
||||
|
||||
const handler = events[type];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (handler === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -120,6 +121,7 @@ export class EventEmitter implements EventEmitterType {
|
|||
} else {
|
||||
// To avoid recursion in the case that type === "newListener"! Before
|
||||
// adding it to the listeners, first emit "newListener".
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (events.newListener !== undefined) {
|
||||
this.emit('newListener', type, unwrapListener(listener));
|
||||
|
||||
|
|
@ -194,6 +196,7 @@ export class EventEmitter implements EventEmitterType {
|
|||
}
|
||||
|
||||
const list = events[type];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (list === undefined) {
|
||||
return this;
|
||||
}
|
||||
|
|
@ -203,6 +206,7 @@ export class EventEmitter implements EventEmitterType {
|
|||
this._events = Object.create(null);
|
||||
} else {
|
||||
delete events[type];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (events.removeListener) {
|
||||
this.emit('removeListener', type, (list as any).listener ?? listener);
|
||||
}
|
||||
|
|
@ -233,6 +237,7 @@ export class EventEmitter implements EventEmitterType {
|
|||
events[type] = list[0];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (events.removeListener !== undefined) {
|
||||
this.emit('removeListener', type, originalListener || listener);
|
||||
}
|
||||
|
|
@ -280,10 +285,12 @@ export class EventEmitter implements EventEmitterType {
|
|||
}
|
||||
|
||||
// not listening for removeListener, no need to emit
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!events.removeListener) {
|
||||
if (type === undefined) {
|
||||
this._events = Object.create(null);
|
||||
this._eventsCount = 0;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} else if (events[type] !== undefined) {
|
||||
if (--this._eventsCount === 0) {
|
||||
this._events = Object.create(null);
|
||||
|
|
@ -315,6 +322,7 @@ export class EventEmitter implements EventEmitterType {
|
|||
|
||||
if (typeof listeners === 'function') {
|
||||
this.removeListener(type, listeners);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} else if (listeners !== undefined) {
|
||||
// LIFO order
|
||||
for (let i = listeners.length - 1; i >= 0; i--) {
|
||||
|
|
@ -338,6 +346,7 @@ export class EventEmitter implements EventEmitterType {
|
|||
if (typeof listener === 'function') {
|
||||
return 1;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (listener !== undefined) {
|
||||
return listener.length;
|
||||
}
|
||||
|
|
@ -370,6 +379,7 @@ export class EventEmitter implements EventEmitterType {
|
|||
}
|
||||
|
||||
const listener = events[type];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (listener === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -421,6 +431,6 @@ function unwrapListeners(arr: Listener[]): Listener[] {
|
|||
return arr.map(l => wrappedListener(l) ?? l);
|
||||
}
|
||||
|
||||
function wrappedListener(l: Listener): Listener {
|
||||
function wrappedListener(l: Listener): Listener | undefined {
|
||||
return (l as any).listener;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
|||
throw new Error(`Unexpected 'data' type`);
|
||||
}
|
||||
} else if (options.form) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (globalThis.FormData && options.form instanceof FormData) {
|
||||
formData = [];
|
||||
for (const [name, value] of options.form.entries()) {
|
||||
|
|
@ -219,6 +220,7 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
|||
}
|
||||
} else if (options.multipart) {
|
||||
multipartData = [];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (globalThis.FormData && options.multipart instanceof FormData) {
|
||||
const form = options.multipart;
|
||||
for (const [name, value] of form.entries()) {
|
||||
|
|
|
|||
|
|
@ -444,6 +444,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
|
|||
|
||||
async waitForFunction<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg, options: WaitForFunctionOptions = {}): Promise<structs.SmartHandle<R>> {
|
||||
if (typeof options.polling === 'string') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
assert(options.polling === 'raf', 'Unknown polling option: ' + options.polling);
|
||||
}
|
||||
const result = await this._channel.waitForFunction({
|
||||
|
|
|
|||
|
|
@ -945,6 +945,7 @@ export class RawHeaders {
|
|||
static _fromHeadersObjectLossy(headers: Headers): RawHeaders {
|
||||
const headersArray: HeadersArray = Object.entries(headers).map(([name, value]) => ({
|
||||
name, value
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
})).filter(header => header.value !== undefined);
|
||||
return new RawHeaders(headersArray);
|
||||
}
|
||||
|
|
@ -958,7 +959,7 @@ export class RawHeaders {
|
|||
|
||||
get(name: string): string | null {
|
||||
const values = this.getAll(name);
|
||||
if (!values || !values.length) {
|
||||
if (!values.length) {
|
||||
return null;
|
||||
}
|
||||
return values.join(name.toLowerCase() === 'set-cookie' ? '\n' : ', ');
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
|
|||
this._bidiChromium._playwright = this;
|
||||
this._bidiFirefox = BrowserType.from(initializer.bidiFirefox);
|
||||
this._bidiFirefox._playwright = this;
|
||||
this.devices = this._connection.localUtils().devices ?? {};
|
||||
this.devices = this._connection.localUtils().devices;
|
||||
this.selectors = new Selectors();
|
||||
this.errors = { TimeoutError };
|
||||
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ class SocksConnection {
|
|||
|
||||
private async _readBytes(length: number): Promise<Buffer> {
|
||||
this._fence = this._offset + length;
|
||||
if (!this._buffer || this._buffer.length < this._fence) {
|
||||
if (this._buffer.length < this._fence) {
|
||||
await new Promise<void>(f => this._fenceCallback = f);
|
||||
}
|
||||
this._offset += length;
|
||||
|
|
|
|||
|
|
@ -20,4 +20,10 @@ export type Rect = Size & Point;
|
|||
export type Quad = [ Point, Point, Point, Point ];
|
||||
export type TimeoutOptions = { timeout?: number };
|
||||
export type NameValue = { name: string, value: string };
|
||||
export type HeadersArray = NameValue[];
|
||||
export type HeadersArray = NameValue[];
|
||||
|
||||
export const assertUnreachable = (x: never): never => assertUnreachableWithError(x, new Error('Unreachable variant'));
|
||||
|
||||
export const assertUnreachableWithError = (x: never, error: Error): never => {
|
||||
throw error;
|
||||
};
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
import type { SerializedValue } from '@protocol/channels';
|
||||
import { assertUnreachable } from '../common/types';
|
||||
|
||||
export function parseSerializedValue(value: SerializedValue, handles: any[] | undefined): any {
|
||||
return innerParseSerializedValue(value, handles, new Map());
|
||||
|
|
@ -34,23 +35,28 @@ function innerParseSerializedValue(value: SerializedValue, handles: any[] | unde
|
|||
return value.b;
|
||||
}
|
||||
if (value.v !== undefined) {
|
||||
if (value.v === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
if (value.v === 'null') {
|
||||
return null;
|
||||
}
|
||||
if (value.v === 'NaN') {
|
||||
return NaN;
|
||||
}
|
||||
if (value.v === 'Infinity') {
|
||||
return Infinity;
|
||||
}
|
||||
if (value.v === '-Infinity') {
|
||||
return -Infinity;
|
||||
}
|
||||
if (value.v === '-0') {
|
||||
return -0;
|
||||
switch (value.v) {
|
||||
case 'undefined': {
|
||||
return undefined;
|
||||
}
|
||||
case 'null': {
|
||||
return null;
|
||||
}
|
||||
case 'NaN': {
|
||||
return NaN;
|
||||
}
|
||||
case 'Infinity': {
|
||||
return Infinity;
|
||||
}
|
||||
case '-Infinity': {
|
||||
return -Infinity;
|
||||
}
|
||||
case '-0': {
|
||||
return -0;
|
||||
}
|
||||
default: {
|
||||
return assertUnreachable(value.v);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value.d !== undefined) {
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ export class PipeTransport {
|
|||
|
||||
_dispatch(buffer: Buffer) {
|
||||
this._data = Buffer.concat([this._data, buffer]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
if (!this._bytesLeft && this._data.length < 4) {
|
||||
// Need more data.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertUnreachableWithError } from '../common/types';
|
||||
import { isUnderTest } from '../utils';
|
||||
|
||||
export class ValidationError extends Error {}
|
||||
|
|
@ -67,28 +68,32 @@ export const tString: Validator = (arg: any, path: string, context: ValidatorCon
|
|||
throw new ValidationError(`${path}: expected string, got ${typeof arg}`);
|
||||
};
|
||||
export const tBinary: Validator = (arg: any, path: string, context: ValidatorContext) => {
|
||||
if (context.binary === 'fromBase64') {
|
||||
if (arg instanceof String) {
|
||||
return Buffer.from(arg.valueOf(), 'base64');
|
||||
switch (context.binary) {
|
||||
case 'fromBase64': {
|
||||
if (arg instanceof String) {
|
||||
return Buffer.from(arg.valueOf(), 'base64');
|
||||
}
|
||||
if (typeof arg === 'string') {
|
||||
return Buffer.from(arg, 'base64');
|
||||
}
|
||||
throw new ValidationError(`${path}: expected base64-encoded buffer, got ${typeof arg}`);
|
||||
}
|
||||
if (typeof arg === 'string') {
|
||||
return Buffer.from(arg, 'base64');
|
||||
case 'toBase64': {
|
||||
if (!(arg instanceof Buffer)) {
|
||||
throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`);
|
||||
}
|
||||
return (arg as Buffer).toString('base64');
|
||||
}
|
||||
case 'buffer': {
|
||||
if (!(arg instanceof Buffer)) {
|
||||
throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
default: {
|
||||
return assertUnreachableWithError(context.binary, new ValidationError(`Unsupported binary behavior "${context.binary}"`));
|
||||
}
|
||||
throw new ValidationError(`${path}: expected base64-encoded buffer, got ${typeof arg}`);
|
||||
}
|
||||
if (context.binary === 'toBase64') {
|
||||
if (!(arg instanceof Buffer)) {
|
||||
throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`);
|
||||
}
|
||||
return (arg as Buffer).toString('base64');
|
||||
}
|
||||
if (context.binary === 'buffer') {
|
||||
if (!(arg instanceof Buffer)) {
|
||||
throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
throw new ValidationError(`Unsupported binary behavior "${context.binary}"`);
|
||||
};
|
||||
export const tUndefined: Validator = (arg: any, path: string, context: ValidatorContext) => {
|
||||
if (Object.is(arg, undefined)) {
|
||||
|
|
@ -156,6 +161,7 @@ export const tChannel = (names: '*' | string[]): Validator => {
|
|||
export const tType = (name: string): Validator => {
|
||||
return (arg: any, path: string, context: ValidatorContext) => {
|
||||
const v = scheme[name];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!v) {
|
||||
throw new ValidationError(path + ': unknown type "' + name + '"');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { DebugControllerDispatcher } from '../server/dispatchers/debugController
|
|||
import { startProfiling, stopProfiling } from '../utils';
|
||||
import { monotonicTime } from '../utils';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { assertUnreachableWithError } from '../common/types';
|
||||
|
||||
export type ClientType = 'controller' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android';
|
||||
|
||||
|
|
@ -108,16 +109,20 @@ export class PlaywrightConnection {
|
|||
|
||||
this._root = new RootDispatcher(this._dispatcherConnection, async (scope, options) => {
|
||||
await startProfiling();
|
||||
if (clientType === 'reuse-browser') {
|
||||
return await this._initReuseBrowsersMode(scope);
|
||||
switch (clientType) {
|
||||
case 'reuse-browser': {
|
||||
return await this._initReuseBrowsersMode(scope);
|
||||
}
|
||||
case 'pre-launched-browser-or-android': {
|
||||
return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope) : await this._initPreLaunchedAndroidMode(scope);
|
||||
}
|
||||
case 'launch-browser': {
|
||||
return await this._initLaunchBrowserMode(scope, options);
|
||||
}
|
||||
default: {
|
||||
return assertUnreachableWithError(clientType, new Error('Unsupported client type: ' + clientType));
|
||||
}
|
||||
}
|
||||
if (clientType === 'pre-launched-browser-or-android') {
|
||||
return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope) : await this._initPreLaunchedAndroidMode(scope);
|
||||
}
|
||||
if (clientType === 'launch-browser') {
|
||||
return await this._initLaunchBrowserMode(scope, options);
|
||||
}
|
||||
throw new Error('Unsupported client type: ' + clientType);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -233,9 +238,6 @@ export class PlaywrightConnection {
|
|||
await context.stopPendingOperations('Connection closed');
|
||||
}
|
||||
}
|
||||
if (!browser.contexts()) {
|
||||
await browser.close({ reason: 'Connection terminated' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ export class AndroidDevice extends SdkObject {
|
|||
const artifactsDir = await fs.promises.mkdtemp(ARTIFACTS_FOLDER);
|
||||
const cleanupArtifactsDir = async () => {
|
||||
const errors = await removeFolders([artifactsDir]);
|
||||
for (let i = 0; i < (errors || []).length; ++i) {
|
||||
for (let i = 0; i < errors.length; ++i) {
|
||||
debug('pw:android')(`exception while removing ${artifactsDir}: ${errors[i]}`);
|
||||
}
|
||||
};
|
||||
|
|
@ -438,6 +438,7 @@ export class AndroidDevice extends SdkObject {
|
|||
}
|
||||
|
||||
const pkg = await this._extractPkg(pid);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (this._isClosed) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,9 +159,9 @@ export class BidiBrowser extends Browser {
|
|||
}
|
||||
return;
|
||||
}
|
||||
let context = this._contexts.get(event.userContext);
|
||||
let context: BidiBrowserContext | undefined | null = this._contexts.get(event.userContext);
|
||||
if (!context) {
|
||||
context = this._defaultContext as BidiBrowserContext;
|
||||
context = this._defaultContext as BidiBrowserContext | null;
|
||||
}
|
||||
if (!context) {
|
||||
return;
|
||||
|
|
@ -240,6 +240,7 @@ export class BidiBrowserContext extends BrowserContext {
|
|||
httpOnly: c.httpOnly,
|
||||
secure: c.secure,
|
||||
expires: c.expiry ?? -1,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
sameSite: c.sameSite ? fromBidiSameSite(c.sameSite) : 'None',
|
||||
};
|
||||
return copy;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertUnreachableWithError } from 'playwright-core/lib/common/types';
|
||||
import { parseEvaluationResultValue } from '../isomorphic/utilityScriptSerializers';
|
||||
import * as js from '../javascript';
|
||||
import type { BidiSession } from './bidiConnection';
|
||||
|
|
@ -51,13 +52,17 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate {
|
|||
awaitPromise: true,
|
||||
userActivation: true,
|
||||
});
|
||||
if (response.type === 'success') {
|
||||
return BidiDeserializer.deserialize(response.result);
|
||||
switch (response.type) {
|
||||
case 'success': {
|
||||
return BidiDeserializer.deserialize(response.result);
|
||||
}
|
||||
case 'exception': {
|
||||
throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
}
|
||||
default: {
|
||||
return assertUnreachableWithError(response, new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response)));
|
||||
}
|
||||
}
|
||||
if (response.type === 'exception') {
|
||||
throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
}
|
||||
throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response));
|
||||
}
|
||||
|
||||
async rawEvaluateHandle(expression: string): Promise<js.ObjectId> {
|
||||
|
|
@ -69,16 +74,20 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate {
|
|||
awaitPromise: true,
|
||||
userActivation: true,
|
||||
});
|
||||
if (response.type === 'success') {
|
||||
if ('handle' in response.result) {
|
||||
return response.result.handle!;
|
||||
switch (response.type) {
|
||||
case 'success': {
|
||||
if ('handle' in response.result) {
|
||||
return response.result.handle!;
|
||||
}
|
||||
throw new js.JavaScriptErrorInEvaluate('Cannot get handle: ' + JSON.stringify(response.result));
|
||||
}
|
||||
case 'exception': {
|
||||
throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
}
|
||||
default: {
|
||||
return assertUnreachableWithError(response, new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response)));
|
||||
}
|
||||
throw new js.JavaScriptErrorInEvaluate('Cannot get handle: ' + JSON.stringify(response.result));
|
||||
}
|
||||
if (response.type === 'exception') {
|
||||
throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
}
|
||||
throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response));
|
||||
}
|
||||
|
||||
async evaluateWithArguments(functionDeclaration: string, returnByValue: boolean, utilityScript: js.JSHandle<any>, values: any[], objectIds: string[]): Promise<any> {
|
||||
|
|
@ -95,17 +104,21 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate {
|
|||
awaitPromise: true,
|
||||
userActivation: true,
|
||||
});
|
||||
if (response.type === 'exception') {
|
||||
throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
}
|
||||
if (response.type === 'success') {
|
||||
if (returnByValue) {
|
||||
return parseEvaluationResultValue(BidiDeserializer.deserialize(response.result));
|
||||
switch (response.type) {
|
||||
case 'success': {
|
||||
if (returnByValue) {
|
||||
return parseEvaluationResultValue(BidiDeserializer.deserialize(response.result));
|
||||
}
|
||||
const objectId = 'handle' in response.result ? response.result.handle : undefined ;
|
||||
return utilityScript._context.createHandle({ objectId, ...response.result });
|
||||
}
|
||||
case 'exception': {
|
||||
throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
}
|
||||
default: {
|
||||
return assertUnreachableWithError(response, new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response)));
|
||||
}
|
||||
const objectId = 'handle' in response.result ? response.result.handle : undefined ;
|
||||
return utilityScript._context.createHandle({ objectId, ...response.result });
|
||||
}
|
||||
throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response));
|
||||
}
|
||||
|
||||
async getProperties(context: js.ExecutionContext, objectId: js.ObjectId): Promise<Map<string, js.JSHandle>> {
|
||||
|
|
@ -134,13 +147,17 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate {
|
|||
awaitPromise: true,
|
||||
userActivation: true,
|
||||
});
|
||||
if (response.type === 'exception') {
|
||||
throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
switch (response.type) {
|
||||
case 'success': {
|
||||
return response.result;
|
||||
}
|
||||
case 'exception': {
|
||||
throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
}
|
||||
default: {
|
||||
return assertUnreachableWithError(response, new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response)));
|
||||
}
|
||||
}
|
||||
if (response.type === 'success') {
|
||||
return response.result;
|
||||
}
|
||||
throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ class BidiRouteImpl implements network.RouteDelegate {
|
|||
async continue(overrides: types.NormalizedContinueOverrides) {
|
||||
// Firefox does not update content-length header.
|
||||
let headers = overrides.headers || this._request.headers();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (overrides.postData && headers) {
|
||||
headers = headers.map(header => {
|
||||
if (header.name.toLowerCase() === 'content-length') {
|
||||
|
|
@ -342,14 +343,17 @@ function toBidiHeaders(headers: types.HeadersArray): bidi.Network.Header[] {
|
|||
}
|
||||
|
||||
export function bidiBytesValueToString(value: bidi.Network.BytesValue): string {
|
||||
if (value.type === 'string') {
|
||||
return value.value;
|
||||
switch (value.type) {
|
||||
case 'string': {
|
||||
return value.value;
|
||||
}
|
||||
case 'base64': {
|
||||
return Buffer.from(value.type, 'base64').toString('binary');
|
||||
}
|
||||
default: {
|
||||
return 'unknown value type: ' + (value as any).type;
|
||||
}
|
||||
}
|
||||
if (value.type === 'base64') {
|
||||
return Buffer.from(value.type, 'base64').toString('binary');
|
||||
}
|
||||
return 'unknown value type: ' + (value as any).type;
|
||||
|
||||
}
|
||||
|
||||
function toBidiSameSite(sameSite?: 'Strict' | 'Lax' | 'None'): bidi.Network.SameSite | undefined {
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ export class BidiPage implements PageDelegate {
|
|||
if (url.startsWith('file:') || url.startsWith('data:') || url === 'about:blank') {
|
||||
// Navigation to file urls doesn't emit network events, so we fire 'commit' event right when navigation is started.
|
||||
// Doing it in domcontentload would be too late as we'd clear frame tree.
|
||||
const frame = this._page._frameManager.frame(frameId)!;
|
||||
const frame = this._page._frameManager.frame(frameId);
|
||||
if (frame) {
|
||||
this._page._frameManager.frameCommittedNewDocumentNavigation(frameId, params.url, '', params.navigation!, /* initial */ false);
|
||||
}
|
||||
|
|
@ -514,6 +514,7 @@ export class BidiPage implements PageDelegate {
|
|||
return 'error:notconnected';
|
||||
}
|
||||
const rects = node.getClientRects();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!rects) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import type * as Bidi from './bidiProtocol';
|
|||
*/
|
||||
export class BidiDeserializer {
|
||||
static deserialize(result: Bidi.Script.RemoteValue): any {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||
await this._cancelAllRoutesInFlight();
|
||||
|
||||
// Close extra pages early.
|
||||
let page: Page | undefined = this.pages()[0];
|
||||
let page = this.pages()[0] as Page | undefined;
|
||||
const [, ...otherPages] = this.pages();
|
||||
for (const p of otherPages) {
|
||||
await p.close(metadata);
|
||||
|
|
@ -391,7 +391,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||
// Race against BrowserContext.close
|
||||
await Promise.race([waitForEvent.promise, this._closePromise]);
|
||||
}
|
||||
const page = this.possiblyUninitializedPages()[0];
|
||||
const page = this.possiblyUninitializedPages()[0] as Page | undefined;
|
||||
if (!page) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -593,7 +593,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||
if (!oldOrigins.size && !newOrigins.size) {
|
||||
return;
|
||||
}
|
||||
let page = this.pages()[0];
|
||||
let page = this.pages()[0] as Page | undefined;
|
||||
|
||||
const internalMetadata = serverSideCallMetadata();
|
||||
page = page || await this.newPage({
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ export class Chromium extends BrowserType {
|
|||
|
||||
if (!headersMap) {
|
||||
headersMap = { 'User-Agent': getUserAgent() };
|
||||
} else if (headersMap && !Object.keys(headersMap).some(key => key.toLowerCase() === 'user-agent')) {
|
||||
} else if (!Object.keys(headersMap).some(key => key.toLowerCase() === 'user-agent')) {
|
||||
headersMap['User-Agent'] = getUserAgent();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ export class CRBrowser extends Browser {
|
|||
if (!context) {
|
||||
// TODO: auto attach only to pages from our contexts.
|
||||
// assert(this._defaultContext);
|
||||
context = this._defaultContext as CRBrowserContext;
|
||||
context = this._defaultContext as CRBrowserContext | null;
|
||||
}
|
||||
|
||||
if (targetInfo.type === 'other' && targetInfo.url.startsWith('devtools://devtools') && this._devtools) {
|
||||
|
|
|
|||
|
|
@ -416,7 +416,7 @@ requestPausedSessionInfo!.session._sendMayFail('Fetch.continueRequest', { reques
|
|||
}
|
||||
return Buffer.concat(chunks);
|
||||
};
|
||||
const timingPayload = responsePayload.timing!;
|
||||
const timingPayload = responsePayload.timing;
|
||||
let timing: network.ResourceTiming;
|
||||
if (timingPayload && !this._responseExtraInfoTracker.servedFromCache(request._requestId)) {
|
||||
timing = {
|
||||
|
|
@ -828,7 +828,7 @@ class ResponseExtraInfoTracker {
|
|||
}
|
||||
|
||||
private _patchHeaders(info: RequestInfo, index: number) {
|
||||
const response = info.responses[index];
|
||||
const response = info.responses[index] as network.Response | undefined;
|
||||
const requestExtraInfo = info.requestWillBeSentExtraInfo[index];
|
||||
if (response && requestExtraInfo) {
|
||||
response.request().setRawRequestHeaders(headersObjectToArray(requestExtraInfo.headers, '\n'));
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ export class CRPage implements PageDelegate {
|
|||
|
||||
private async _go(delta: number): Promise<boolean> {
|
||||
const history = await this._mainFrameSession._client.send('Page.getNavigationHistory');
|
||||
const entry = history.entries[history.currentIndex + delta];
|
||||
const entry = history.entries[history.currentIndex + delta] as Protocol.Page.NavigationEntry | undefined;
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -639,6 +639,7 @@ class FrameSession {
|
|||
// In this case, we already have a new session for this frame, so events
|
||||
// in the old session should be ignored.
|
||||
const session = this._crPage._sessionForFrame(frame);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return session && session !== this && !session._swappedIn;
|
||||
}
|
||||
|
||||
|
|
@ -1140,6 +1141,7 @@ class FrameSession {
|
|||
const nodeInfo = await this._client.send('DOM.describeNode', {
|
||||
objectId: handle._objectId
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!nodeInfo || typeof nodeInfo.node.frameId !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1150,11 +1152,13 @@ class FrameSession {
|
|||
// document.documentElement has frameId of the owner frame.
|
||||
const documentElement = await handle.evaluateHandle(node => {
|
||||
const doc = node as Document;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (doc.documentElement && doc.documentElement.ownerDocument === doc) {
|
||||
return doc.documentElement;
|
||||
}
|
||||
return node.ownerDocument ? node.ownerDocument.documentElement : null;
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!documentElement) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1164,6 +1168,7 @@ class FrameSession {
|
|||
const nodeInfo = await this._client.send('DOM.describeNode', {
|
||||
objectId: documentElement._objectId
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const frameId = nodeInfo && typeof nodeInfo.node.frameId === 'string' ?
|
||||
nodeInfo.node.frameId : null;
|
||||
documentElement.dispose();
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||
} else if (mode === 'mstest') {
|
||||
this.name = 'MSTest';
|
||||
this.id = 'csharp-mstest';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} else if (mode === 'nunit') {
|
||||
this.name = 'NUnit';
|
||||
this.id = 'csharp-nunit';
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export class JavaLanguageGenerator implements LanguageGenerator {
|
|||
if (mode === 'library') {
|
||||
this.name = 'Library';
|
||||
this.id = 'java';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} else if (mode === 'junit') {
|
||||
this.name = 'JUnit';
|
||||
this.id = 'java-junit';
|
||||
|
|
|
|||
|
|
@ -240,9 +240,6 @@ export class DebugController extends SdkObject {
|
|||
await context.close({ reason: 'Browser collected' });
|
||||
}
|
||||
}
|
||||
if (!browser.contexts()) {
|
||||
await browser.close({ reason: 'Browser collected' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export class ArtifactDispatcher extends Dispatcher<Artifact, channels.ArtifactCh
|
|||
}
|
||||
|
||||
static fromNullable(parentScope: DispatcherScope, artifact: Artifact): ArtifactDispatcher | undefined {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!artifact) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,6 +350,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
|||
|
||||
async harExport(params: channels.BrowserContextHarExportParams): Promise<channels.BrowserContextHarExportResult> {
|
||||
const artifact = await this._context._harExport(params.harId);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!artifact) {
|
||||
throw new Error('No HAR artifact. Ensure record.harPath is set.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -357,6 +357,7 @@ class HarBackend {
|
|||
private async _harFindResponse(url: string, method: string, headers: HeadersArray, postData: Buffer | undefined): Promise<har.Entry | undefined> {
|
||||
const harLog = this._harFile.log;
|
||||
const visited = new Set<har.Entry>();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
const entries: har.Entry[] = [];
|
||||
for (const candidate of harLog.entries) {
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ export class WebSocketRouteDispatcher extends Dispatcher<{ guid: string }, chann
|
|||
}
|
||||
|
||||
function matchesPattern(dispatcher: PageDispatcher | BrowserContextDispatcher, baseURL: string | undefined, url: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
for (const pattern of dispatcher._webSocketInterceptionPatterns || []) {
|
||||
const urlMatch = pattern.regexSource ? new RegExp(pattern.regexSource, pattern.regexFlags) : pattern.glob;
|
||||
if (urlMatches(baseURL, url, urlMatch)) {
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
async isIframeElement(): Promise<boolean | 'error:notconnected'> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return this.evaluateInUtility(([injected, node]) => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME'), {});
|
||||
}
|
||||
|
||||
|
|
@ -497,6 +498,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
let restoreModifiers: types.KeyboardModifier[] | undefined;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (options && options.modifiers) {
|
||||
restoreModifiers = await this._page.keyboard.ensureModifiers(options.modifiers);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -566,6 +566,7 @@ export abstract class APIRequestContext extends SdkObject {
|
|||
|
||||
|
||||
progress.log(`→ ${options.method} ${url.toString()}`);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (options.headers) {
|
||||
for (const [name, value] of Object.entries(options.headers)) {
|
||||
progress.log(` ${name}: ${value}`);
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ export class FFBrowser extends Browser {
|
|||
|
||||
_onAttachedToTarget(payload: Protocol.Browser.attachedToTargetPayload) {
|
||||
const { targetId, browserContextId, openerId, type } = payload.targetInfo;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
assert(type === 'page');
|
||||
const context = browserContextId ? this._contexts.get(browserContextId)! : this._defaultContext as FFBrowserContext;
|
||||
assert(context, `Unknown context id:${browserContextId}, _defaultContext: ${this._defaultContext}`);
|
||||
|
|
|
|||
|
|
@ -38,16 +38,20 @@ function toModifiersMask(modifiers: Set<types.KeyboardModifier>): number {
|
|||
}
|
||||
|
||||
function toButtonNumber(button: types.MouseButton): number {
|
||||
if (button === 'left') {
|
||||
return 0;
|
||||
switch (button) {
|
||||
case 'left': {
|
||||
return 0;
|
||||
}
|
||||
case 'middle': {
|
||||
return 1;
|
||||
}
|
||||
case 'right': {
|
||||
return 2;
|
||||
}
|
||||
default: {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (button === 'middle') {
|
||||
return 1;
|
||||
}
|
||||
if (button === 'right') {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function toButtonsMask(buttons: Set<types.MouseButton>): number {
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ export class FrameSelectors {
|
|||
const targetContext = await resolved.frame._mainContext();
|
||||
const result: Promise<ElementHandle<Element>>[] = [];
|
||||
for (const property of properties.values()) {
|
||||
const elementHandle = property.asElement() as ElementHandle<Element>;
|
||||
const elementHandle = property.asElement() as ElementHandle<Element> | null;
|
||||
if (elementHandle) {
|
||||
result.push(adoptIfNeeded(elementHandle, targetContext));
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ export class FrameManager {
|
|||
}
|
||||
|
||||
createDummyMainFrameIfNeeded() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!this._mainFrame) {
|
||||
this.frameAttached(kDummyFrameId, null);
|
||||
}
|
||||
|
|
@ -143,6 +144,7 @@ export class FrameManager {
|
|||
frameAttached(frameId: string, parentFrameId: string | null | undefined): Frame {
|
||||
const parentFrame = parentFrameId ? this._frames.get(parentFrameId)! : null;
|
||||
if (!parentFrame) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (this._mainFrame) {
|
||||
// Update frame id to retain frame identity on cross-process navigation.
|
||||
this._frames.delete(this._mainFrame._id);
|
||||
|
|
@ -880,7 +882,7 @@ export class Frame extends SdkObject {
|
|||
throw injected.createStacklessError('Element is not attached to the DOM');
|
||||
}
|
||||
const elements = injected.querySelectorAll(info.parsed, root || document);
|
||||
const element: Element | undefined = elements[0];
|
||||
const element = elements[0] as Element | undefined;
|
||||
const visible = element ? injected.utils.isElementVisible(element) : false;
|
||||
let log = '';
|
||||
if (elements.length > 1) {
|
||||
|
|
@ -970,6 +972,7 @@ export class Frame extends SdkObject {
|
|||
if (document.doctype) {
|
||||
retVal = new XMLSerializer().serializeToString(document.doctype);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (document.documentElement) {
|
||||
retVal += document.documentElement.outerHTML;
|
||||
}
|
||||
|
|
@ -1083,6 +1086,7 @@ export class Frame extends SdkObject {
|
|||
let error = null;
|
||||
script.onerror = e => error = e;
|
||||
document.head.appendChild(script);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
|
@ -1864,6 +1868,7 @@ export class Frame extends SdkObject {
|
|||
}
|
||||
|
||||
// Clean Service Workers
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const registrations = navigator.serviceWorker ? await navigator.serviceWorker.getRegistrations() : [];
|
||||
await Promise.all(registrations.map(async r => {
|
||||
// Heuristic for service workers that stalled during main script fetch or importScripts:
|
||||
|
|
@ -1879,6 +1884,7 @@ export class Frame extends SdkObject {
|
|||
}));
|
||||
|
||||
// Clean IndexedDB
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
for (const db of await indexedDB.databases() || []) {
|
||||
// Do not wait for the callback - it is called on timer in Chromium (slow).
|
||||
if (db.name) {
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ function normalizeStringChildren(rootA11yNode: AriaNode) {
|
|||
const visit = (ariaNode: AriaNode) => {
|
||||
const normalizedChildren: (AriaNode | string)[] = [];
|
||||
const buffer: string[] = [];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
for (const child of ariaNode.children || []) {
|
||||
if (typeof child === 'string') {
|
||||
buffer.push(child);
|
||||
|
|
@ -296,6 +297,7 @@ function matchesNode(node: AriaNode | string, template: AriaTemplateNode, depth:
|
|||
if (!matchesName(node.name, template)) {
|
||||
return false;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!containsList(node.children || [], template.children || [], depth)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -335,6 +337,7 @@ function matchesNodeDeep(root: AriaNode, template: AriaTemplateNode, collectAll:
|
|||
if (typeof node === 'string') {
|
||||
return false;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
for (const child of node.children || []) {
|
||||
if (visit(child)) {
|
||||
return true;
|
||||
|
|
@ -414,6 +417,7 @@ export function renderAriaTree(ariaNode: AriaNode, options?: { mode?: 'raw' | 'r
|
|||
}
|
||||
} else {
|
||||
lines.push(escapedKey + ':');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
for (const child of ariaNode.children || []) {
|
||||
visit(child, ariaNode, indent + ' ');
|
||||
}
|
||||
|
|
@ -422,6 +426,7 @@ export function renderAriaTree(ariaNode: AriaNode, options?: { mode?: 'raw' | 'r
|
|||
|
||||
if (ariaNode.role === 'fragment') {
|
||||
// Render fragment.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
for (const child of ariaNode.children || []) {
|
||||
visit(child, ariaNode, '');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ export class ClockController {
|
|||
}
|
||||
|
||||
let firstException: Error | undefined;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
const result = await this._callFirstTimer(to);
|
||||
if (!result.timerFound) {
|
||||
|
|
@ -428,6 +429,7 @@ export class ClockController {
|
|||
isPaused = false;
|
||||
} else if (type === 'setFixedTime') {
|
||||
this._innerSetFixedTime(asWallTime(param!));
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} else if (type === 'setSystemTime') {
|
||||
this._innerSetTime(asWallTime(param!));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export function parentElementOrShadowHost(element: Element): Element | undefined
|
|||
if (!element.parentNode) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (element.parentNode.nodeType === 11 /* Node.DOCUMENT_FRAGMENT_NODE */ && (element.parentNode as ShadowRoot).host) {
|
||||
return (element.parentNode as ShadowRoot).host;
|
||||
}
|
||||
|
|
@ -80,6 +81,7 @@ export function closestCrossShadow(element: Element | undefined, css: string, sc
|
|||
}
|
||||
|
||||
export function getElementComputedStyle(element: Element, pseudo?: string): CSSStyleDeclaration | undefined {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : undefined;
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +96,7 @@ export function isElementStyleVisibilityVisible(element: Element, style?: CSSSty
|
|||
// All the browser implement it, but WebKit has a bug which prevents us from using it:
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=264733
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (Element.prototype.checkVisibility && browserNameForWorkarounds !== 'webkit') {
|
||||
if (!element.checkVisibility()) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ export class Highlight {
|
|||
|
||||
install() {
|
||||
// NOTE: document.documentElement can be null: https://github.com/microsoft/TypeScript/issues/50078
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (this._injectedScript.document.documentElement && !this._injectedScript.document.documentElement.contains(this._glassPaneElement)) {
|
||||
this._injectedScript.document.documentElement.appendChild(this._glassPaneElement);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -271,11 +271,6 @@ export class InjectedScript {
|
|||
throw this.createStacklessError('Node is not queryable.');
|
||||
}
|
||||
|
||||
if (selector.capture !== undefined) {
|
||||
// We should have handled the capture above.
|
||||
throw this.createStacklessError('Internal error: there should not be a capture in the selector.');
|
||||
}
|
||||
|
||||
// Workaround so that ":scope" matches the ShadowRoot.
|
||||
// This is, unfortunately, because an ElementHandle can point to any Node (including ShadowRoot/Document/etc),
|
||||
// and not just to an Element, and we support various APIs on ElementHandle like "textContent()".
|
||||
|
|
@ -531,6 +526,7 @@ export class InjectedScript {
|
|||
}
|
||||
|
||||
describeIFrameStyle(iframe: Element): 'error:notconnected' | 'transformed' | { left: number, top: number } {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!iframe.ownerDocument || !iframe.ownerDocument.defaultView) {
|
||||
return 'error:notconnected';
|
||||
}
|
||||
|
|
@ -687,6 +683,7 @@ export class InjectedScript {
|
|||
return !disabled && !readonly;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (state === 'checked' || state === 'unchecked') {
|
||||
const need = state === 'checked';
|
||||
const checked = getChecked(element, false);
|
||||
|
|
@ -718,6 +715,7 @@ export class InjectedScript {
|
|||
}
|
||||
let matches = true;
|
||||
if (optionToSelect.valueOrLabel !== undefined) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
matches = matches && (optionToSelect.valueOrLabel === option.value || optionToSelect.valueOrLabel === option.label);
|
||||
}
|
||||
if (optionToSelect.value !== undefined) {
|
||||
|
|
@ -835,6 +833,7 @@ export class InjectedScript {
|
|||
}
|
||||
|
||||
const { activeElement, isFocused: wasFocused } = this._activelyFocused(node);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if ((node as HTMLElement).isContentEditable && !wasFocused && activeElement && (activeElement as HTMLElement | SVGElement).blur) {
|
||||
// Workaround the Firefox bug where focusing the element does not switch current
|
||||
// contenteditable to the new element. However, blurring the previous one helps.
|
||||
|
|
@ -899,7 +898,7 @@ export class InjectedScript {
|
|||
|
||||
// Get all component roots leading to the target element.
|
||||
// Go from the bottom to the top to make it work with closed shadow roots.
|
||||
let parentElement = targetElement;
|
||||
let parentElement = targetElement as Element | undefined;
|
||||
while (parentElement) {
|
||||
const root = enclosingShadowRootOrDocument(parentElement);
|
||||
if (!root) {
|
||||
|
|
@ -1054,10 +1053,12 @@ export class InjectedScript {
|
|||
}
|
||||
|
||||
// Determine the event point. Note that Firefox does not always have window.TouchEvent.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const point = (!!this.window.TouchEvent && (event instanceof this.window.TouchEvent)) ? event.touches[0] : (event as MouseEvent | PointerEvent);
|
||||
|
||||
// Check that we hit the right element at the first event, and assume all
|
||||
// subsequent events will be fine.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (result === undefined && point) {
|
||||
result = this.expectHitTarget({ x: point.clientX, y: point.clientY }, element);
|
||||
}
|
||||
|
|
@ -1265,6 +1266,8 @@ export class InjectedScript {
|
|||
// New documentElement - let's check whether listeners are still here.
|
||||
seenEvent = false;
|
||||
this.window.dispatchEvent(new CustomEvent(customEventName));
|
||||
// TODO: Dead code?
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (seenEvent) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,12 +128,13 @@ const kImplicitRoleByTagName: { [tagName: string]: (e: Element) => AriaRole | nu
|
|||
}
|
||||
if (['email', 'tel', 'text', 'url', ''].includes(type)) {
|
||||
// https://html.spec.whatwg.org/multipage/input.html#concept-input-list
|
||||
const list = getIdRefs(e, e.getAttribute('list'))[0];
|
||||
const list = getIdRefs(e, e.getAttribute('list'))[0] as Element | undefined;
|
||||
return (list && elementSafeTagName(list) === 'DATALIST') ? 'combobox' : 'textbox';
|
||||
}
|
||||
if (type === 'hidden') {
|
||||
return null;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return inputTypeToRole[type] || 'textbox';
|
||||
},
|
||||
'INS': () => 'insertion',
|
||||
|
|
@ -206,10 +207,11 @@ function getImplicitAriaRole(element: Element): AriaRole | null {
|
|||
}
|
||||
// Inherit presentation role when required.
|
||||
// https://www.w3.org/TR/wai-aria-1.2/#conflict_resolution_presentation_none
|
||||
let ancestor: Element | null = element;
|
||||
let ancestor = element as Element | null;
|
||||
while (ancestor) {
|
||||
const parent = parentElementOrShadowHost(ancestor);
|
||||
const parents = kPresentationInheritanceParents[elementSafeTagName(ancestor)];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!parents || !parent || !parents.includes(elementSafeTagName(parent))) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -233,7 +235,7 @@ const validRoles: AriaRole[] = ['alert', 'alertdialog', 'application', 'article'
|
|||
function getExplicitAriaRole(element: Element): AriaRole | null {
|
||||
// https://www.w3.org/TR/wai-aria-1.2/#document-handling_author-errors_roles
|
||||
const roles = (element.getAttribute('role') || '').split(' ').map(role => role.trim());
|
||||
return roles.find(role => validRoles.includes(role as any)) as AriaRole || null;
|
||||
return (roles.find(role => validRoles.includes(role as any)) as AriaRole | undefined) || null;
|
||||
}
|
||||
|
||||
function hasPresentationConflictResolution(element: Element, role: string | null) {
|
||||
|
|
@ -661,6 +663,7 @@ function getTextAlternativeInternal(element: Element, options: AccessibleNameOpt
|
|||
// https://w3c.github.io/html-aam/#button-element-accessible-name-computation
|
||||
if (!labelledBy && tagName === 'BUTTON') {
|
||||
options.visitedElements.add(element);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const labels = (element as HTMLButtonElement).labels || [];
|
||||
if (labels.length) {
|
||||
return getAccessibleNameFromAssociatedLabels(labels, options);
|
||||
|
|
@ -671,6 +674,7 @@ function getTextAlternativeInternal(element: Element, options: AccessibleNameOpt
|
|||
// https://w3c.github.io/html-aam/#output-element-accessible-name-computation
|
||||
if (!labelledBy && tagName === 'OUTPUT') {
|
||||
options.visitedElements.add(element);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const labels = (element as HTMLOutputElement).labels || [];
|
||||
if (labels.length) {
|
||||
return getAccessibleNameFromAssociatedLabels(labels, options);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { isElementVisible, parentElementOrShadowHost } from './domUtils';
|
|||
import { type LayoutSelectorName, layoutSelectorScore } from './layoutSelectorUtils';
|
||||
import { elementMatchesText, elementText, shouldSkipForTextMatching, type ElementText } from './selectorUtils';
|
||||
import { normalizeWhiteSpace } from '../../utils/isomorphic/stringUtils';
|
||||
import { assertUnreachableWithError } from 'playwright-core/lib/common/types';
|
||||
|
||||
type QueryContext = {
|
||||
scope: Element | Document;
|
||||
|
|
@ -280,66 +281,70 @@ export class SelectorEvaluatorImpl implements SelectorEvaluator {
|
|||
}
|
||||
return this._cached<boolean>(this._cacheMatchesParents, element, [complex, index, context.scope, context.pierceShadow, context.originalScope], () => {
|
||||
const { selector: simple, combinator } = complex.simples[index];
|
||||
if (combinator === '>') {
|
||||
const parent = parentElementOrShadowHostInContext(element, context);
|
||||
if (!parent || !this._matchesSimple(parent, simple, context)) {
|
||||
switch (combinator) {
|
||||
case '>': {
|
||||
const parent = parentElementOrShadowHostInContext(element, context);
|
||||
if (!parent || !this._matchesSimple(parent, simple, context)) {
|
||||
return false;
|
||||
}
|
||||
return this._matchesParents(parent, complex, index - 1, context);
|
||||
}
|
||||
case '+': {
|
||||
const previousSibling = previousSiblingInContext(element, context);
|
||||
if (!previousSibling || !this._matchesSimple(previousSibling, simple, context)) {
|
||||
return false;
|
||||
}
|
||||
return this._matchesParents(previousSibling, complex, index - 1, context);
|
||||
}
|
||||
case '': {
|
||||
let parent = parentElementOrShadowHostInContext(element, context);
|
||||
while (parent) {
|
||||
if (this._matchesSimple(parent, simple, context)) {
|
||||
if (this._matchesParents(parent, complex, index - 1, context)) {
|
||||
return true;
|
||||
}
|
||||
if (complex.simples[index - 1].combinator === '') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
parent = parentElementOrShadowHostInContext(parent, context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return this._matchesParents(parent, complex, index - 1, context);
|
||||
}
|
||||
if (combinator === '+') {
|
||||
const previousSibling = previousSiblingInContext(element, context);
|
||||
if (!previousSibling || !this._matchesSimple(previousSibling, simple, context)) {
|
||||
case '~': {
|
||||
let previousSibling = previousSiblingInContext(element, context);
|
||||
while (previousSibling) {
|
||||
if (this._matchesSimple(previousSibling, simple, context)) {
|
||||
if (this._matchesParents(previousSibling, complex, index - 1, context)) {
|
||||
return true;
|
||||
}
|
||||
if (complex.simples[index - 1].combinator === '~') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
previousSibling = previousSiblingInContext(previousSibling, context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return this._matchesParents(previousSibling, complex, index - 1, context);
|
||||
}
|
||||
if (combinator === '') {
|
||||
let parent = parentElementOrShadowHostInContext(element, context);
|
||||
while (parent) {
|
||||
if (this._matchesSimple(parent, simple, context)) {
|
||||
if (this._matchesParents(parent, complex, index - 1, context)) {
|
||||
return true;
|
||||
}
|
||||
if (complex.simples[index - 1].combinator === '') {
|
||||
break;
|
||||
case '>=': {
|
||||
let parent: Element | undefined = element;
|
||||
while (parent) {
|
||||
if (this._matchesSimple(parent, simple, context)) {
|
||||
if (this._matchesParents(parent, complex, index - 1, context)) {
|
||||
return true;
|
||||
}
|
||||
if (complex.simples[index - 1].combinator === '') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
parent = parentElementOrShadowHostInContext(parent, context);
|
||||
}
|
||||
parent = parentElementOrShadowHostInContext(parent, context);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (combinator === '~') {
|
||||
let previousSibling = previousSiblingInContext(element, context);
|
||||
while (previousSibling) {
|
||||
if (this._matchesSimple(previousSibling, simple, context)) {
|
||||
if (this._matchesParents(previousSibling, complex, index - 1, context)) {
|
||||
return true;
|
||||
}
|
||||
if (complex.simples[index - 1].combinator === '~') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
previousSibling = previousSiblingInContext(previousSibling, context);
|
||||
default: {
|
||||
return assertUnreachableWithError(combinator, new Error(`Unsupported combinator "${combinator}"`));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (combinator === '>=') {
|
||||
let parent: Element | undefined = element;
|
||||
while (parent) {
|
||||
if (this._matchesSimple(parent, simple, context)) {
|
||||
if (this._matchesParents(parent, complex, index - 1, context)) {
|
||||
return true;
|
||||
}
|
||||
if (complex.simples[index - 1].combinator === '') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
parent = parentElementOrShadowHostInContext(parent, context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
throw new Error(`Unsupported combinator "${combinator}"`);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -461,6 +466,7 @@ const scopeEngine: SelectorEngine = {
|
|||
const actualScope = context.originalScope || context.scope;
|
||||
if (actualScope.nodeType === 9 /* Node.DOCUMENT_NODE */) {
|
||||
const root = (actualScope as Document).documentElement;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return root ? [root] : [];
|
||||
}
|
||||
if (actualScope.nodeType === 1 /* Node.ELEMENT_NODE */) {
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ function generateSelectorFor(injectedScript: InjectedScript, targetElement: Elem
|
|||
// This is best theoretically possible candidate from the current parent.
|
||||
// We use the fact that widening the scope to grand-parent makes any selector
|
||||
// even less likely to match.
|
||||
let bestPossibleInParent: SelectorToken[] | null = candidates[0];
|
||||
let bestPossibleInParent = candidates[0] as SelectorToken[] | null;
|
||||
if (!bestPossibleInParent) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -442,7 +442,7 @@ function cssFallback(injectedScript: InjectedScript, targetElement: Element, opt
|
|||
bestTokenForLevel = token;
|
||||
}
|
||||
|
||||
const parent = element.parentNode as (Element | ShadowRoot);
|
||||
const parent = element.parentNode as (Element | ShadowRoot | null);
|
||||
|
||||
// Combine class names until unique.
|
||||
const classes = [...element.classList];
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export function matchesAttributePart(value: any, attr: AttributeSelectorPart) {
|
|||
if (attr.op === '|=') {
|
||||
return objValue === attrValue || objValue.startsWith(attrValue + '-');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (attr.op === '~=') {
|
||||
return objValue.split(' ').includes(attrValue);
|
||||
}
|
||||
|
|
@ -63,6 +64,7 @@ export function matchesAttributePart(value: any, attr: AttributeSelectorPart) {
|
|||
|
||||
export function shouldSkipForTextMatching(element: Element | ShadowRoot) {
|
||||
const document = element.ownerDocument;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return element.nodeName === 'SCRIPT' || element.nodeName === 'NOSCRIPT' || element.nodeName === 'STYLE' || document.head && document.head.contains(element);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export const XPathEngine: SelectorEngine = {
|
|||
}
|
||||
const result: Element[] = [];
|
||||
const document = root.ownerDocument || root;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!document) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ export function source() {
|
|||
if (Object.is(value, undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (typeof value === 'object' && value) {
|
||||
if ('ref' in value) {
|
||||
return refs.get(value.ref);
|
||||
|
|
@ -92,6 +93,7 @@ export function source() {
|
|||
if (value.v === '-Infinity') {
|
||||
return -Infinity;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (value.v === '-0') {
|
||||
return -0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ export async function syncLocalStorageWithSettings(page: Page, appName: string)
|
|||
await page.addInitScript(
|
||||
`(${String((settings: any) => {
|
||||
// iframes w/ snapshots, etc.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (location && location.protocol === 'data:') {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -494,6 +494,7 @@ export class Page extends SdkObject {
|
|||
return;
|
||||
}
|
||||
const mainFrame = this._frameManager.mainFrame();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!mainFrame || !mainFrame.pendingDocument()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -619,11 +620,11 @@ export class Page extends SdkObject {
|
|||
async expectScreenshot(metadata: CallMetadata, options: ExpectScreenshotOptions = {}): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[] }> {
|
||||
const locator = options.locator;
|
||||
const rafrafScreenshot = locator ? async (progress: Progress, timeout: number) => {
|
||||
return await locator.frame.rafrafTimeoutScreenshotElementWithProgress(progress, locator.selector, timeout, options || {});
|
||||
return await locator.frame.rafrafTimeoutScreenshotElementWithProgress(progress, locator.selector, timeout, options);
|
||||
} : async (progress: Progress, timeout: number) => {
|
||||
await this.performActionPreChecks(progress);
|
||||
await this.mainFrame().rafrafTimeout(timeout);
|
||||
return await this._screenshotter.screenshotPage(progress, options || {});
|
||||
return await this._screenshotter.screenshotPage(progress, options);
|
||||
};
|
||||
|
||||
const comparator = getComparator('image/png');
|
||||
|
|
@ -632,7 +633,7 @@ export class Page extends SdkObject {
|
|||
return { errorMessage: '"not" matcher requires expected result' };
|
||||
}
|
||||
try {
|
||||
const format = validateScreenshotOptions(options || {});
|
||||
const format = validateScreenshotOptions(options);
|
||||
if (format !== 'png') {
|
||||
throw new Error('Only PNG screenshots are supported');
|
||||
}
|
||||
|
|
@ -667,6 +668,7 @@ export class Page extends SdkObject {
|
|||
progress.log(` generating new stable screenshot expectation`);
|
||||
}
|
||||
let isFirstIteration = true;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
progress.throwIfAborted();
|
||||
if (this.isClosed()) {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export class Recorder implements InstrumentationListener, IRecorder {
|
|||
}
|
||||
|
||||
static show(codegenMode: 'actions' | 'trace-events', context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextEnableRecorderParams): Promise<Recorder> {
|
||||
let recorderPromise = (context as any)[recorderSymbol] as Promise<Recorder>;
|
||||
let recorderPromise = (context as any)[recorderSymbol] as Promise<Recorder> | undefined;
|
||||
if (!recorderPromise) {
|
||||
recorderPromise = Recorder._create(codegenMode, context, recorderAppFactory, params);
|
||||
(context as any)[recorderSymbol] = recorderPromise;
|
||||
|
|
@ -135,6 +135,7 @@ export class Recorder implements InstrumentationListener, IRecorder {
|
|||
this._contextRecorder.clearScript();
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (data.event === 'runTask') {
|
||||
this._contextRecorder.runTask(data.params.task);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ function iterablePump(): ChunkIterator {
|
|||
|
||||
const iterable = (async function* () {
|
||||
const reader = stream.getReader();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
|
|
|
|||
|
|
@ -283,13 +283,14 @@ export class ContextRecorder extends EventEmitter {
|
|||
|
||||
export async function generateFrameSelector(frame: Frame): Promise<string[]> {
|
||||
const selectorPromises: Promise<string>[] = [];
|
||||
while (frame) {
|
||||
const parent = frame.parentFrame();
|
||||
let currentFrame = frame as Frame | undefined;
|
||||
while (currentFrame) {
|
||||
const parent = currentFrame.parentFrame();
|
||||
if (!parent) {
|
||||
break;
|
||||
}
|
||||
selectorPromises.push(generateFrameSelectorInParent(parent, frame));
|
||||
frame = parent;
|
||||
selectorPromises.push(generateFrameSelectorInParent(parent, currentFrame));
|
||||
currentFrame = parent;
|
||||
}
|
||||
const result = await Promise.all(selectorPromises);
|
||||
return result.reverse();
|
||||
|
|
@ -299,6 +300,7 @@ async function generateFrameSelectorInParent(parent: Frame, frame: Frame): Promi
|
|||
const result = await raceAgainstDeadline(async () => {
|
||||
try {
|
||||
const frameElement = await frame.frameElement();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!frameElement || !parent) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export class RecorderCollection extends EventEmitter {
|
|||
|
||||
if (signal.name === 'navigation' && frame._page.mainFrame() === frame) {
|
||||
const timestamp = monotonicTime();
|
||||
const lastAction = this._actions[this._actions.length - 1];
|
||||
const lastAction = this._actions[this._actions.length - 1] as actions.ActionInContext | undefined;
|
||||
const signalThreshold = isUnderTest() ? 500 : 5000;
|
||||
|
||||
let generateGoto = false;
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ export function callMetadataForAction(pageAliases: Map<Page, string>, actionInCo
|
|||
export function collapseActions(actions: actions.ActionInContext[]): actions.ActionInContext[] {
|
||||
const result: actions.ActionInContext[] = [];
|
||||
for (const action of actions) {
|
||||
const lastAction = result[result.length - 1];
|
||||
const lastAction = result[result.length - 1] as actions.ActionInContext | undefined;
|
||||
const isSameAction = lastAction && lastAction.action.name === action.action.name && lastAction.frame.pageAlias === action.frame.pageAlias && lastAction.frame.framePath.join('|') === action.frame.framePath.join('|');
|
||||
const isSameSelector = lastAction && 'selector' in lastAction.action && 'selector' in action.action && action.action.selector === lastAction.action.selector;
|
||||
const shouldMerge = isSameAction && (action.action.name === 'navigate' || (action.action.name === 'fill' && isSameSelector));
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ function getDownloadProgress(): OnProgressCallback {
|
|||
}
|
||||
|
||||
function getAnimatedDownloadProgress(): OnProgressCallback {
|
||||
let progressBar: ProgressBar;
|
||||
let progressBar: ProgressBar | undefined;
|
||||
let lastDownloadedBytes = 0;
|
||||
|
||||
return (downloadedBytes: number, totalBytes: number) => {
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ export class Screenshotter {
|
|||
|
||||
private async _fullPageSize(progress: Progress): Promise<types.Size> {
|
||||
const fullPageSize = await this._page.mainFrame().waitForFunctionValueInUtility(progress, () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!document.body || !document.documentElement) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -297,6 +298,7 @@ export class Screenshotter {
|
|||
return cleanup;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
await Promise.all((options.mask || []).map(async ({ frame, selector }) => {
|
||||
const pair = await frame.selectors.resolveFrameForSelector(selector);
|
||||
if (pair) {
|
||||
|
|
@ -378,6 +380,7 @@ export function validateScreenshotOptions(options: ScreenshotOptions): 'png' | '
|
|||
// options.type takes precedence over inferring the type from options.path
|
||||
// because it may be a 0-length file with no extension created beforehand (i.e. as a temp file).
|
||||
if (options.type) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
assert(options.type === 'png' || options.type === 'jpeg', 'Unknown options.type value: ' + options.type);
|
||||
format = options.type;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ export class Selectors {
|
|||
});
|
||||
return {
|
||||
parsed,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
world: needsMainWorld ? 'main' as const : 'utility' as const,
|
||||
strict,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export class Snapshotter {
|
|||
|
||||
// In each frame, in a non-stalling manner, capture the snapshots.
|
||||
const snapshots = page.frames().map(async frame => {
|
||||
const data = await frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => debugLogger.log('error', e)) as SnapshotData;
|
||||
const data = await frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => debugLogger.log('error', e)) as SnapshotData | undefined;
|
||||
// Something went wrong -> bail out, our snapshots are best-efforty.
|
||||
if (!data || !this._started) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
|
|||
// New documentElement - let's check whether listeners are still here.
|
||||
seenEvent = false;
|
||||
window.dispatchEvent(new CustomEvent(customEventName));
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!seenEvent) {
|
||||
// Listener did not fire. Reattach the listener and notify.
|
||||
window.addEventListener(customEventName, handleCustomEvent);
|
||||
|
|
@ -163,7 +164,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
|
|||
}
|
||||
|
||||
private _interceptNativeMethod(obj: any, method: string, cb: (thisObj: any, result: any) => void) {
|
||||
const native = obj[method] as Function;
|
||||
const native = obj[method] as Function | undefined;
|
||||
if (!native) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -175,7 +176,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
|
|||
}
|
||||
|
||||
private _interceptNativeAsyncMethod(obj: any, method: string, cb: (thisObj: any, result: any) => void) {
|
||||
const native = obj[method] as Function;
|
||||
const native = obj[method] as Function | undefined;
|
||||
if (!native) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -436,11 +437,9 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
|
|||
|
||||
const visitChildStyleSheet = (child: CSSStyleSheet) => {
|
||||
const snapshot = visitStyleSheet(child);
|
||||
if (snapshot) {
|
||||
result.push(snapshot.n);
|
||||
expectValue(child);
|
||||
equals = equals && snapshot.equals;
|
||||
}
|
||||
result.push(snapshot.n);
|
||||
expectValue(child);
|
||||
equals = equals && snapshot.equals;
|
||||
};
|
||||
|
||||
if (nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
|
|
@ -482,6 +481,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
|
|||
expectValue(value);
|
||||
attrs[kBoundingRectAttribute] = value;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if ((element as HTMLElement).popover && (element as HTMLElement).matches && (element as HTMLElement).matches(':popover-open')) {
|
||||
const value = 'true';
|
||||
expectValue(kPopoverOpenAttribute);
|
||||
|
|
@ -626,6 +626,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
|
|||
};
|
||||
|
||||
let html: NodeSnapshot;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (document.documentElement) {
|
||||
const { n } = visitNode(document.documentElement)!;
|
||||
html = n;
|
||||
|
|
|
|||
|
|
@ -405,9 +405,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
|||
const error = await this._fs.syncAndGetError();
|
||||
|
||||
this._isStopping = false;
|
||||
if (this._state) {
|
||||
this._state.recording = false;
|
||||
}
|
||||
this._state.recording = false;
|
||||
|
||||
// IMPORTANT: no awaits after this point, to make sure recording state is correct.
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ export class WebSocketTransport implements ConnectionTransport {
|
|||
|
||||
if (result.redirect) {
|
||||
// Strip authorization headers from the redirected request.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const newHeaders = Object.fromEntries(Object.entries(headers || {}).filter(([name]) => {
|
||||
return !name.includes('access-key') && name.toLowerCase() !== 'authorization';
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ export class WKBrowser extends Browser {
|
|||
context = this._contexts.get(event.browserContextId) || null;
|
||||
}
|
||||
if (!context) {
|
||||
context = this._defaultContext as WKBrowserContext;
|
||||
context = this._defaultContext as WKBrowserContext | null;
|
||||
}
|
||||
if (!context) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ export class WKPage implements PageDelegate {
|
|||
promises.push(this.updateUserAgent());
|
||||
}
|
||||
const emulatedMedia = this._page.emulatedMedia();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (emulatedMedia.media || emulatedMedia.colorScheme || emulatedMedia.reducedMotion || emulatedMedia.forcedColors) {
|
||||
promises.push(WKPage._setEmulateMedia(session, emulatedMedia.media, emulatedMedia.colorScheme, emulatedMedia.reducedMotion, emulatedMedia.forcedColors));
|
||||
}
|
||||
|
|
@ -274,6 +275,7 @@ export class WKPage implements PageDelegate {
|
|||
this._pageProxySession.dispose();
|
||||
eventsHelper.removeEventListeners(this._sessionListeners);
|
||||
eventsHelper.removeEventListeners(this._eventListeners);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (this._session) {
|
||||
this._session.dispose();
|
||||
}
|
||||
|
|
@ -1038,6 +1040,7 @@ export class WKPage implements PageDelegate {
|
|||
frameId: frame._id,
|
||||
executionContextId: ((context as any)[contextDelegateSymbol] as WKExecutionContext)._contextId
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!result || result.object.subtype === 'null') {
|
||||
throw new Error('Frame has been detached.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,14 +60,14 @@ export class WKWorkers {
|
|||
});
|
||||
}),
|
||||
eventsHelper.addEventListener(session, 'Worker.dispatchMessageFromWorker', (event: Protocol.Worker.dispatchMessageFromWorkerPayload) => {
|
||||
const workerSession = this._workerSessions.get(event.workerId)!;
|
||||
const workerSession = this._workerSessions.get(event.workerId);
|
||||
if (!workerSession) {
|
||||
return;
|
||||
}
|
||||
workerSession.dispatchMessage(JSON.parse(event.message));
|
||||
}),
|
||||
eventsHelper.addEventListener(session, 'Worker.workerTerminated', (event: Protocol.Worker.workerTerminatedPayload) => {
|
||||
const workerSession = this._workerSessions.get(event.workerId)!;
|
||||
const workerSession = this._workerSessions.get(event.workerId);
|
||||
if (!workerSession) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ export function compareBuffersOrStrings(actualBuffer: Buffer | string, expectedB
|
|||
if (typeof actualBuffer === 'string') {
|
||||
return compareText(actualBuffer, expectedBuffer);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!actualBuffer || !(actualBuffer instanceof Buffer)) {
|
||||
return { errorMessage: 'Actual result should be a Buffer or a string.' };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export function headersObjectToArray(headers: HeadersObject, separator?: string,
|
|||
}
|
||||
const result: HeadersArray = [];
|
||||
for (const name in headers) {
|
||||
const values = headers[name];
|
||||
const values = headers[name] as string | undefined;
|
||||
if (values === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,6 +230,7 @@ class KeyParser {
|
|||
|
||||
private _readAttributes(result: AriaTemplateRoleNode) {
|
||||
let errorPos = this._pos;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
this._skipWhitespace();
|
||||
if (this._peek() === '[') {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ export function parseCSS(selector: string, customNames: Set<string>): { selector
|
|||
|
||||
function consumeFunctionArguments(): CSSFunctionArgument[] {
|
||||
const result = [consumeArgument()];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
skipWhitespace();
|
||||
if (!isComma()) {
|
||||
|
|
@ -163,6 +164,7 @@ export function parseCSS(selector: string, customNames: Set<string>): { selector
|
|||
} else {
|
||||
result.simples.push({ selector: consumeSimpleSelector(), combinator: '' });
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
skipWhitespace();
|
||||
if (isClauseCombinator()) {
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ export function tokenize(str1: string): CSSTokenInterface[] {
|
|||
const consumeComments = function() {
|
||||
while (next(1) === 0x2f && next(2) === 0x2a) {
|
||||
consume(2);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
consume();
|
||||
if (code === 0x2a && next() === 0x2f) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import { escapeWithQuotes, normalizeEscapedRegexQuotes, toSnakeCase, toTitleCase } from './stringUtils';
|
||||
import { type NestedSelectorBody, parseAttributeSelector, parseSelector, stringifySelector } from './selectorParser';
|
||||
import type { ParsedSelector } from './selectorParser';
|
||||
import type { ParsedSelector, ParsedSelectorPart } from './selectorParser';
|
||||
|
||||
export type Language = 'javascript' | 'python' | 'java' | 'csharp' | 'jsonl';
|
||||
export type LocatorType = 'default' | 'role' | 'text' | 'label' | 'placeholder' | 'alt' | 'title' | 'test-id' | 'nth' | 'first' | 'last' | 'has-text' | 'has-not-text' | 'has' | 'hasNot' | 'frame' | 'frame-locator' | 'and' | 'or' | 'chain';
|
||||
|
|
@ -177,7 +177,7 @@ function innerAsLocators(factory: LocatorFactory, parsed: ParsedSelector, isFram
|
|||
continue;
|
||||
}
|
||||
|
||||
const nextPart = parts[index + 1];
|
||||
const nextPart = parts[index + 1] as ParsedSelectorPart | undefined;
|
||||
|
||||
const selectorPart = stringifySelector({ parts: [part] });
|
||||
const locatorPart = factory.generateLocator(base, 'default', selectorPart);
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ function shiftParams(template: string, sub: number) {
|
|||
function transform(template: string, params: TemplateParams, testIdAttributeName: string): string {
|
||||
// Recursively handle filter(has=, hasnot=, sethas(), sethasnot()).
|
||||
// TODO: handle and(locator), or(locator), locator(locator), locator(has=, hasnot=, sethas(), sethasnot()).
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
const hasMatch = template.match(/filter\(,?(has=|hasnot=|sethas\(|sethasnot\()/);
|
||||
if (!hasMatch) {
|
||||
|
|
|
|||
|
|
@ -328,6 +328,7 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
|
|||
while (!EOL) {
|
||||
if (next() === '\\') {
|
||||
source += eat1();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (EOL) {
|
||||
syntaxError('parsing regular expression');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,13 +30,13 @@ export function globToRegex(glob: string): RegExp {
|
|||
continue;
|
||||
}
|
||||
if (c === '*') {
|
||||
const beforeDeep = glob[i - 1];
|
||||
const beforeDeep = glob[i - 1] as string | undefined;
|
||||
let starCount = 1;
|
||||
while (glob[i + 1] === '*') {
|
||||
starCount++;
|
||||
i++;
|
||||
}
|
||||
const afterDeep = glob[i + 1];
|
||||
const afterDeep = glob[i + 1] as string | undefined;
|
||||
const isDeep = starCount > 1 &&
|
||||
(beforeDeep === '/' || beforeDeep === undefined) &&
|
||||
(afterDeep === '/' || afterDeep === undefined);
|
||||
|
|
|
|||
|
|
@ -80,17 +80,15 @@ export function httpRequest(params: HTTPRequestParams, onResponse: (r: http.Inco
|
|||
https.request(options, requestCallback) :
|
||||
http.request(options, requestCallback);
|
||||
request.on('error', onError);
|
||||
if (timeout !== undefined) {
|
||||
const rejectOnTimeout = () => {
|
||||
onError(new Error(`Request to ${params.url} timed out after ${timeout}ms`));
|
||||
request.abort();
|
||||
};
|
||||
if (timeout <= 0) {
|
||||
rejectOnTimeout();
|
||||
return;
|
||||
}
|
||||
request.setTimeout(timeout, rejectOnTimeout);
|
||||
const rejectOnTimeout = () => {
|
||||
onError(new Error(`Request to ${params.url} timed out after ${timeout}ms`));
|
||||
request.abort();
|
||||
};
|
||||
if (timeout <= 0) {
|
||||
rejectOnTimeout();
|
||||
return;
|
||||
}
|
||||
request.setTimeout(timeout, rejectOnTimeout);
|
||||
request.end(params.data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export async function pollAgainstDeadline<T>(callback: () => Promise<{ continueP
|
|||
const lastPollInterval = pollIntervals.pop() ?? 1000;
|
||||
let lastResult: T|undefined;
|
||||
const wrappedCallback = () => Promise.resolve().then(callback);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
const time = monotonicTime();
|
||||
if (deadline && time >= deadline) {
|
||||
|
|
|
|||
|
|
@ -51,13 +51,14 @@ export class ZipFile {
|
|||
|
||||
async read(entryPath: string): Promise<Buffer> {
|
||||
await this._openedPromise;
|
||||
const entry = this._entries.get(entryPath)!;
|
||||
const entry = this._entries.get(entryPath);
|
||||
if (!entry) {
|
||||
throw new Error(`${entryPath} not found in file ${this._fileName}`);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._zipFile!.openReadStream(entry, (error, readStream) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (error || !readStream) {
|
||||
reject(error || 'Entry not found');
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ function vitePlugin(registerSource: string, templateDir: string, buildInfo: Buil
|
|||
async transform(this: PluginContext, content, id) {
|
||||
const queryIndex = id.indexOf('?');
|
||||
const file = queryIndex !== -1 ? id.substring(0, queryIndex) : id;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!buildInfo.sources[file]) {
|
||||
try {
|
||||
const timestamp = (await fs.promises.stat(file)).mtimeMs;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ function __pwRender(value) {
|
|||
if (isJsxFragment(type)) {
|
||||
type = __pwReact.Fragment;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const props = component.props ? __pwRender(component.props) : {};
|
||||
const key = component.key ? __pwRender(component.key) : undefined;
|
||||
const { children, ...propsWithoutChildren } = props;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ function __pwRender(value) {
|
|||
return window.__pwTransformObject(value, v => {
|
||||
if (isJsxComponent(v)) {
|
||||
const component = v;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const props = component.props ? __pwRender(component.props) : {};
|
||||
const key = component.key ? __pwRender(component.key) : undefined;
|
||||
const { children, ...propsWithoutChildren } = props;
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
|
|||
|
||||
window.playwrightUnmount = async rootElement => {
|
||||
const svelteComponent = /** @type {SvelteComponent} */ (rootElement[__pwSvelteComponentKey]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!svelteComponent) {
|
||||
throw new Error('Component was not mounted');
|
||||
}
|
||||
|
|
@ -125,6 +126,7 @@ window.playwrightUpdate = async (rootElement, component) => {
|
|||
}
|
||||
|
||||
const svelteComponent = /** @type {SvelteComponent} */ (rootElement[__pwSvelteComponentKey]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!svelteComponent) {
|
||||
throw new Error('Component was not mounted');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ const utils = Object.freeze({
|
|||
});
|
||||
|
||||
function getPrototype(obj: object) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (Object.getPrototypeOf) {
|
||||
return Object.getPrototypeOf(obj);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -323,6 +323,7 @@ const makeThrowingMatcher = (
|
|||
|
||||
// Try to remove this function from the stack trace frame.
|
||||
// Guard for some environments (browsers) that do not support this feature.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(error, throwingMatcher);
|
||||
}
|
||||
|
|
@ -350,6 +351,7 @@ const makeThrowingMatcher = (
|
|||
!(error instanceof JestAssertionError) &&
|
||||
error.name !== 'PrettyFormatPluginError' &&
|
||||
// Guard for some environments (browsers) that do not support this feature.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
Error.captureStackTrace
|
||||
) {
|
||||
// Try to remove this and deeper functions from the stack trace frame.
|
||||
|
|
@ -373,6 +375,7 @@ const makeThrowingMatcher = (
|
|||
|
||||
if (isPromise(potentialResult)) {
|
||||
const asyncError = new JestAssertionError();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(asyncError, throwingMatcher);
|
||||
}
|
||||
|
|
@ -432,6 +435,7 @@ const _validateResult = (result: any) => {
|
|||
|
||||
function assertions(expected: number): void {
|
||||
const error = new Error();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(error, assertions);
|
||||
}
|
||||
|
|
@ -444,6 +448,7 @@ function assertions(expected: number): void {
|
|||
}
|
||||
function hasAssertions(...args: Array<unknown>): void {
|
||||
const error = new Error();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(error, hasAssertions);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ export async function loadConfig(location: ConfigLocation, overrides?: ConfigCLI
|
|||
}
|
||||
|
||||
function validateConfig(file: string, config: Config) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (typeof config !== 'object' || !config) {
|
||||
throw errorWithFile(file, `Configuration file must export a single object`);
|
||||
}
|
||||
|
|
@ -239,7 +240,7 @@ function validateConfig(file: string, config: Config) {
|
|||
}
|
||||
|
||||
if ('reportSlowTests' in config && config.reportSlowTests !== undefined && config.reportSlowTests !== null) {
|
||||
if (!config.reportSlowTests || typeof config.reportSlowTests !== 'object') {
|
||||
if (typeof config.reportSlowTests !== 'object') {
|
||||
throw errorWithFile(file, `config.reportSlowTests must be an object`);
|
||||
}
|
||||
if (!('max' in config.reportSlowTests) || typeof config.reportSlowTests.max !== 'number' || config.reportSlowTests.max < 0) {
|
||||
|
|
@ -251,7 +252,7 @@ function validateConfig(file: string, config: Config) {
|
|||
}
|
||||
|
||||
if ('shard' in config && config.shard !== undefined && config.shard !== null) {
|
||||
if (!config.shard || typeof config.shard !== 'object') {
|
||||
if (typeof config.shard !== 'object') {
|
||||
throw errorWithFile(file, `config.shard must be an object`);
|
||||
}
|
||||
if (!('total' in config.shard) || typeof config.shard.total !== 'number' || config.shard.total < 1) {
|
||||
|
|
@ -278,6 +279,7 @@ function validateConfig(file: string, config: Config) {
|
|||
}
|
||||
|
||||
function validateProject(file: string, project: Project, title: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (typeof project !== 'object' || !project) {
|
||||
throw errorWithFile(file, `${title} must be an object`);
|
||||
}
|
||||
|
|
@ -334,7 +336,7 @@ function validateProject(file: string, project: Project, title: string) {
|
|||
}
|
||||
|
||||
if ('use' in project && project.use !== undefined) {
|
||||
if (!project.use || typeof project.use !== 'object') {
|
||||
if (typeof project.use !== 'object') {
|
||||
throw errorWithFile(file, `${title}.use must be an object`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ export class FixturePool {
|
|||
}
|
||||
|
||||
// If no errors found, iterate over boxed fixtures
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!hasDependencyErrors) {
|
||||
for (const name of names) {
|
||||
const registration = this._registrations.get(name)!;
|
||||
|
|
@ -260,6 +261,7 @@ export class FixturePool {
|
|||
const signatureSymbol = Symbol('signature');
|
||||
|
||||
export function formatPotentiallyInternalLocation(location: Location): string {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const isUserFixture = location && filterStackFile(location.file);
|
||||
return isUserFixture ? formatLocation(location) : '<builtin>';
|
||||
}
|
||||
|
|
@ -317,6 +319,7 @@ function filterOutComments(s: string): string {
|
|||
if (s[i - 1] === '*' && s[i] === '/') {
|
||||
commentState = 'none';
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} else if (commentState === 'none') {
|
||||
if (s[i] === '/' && s[i + 1] === '/') {
|
||||
commentState = 'singleline';
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ export class TestCase extends Base implements reporterTypes.TestCase {
|
|||
}
|
||||
|
||||
titlePath(): string[] {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const titlePath = this.parent ? this.parent.titlePath() : [];
|
||||
titlePath.push(this.title);
|
||||
return titlePath;
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ export const rootTestType = new TestTypeImpl([]);
|
|||
export function mergeTests(...tests: TestType<any, any>[]) {
|
||||
let result = rootTestType;
|
||||
for (const t of tests) {
|
||||
const testTypeImpl = (t as any)[testTypeSymbol] as TestTypeImpl;
|
||||
const testTypeImpl = (t as any)[testTypeSymbol] as TestTypeImpl | undefined;
|
||||
if (!testTypeImpl) {
|
||||
throw new Error(`mergeTests() accepts "test" functions as parameters.\nDid you mean to call test.extend() with fixtures instead?`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
handleSIGINT: false,
|
||||
...launchOptions,
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (headless !== undefined) {
|
||||
options.headless = headless;
|
||||
}
|
||||
|
|
@ -179,6 +180,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
serviceWorkers,
|
||||
}, use) => {
|
||||
const options: BrowserContextOptions = {};
|
||||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
||||
if (acceptDownloads !== undefined) {
|
||||
options.acceptDownloads = acceptDownloads;
|
||||
}
|
||||
|
|
@ -245,6 +247,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
if (serviceWorkers !== undefined) {
|
||||
options.serviceWorkers = serviceWorkers;
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-unnecessary-condition */
|
||||
await use({
|
||||
...contextOptions,
|
||||
...options,
|
||||
|
|
@ -454,7 +457,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
}
|
||||
|
||||
// First time we are reusing the context, we should create the page.
|
||||
let [page] = context.pages();
|
||||
let page = context.pages()[0] as Page | undefined;
|
||||
if (!page) {
|
||||
page = await context.newPage();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -494,6 +494,7 @@ export class TeleTestCase implements reporterTypes.TestCase {
|
|||
}
|
||||
|
||||
titlePath(): string[] {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const titlePath = this.parent ? this.parent.titlePath() : [];
|
||||
titlePath.push(this.title);
|
||||
return titlePath;
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ export class TestTree {
|
|||
|
||||
for (const test of parentSuite.tests) {
|
||||
const title = test.title;
|
||||
let testCaseItem = parentGroup.children.find(t => t.kind !== 'group' && t.title === title) as TestCaseItem;
|
||||
let testCaseItem = parentGroup.children.find(t => t.kind !== 'group' && t.title === title) as TestCaseItem | undefined;
|
||||
if (!testCaseItem) {
|
||||
testCaseItem = {
|
||||
kind: 'case',
|
||||
|
|
@ -135,9 +135,9 @@ export class TestTree {
|
|||
status = 'skipped';
|
||||
} else if (result.status === 'interrupted') {
|
||||
status = 'none';
|
||||
} else if (result && test.outcome() !== 'expected') {
|
||||
} else if (test.outcome() !== 'expected') {
|
||||
status = 'failed';
|
||||
} else if (result && test.outcome() === 'expected') {
|
||||
} else if (test.outcome() === 'expected') {
|
||||
status = 'passed';
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue