From e029e78ebe56cf59ea2a3c82c3c10113388c3e8e Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Tue, 17 Dec 2024 09:56:44 -0800 Subject: [PATCH] Applied no-unnecessary-condition rule --- .eslintrc.js | 5 +- packages/html-reporter/src/reportView.tsx | 2 +- packages/html-reporter/src/testCaseView.tsx | 10 +- packages/html-reporter/src/testFileView.tsx | 4 +- packages/html-reporter/src/testFilesView.tsx | 4 +- packages/html-reporter/src/testResultView.tsx | 2 +- .../playwright-core/src/browserServerImpl.ts | 1 + .../src/client/browserContext.ts | 4 +- .../playwright-core/src/client/browserType.ts | 2 +- .../playwright-core/src/client/connection.ts | 4 +- .../src/client/consoleMessage.ts | 2 +- .../src/client/elementHandle.ts | 2 + .../src/client/eventEmitter.ts | 12 +- packages/playwright-core/src/client/fetch.ts | 2 + packages/playwright-core/src/client/frame.ts | 1 + .../playwright-core/src/client/network.ts | 3 +- .../playwright-core/src/client/playwright.ts | 2 +- .../playwright-core/src/common/socksProxy.ts | 2 +- packages/playwright-core/src/common/types.ts | 8 +- .../src/protocol/serializers.ts | 40 ++++--- .../playwright-core/src/protocol/transport.ts | 1 + .../src/protocol/validatorPrimitives.ts | 44 ++++--- .../src/remote/playwrightConnection.ts | 26 +++-- .../src/server/android/android.ts | 3 +- .../src/server/bidi/bidiBrowser.ts | 5 +- .../src/server/bidi/bidiExecutionContext.ts | 75 +++++++----- .../src/server/bidi/bidiNetworkManager.ts | 18 +-- .../src/server/bidi/bidiPage.ts | 3 +- .../bidi/third_party/bidiDeserializer.ts | 1 + .../src/server/browserContext.ts | 6 +- .../src/server/chromium/chromium.ts | 2 +- .../src/server/chromium/crBrowser.ts | 2 +- .../src/server/chromium/crNetworkManager.ts | 4 +- .../src/server/chromium/crPage.ts | 7 +- .../src/server/codegen/csharp.ts | 1 + .../src/server/codegen/java.ts | 1 + .../src/server/debugController.ts | 3 - .../server/dispatchers/artifactDispatcher.ts | 1 + .../dispatchers/browserContextDispatcher.ts | 1 + .../dispatchers/localUtilsDispatcher.ts | 1 + .../dispatchers/webSocketRouteDispatcher.ts | 1 + packages/playwright-core/src/server/dom.ts | 2 + packages/playwright-core/src/server/fetch.ts | 1 + .../src/server/firefox/ffBrowser.ts | 1 + .../src/server/firefox/ffInput.ts | 22 ++-- .../src/server/frameSelectors.ts | 2 +- packages/playwright-core/src/server/frames.ts | 8 +- .../src/server/injected/ariaSnapshot.ts | 5 + .../src/server/injected/clock.ts | 2 + .../src/server/injected/domUtils.ts | 3 + .../src/server/injected/highlight.ts | 1 + .../src/server/injected/injectedScript.ts | 15 ++- .../src/server/injected/roleUtils.ts | 10 +- .../src/server/injected/selectorEvaluator.ts | 108 +++++++++--------- .../src/server/injected/selectorGenerator.ts | 4 +- .../src/server/injected/selectorUtils.ts | 2 + .../server/injected/xpathSelectorEngine.ts | 1 + .../isomorphic/utilityScriptSerializers.ts | 2 + .../playwright-core/src/server/launchApp.ts | 1 + packages/playwright-core/src/server/page.ts | 8 +- .../playwright-core/src/server/recorder.ts | 3 +- .../src/server/recorder/chat.ts | 1 + .../src/server/recorder/contextRecorder.ts | 10 +- .../src/server/recorder/recorderCollection.ts | 2 +- .../src/server/recorder/recorderUtils.ts | 2 +- .../src/server/registry/browserFetcher.ts | 2 +- .../src/server/screenshotter.ts | 3 + .../playwright-core/src/server/selectors.ts | 1 + .../src/server/trace/recorder/snapshotter.ts | 2 +- .../trace/recorder/snapshotterInjected.ts | 15 +-- .../src/server/trace/recorder/tracing.ts | 4 +- .../playwright-core/src/server/transport.ts | 1 + .../src/server/webkit/wkBrowser.ts | 2 +- .../src/server/webkit/wkPage.ts | 3 + .../src/server/webkit/wkWorkers.ts | 4 +- .../playwright-core/src/utils/comparators.ts | 1 + packages/playwright-core/src/utils/headers.ts | 2 +- .../src/utils/isomorphic/ariaSnapshot.ts | 1 + .../src/utils/isomorphic/cssParser.ts | 2 + .../src/utils/isomorphic/cssTokenizer.ts | 1 + .../src/utils/isomorphic/locatorGenerators.ts | 4 +- .../src/utils/isomorphic/locatorParser.ts | 1 + .../src/utils/isomorphic/selectorParser.ts | 1 + .../src/utils/isomorphic/urlMatch.ts | 4 +- packages/playwright-core/src/utils/network.ts | 18 ++- .../src/utils/timeoutRunner.ts | 1 + packages/playwright-core/src/utils/zipFile.ts | 3 +- packages/playwright-ct-core/src/vitePlugin.ts | 1 + .../playwright-ct-react/registerSource.mjs | 1 + .../playwright-ct-react17/registerSource.mjs | 1 + .../playwright-ct-svelte/registerSource.mjs | 2 + .../expect/third_party/asymmetricMatchers.ts | 1 + .../bundles/expect/third_party/index.ts | 5 + .../playwright/src/common/configLoader.ts | 8 +- packages/playwright/src/common/fixtures.ts | 3 + packages/playwright/src/common/test.ts | 1 + packages/playwright/src/common/testType.ts | 2 +- packages/playwright/src/index.ts | 5 +- .../playwright/src/isomorphic/teleReceiver.ts | 1 + .../playwright/src/isomorphic/testTree.ts | 6 +- .../src/matchers/toMatchAriaSnapshot.ts | 4 +- .../src/matchers/toMatchSnapshot.ts | 1 + .../playwright/src/matchers/toMatchText.ts | 1 + .../src/plugins/gitCommitInfoPlugin.ts | 1 + packages/playwright/src/reporters/json.ts | 1 + packages/playwright/src/reporters/merge.ts | 2 + .../playwright/src/reporters/reporterV2.ts | 1 + packages/playwright/src/runner/dispatcher.ts | 2 +- packages/playwright/src/runner/lastRun.ts | 4 +- packages/playwright/src/runner/rebase.ts | 5 +- packages/playwright/src/runner/tasks.ts | 1 + packages/playwright/src/runner/testServer.ts | 1 + packages/playwright/src/runner/watchMode.ts | 3 +- .../playwright/src/transform/transform.ts | 2 + .../playwright/src/worker/fixtureRunner.ts | 1 + packages/playwright/src/worker/testInfo.ts | 2 + packages/playwright/src/worker/testTracing.ts | 3 +- .../playwright/src/worker/timeoutManager.ts | 1 + packages/playwright/src/worker/workerMain.ts | 3 + packages/recorder/src/recorder.tsx | 4 +- packages/recorder/src/recorderTypes.ts | 3 +- packages/trace-viewer/bundle.ts | 1 + packages/trace-viewer/src/index.tsx | 1 + packages/trace-viewer/src/recorder.tsx | 1 + .../trace-viewer/src/sw/snapshotRenderer.ts | 2 + packages/trace-viewer/src/ui/callTab.tsx | 1 + packages/trace-viewer/src/ui/consoleTab.tsx | 1 + packages/trace-viewer/src/ui/filmStrip.tsx | 1 + packages/trace-viewer/src/ui/modelUtil.ts | 7 +- packages/trace-viewer/src/ui/networkTab.tsx | 1 + .../src/ui/recorder/actionListView.tsx | 1 + packages/trace-viewer/src/ui/snapshotTab.tsx | 2 +- .../trace-viewer/src/ui/uiModeTraceView.tsx | 2 +- packages/trace-viewer/src/ui/uiModeView.tsx | 4 + packages/trace-viewer/src/uiMode.tsx | 1 + .../web/src/components/codeMirrorWrapper.tsx | 2 +- packages/web/src/components/splitView.tsx | 4 +- packages/web/src/components/treeView.tsx | 2 +- packages/web/src/shared/resizeView.tsx | 1 + packages/web/src/theme.ts | 4 +- 140 files changed, 464 insertions(+), 268 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 5ecf3f5e98..1b2476bf3d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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 diff --git a/packages/html-reporter/src/reportView.tsx b/packages/html-reporter/src/reportView.tsx index db95dd412c..1b37aea0ca 100644 --- a/packages/html-reporter/src/reportView.tsx +++ b/packages/html-reporter/src/reportView.tsx @@ -78,7 +78,7 @@ export const ReportView: React.FC<{ return
{report?.json() && } - {report?.json().metadata && } + {report?.json().metadata && } { - return test?.annotations?.filter(annotation => !annotation.type.startsWith('_')) || []; + return test?.annotations.filter(annotation => !annotation.type.startsWith('_')) || []; }, [test?.annotations]); return
@@ -58,10 +58,10 @@ export const TestCaseView: React.FC<{
next »
} - {test &&
{test?.title}
} + {test &&
{test.title}
} {test &&
- + {test.location.file}:{test.location.line}
@@ -69,7 +69,7 @@ export const TestCaseView: React.FC<{
{msToString(test.duration)}
} {test && (!!test.projectName || labels) &&
- {test && !!test.projectName && } + {!!test.projectName && } {labels && }
} {!!visibleAnnotations.length && @@ -80,7 +80,7 @@ export const TestCaseView: React.FC<{ id: String(index), title:
{statusIcon(result.status)} {retryLabel(index)}
, render: () => - })) || []} selectedTab={String(selectedResultIndex)} setSelectedTab={id => setSelectedResultIndex(+id)} />} + }))} selectedTab={String(selectedResultIndex)} setSelectedTab={id => setSelectedResultIndex(+id)} />}
; }; diff --git a/packages/html-reporter/src/testFileView.tsx b/packages/html-reporter/src/testFileView.tsx index 2113c008ff..b6689c14c7 100644 --- a/packages/html-reporter/src/testFileView.tsx +++ b/packages/html-reporter/src/testFileView.tsx @@ -86,9 +86,9 @@ function videoBadge(test: TestCaseSummary): JSX.Element | undefined { return resultWithVideo ? {video()} : 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 ? {trace()} : undefined; + return {trace()}; } const LabelsClickView: React.FCProject: {report.projectNames[0]}} {filteredStats &&
Filtered: {filteredStats.total} {!!filteredStats.total && ('(' + msToString(filteredStats.duration) + ')')}
}
-
{report ? new Date(report.startTime).toLocaleString() : ''}
-
Total time: {msToString(report.duration ?? 0)}
+
{new Date(report.startTime).toLocaleString()}
+
Total time: {msToString(report.duration)}
{!!report.errors.length && {report.errors.map((error, index) => )} diff --git a/packages/html-reporter/src/testResultView.tsx b/packages/html-reporter/src/testResultView.tsx index bdaec18d24..8b95c02bdf 100644 --- a/packages/html-reporter/src/testResultView.tsx +++ b/packages/html-reporter/src/testResultView.tsx @@ -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/')); diff --git a/packages/playwright-core/src/browserServerImpl.ts b/packages/playwright-core/src/browserServerImpl.ts index 73455aa5c0..80053db7a0 100644 --- a/packages/playwright-core/src/browserServerImpl.ts +++ b/packages/playwright-core/src/browserServerImpl.ts @@ -38,6 +38,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher { async launchServer(options: LaunchServerOptions = {}): Promise { 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); diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 28afee001e..e30461464e 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -116,9 +116,7 @@ export class BrowserContext extends ChannelOwner 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); diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index d4eb59ff5a..a7ef63f855 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -150,7 +150,7 @@ export class BrowserType extends ChannelOwner 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(); } diff --git a/packages/playwright-core/src/client/connection.ts b/packages/playwright-core/src/client/connection.ts index dba0cdd303..51394e6066 100644 --- a/packages/playwright-core/src/client/connection.ts +++ b/packages/playwright-core/src/client/connection.ts @@ -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`); } diff --git a/packages/playwright-core/src/client/consoleMessage.ts b/packages/playwright-core/src/client/consoleMessage.ts index fcdc3fd149..f880ff47a4 100644 --- a/packages/playwright-core/src/client/consoleMessage.ts +++ b/packages/playwright-core/src/client/consoleMessage.ts @@ -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; } diff --git a/packages/playwright-core/src/client/elementHandle.ts b/packages/playwright-core/src/client/elementHandle.ts index c731781ffb..30387fb7c1 100644 --- a/packages/playwright-core/src/client/elementHandle.ts +++ b/packages/playwright-core/src/client/elementHandle.ts @@ -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; 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; } diff --git a/packages/playwright-core/src/client/eventEmitter.ts b/packages/playwright-core/src/client/eventEmitter.ts index 01d620225d..1bf502a15b 100644 --- a/packages/playwright-core/src/client/eventEmitter.ts +++ b/packages/playwright-core/src/client/eventEmitter.ts @@ -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; } diff --git a/packages/playwright-core/src/client/fetch.ts b/packages/playwright-core/src/client/fetch.ts index d508b6fd3a..2e60fd9933 100644 --- a/packages/playwright-core/src/client/fetch.ts +++ b/packages/playwright-core/src/client/fetch.ts @@ -206,6 +206,7 @@ export class APIRequestContext extends ChannelOwner implements api.Fr async waitForFunction(pageFunction: structs.PageFunction, arg?: Arg, options: WaitForFunctionOptions = {}): Promise> { 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({ diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 56e5e879bb..958ed06d9f 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -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' : ', '); diff --git a/packages/playwright-core/src/client/playwright.ts b/packages/playwright-core/src/client/playwright.ts index 7221a04115..2590a5beb1 100644 --- a/packages/playwright-core/src/client/playwright.ts +++ b/packages/playwright-core/src/client/playwright.ts @@ -51,7 +51,7 @@ export class Playwright extends ChannelOwner { 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 }; diff --git a/packages/playwright-core/src/common/socksProxy.ts b/packages/playwright-core/src/common/socksProxy.ts index 615d47eb11..7c3c0286af 100644 --- a/packages/playwright-core/src/common/socksProxy.ts +++ b/packages/playwright-core/src/common/socksProxy.ts @@ -200,7 +200,7 @@ class SocksConnection { private async _readBytes(length: number): Promise { this._fence = this._offset + length; - if (!this._buffer || this._buffer.length < this._fence) { + if (this._buffer.length < this._fence) { await new Promise(f => this._fenceCallback = f); } this._offset += length; diff --git a/packages/playwright-core/src/common/types.ts b/packages/playwright-core/src/common/types.ts index 55145b5565..da62f3298c 100644 --- a/packages/playwright-core/src/common/types.ts +++ b/packages/playwright-core/src/common/types.ts @@ -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[]; \ No newline at end of file +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; +}; \ No newline at end of file diff --git a/packages/playwright-core/src/protocol/serializers.ts b/packages/playwright-core/src/protocol/serializers.ts index 6794d20797..78f47dfdf3 100644 --- a/packages/playwright-core/src/protocol/serializers.ts +++ b/packages/playwright-core/src/protocol/serializers.ts @@ -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) { diff --git a/packages/playwright-core/src/protocol/transport.ts b/packages/playwright-core/src/protocol/transport.ts index ad8fdd29d0..e5c0913b30 100644 --- a/packages/playwright-core/src/protocol/transport.ts +++ b/packages/playwright-core/src/protocol/transport.ts @@ -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. diff --git a/packages/playwright-core/src/protocol/validatorPrimitives.ts b/packages/playwright-core/src/protocol/validatorPrimitives.ts index 6c66e153d4..32fcb58046 100644 --- a/packages/playwright-core/src/protocol/validatorPrimitives.ts +++ b/packages/playwright-core/src/protocol/validatorPrimitives.ts @@ -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 + '"'); } diff --git a/packages/playwright-core/src/remote/playwrightConnection.ts b/packages/playwright-core/src/remote/playwrightConnection.ts index 3b10592d9d..0a6f8c1cc4 100644 --- a/packages/playwright-core/src/remote/playwrightConnection.ts +++ b/packages/playwright-core/src/remote/playwrightConnection.ts @@ -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' }); - } } }); diff --git a/packages/playwright-core/src/server/android/android.ts b/packages/playwright-core/src/server/android/android.ts index e0e437ab26..9c7035d7da 100644 --- a/packages/playwright-core/src/server/android/android.ts +++ b/packages/playwright-core/src/server/android/android.ts @@ -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; } diff --git a/packages/playwright-core/src/server/bidi/bidiBrowser.ts b/packages/playwright-core/src/server/bidi/bidiBrowser.ts index 569998b115..a314127510 100644 --- a/packages/playwright-core/src/server/bidi/bidiBrowser.ts +++ b/packages/playwright-core/src/server/bidi/bidiBrowser.ts @@ -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; diff --git a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts index cfc691f52e..1d063f6e6e 100644 --- a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts +++ b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts @@ -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 { @@ -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, values: any[], objectIds: string[]): Promise { @@ -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> { @@ -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)); } } diff --git a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts index 465e51e081..8fbbc02d78 100644 --- a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts +++ b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts @@ -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 { diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index b25a4711ff..5d4f483079 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -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; } diff --git a/packages/playwright-core/src/server/bidi/third_party/bidiDeserializer.ts b/packages/playwright-core/src/server/bidi/third_party/bidiDeserializer.ts index 4bdb3d081c..986fd7a84c 100644 --- a/packages/playwright-core/src/server/bidi/third_party/bidiDeserializer.ts +++ b/packages/playwright-core/src/server/bidi/third_party/bidiDeserializer.ts @@ -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; } diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index 32f4efaa99..7a020fdac1 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -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({ diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index 7ac9c6f8be..e2cb848ae2 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -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(); } diff --git a/packages/playwright-core/src/server/chromium/crBrowser.ts b/packages/playwright-core/src/server/chromium/crBrowser.ts index 397725e34a..d66c15a232 100644 --- a/packages/playwright-core/src/server/chromium/crBrowser.ts +++ b/packages/playwright-core/src/server/chromium/crBrowser.ts @@ -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) { diff --git a/packages/playwright-core/src/server/chromium/crNetworkManager.ts b/packages/playwright-core/src/server/chromium/crNetworkManager.ts index 955f7e7b5f..046d8aaf82 100644 --- a/packages/playwright-core/src/server/chromium/crNetworkManager.ts +++ b/packages/playwright-core/src/server/chromium/crNetworkManager.ts @@ -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')); diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts index 5346798870..f485f37008 100644 --- a/packages/playwright-core/src/server/chromium/crPage.ts +++ b/packages/playwright-core/src/server/chromium/crPage.ts @@ -212,7 +212,7 @@ export class CRPage implements PageDelegate { private async _go(delta: number): Promise { 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(); diff --git a/packages/playwright-core/src/server/codegen/csharp.ts b/packages/playwright-core/src/server/codegen/csharp.ts index 80a0c75304..b01beb2d2d 100644 --- a/packages/playwright-core/src/server/codegen/csharp.ts +++ b/packages/playwright-core/src/server/codegen/csharp.ts @@ -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'; diff --git a/packages/playwright-core/src/server/codegen/java.ts b/packages/playwright-core/src/server/codegen/java.ts index 7b466049d6..51d7e1244a 100644 --- a/packages/playwright-core/src/server/codegen/java.ts +++ b/packages/playwright-core/src/server/codegen/java.ts @@ -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'; diff --git a/packages/playwright-core/src/server/debugController.ts b/packages/playwright-core/src/server/debugController.ts index ac6d6ac4f3..3169d2b06c 100644 --- a/packages/playwright-core/src/server/debugController.ts +++ b/packages/playwright-core/src/server/debugController.ts @@ -240,9 +240,6 @@ export class DebugController extends SdkObject { await context.close({ reason: 'Browser collected' }); } } - if (!browser.contexts()) { - await browser.close({ reason: 'Browser collected' }); - } } } } diff --git a/packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts b/packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts index 2140c4bbdb..1a89706c89 100644 --- a/packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts @@ -31,6 +31,7 @@ export class ArtifactDispatcher extends Dispatcher { 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.'); } diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts index 0bfa9a17b1..c4a71a9dc4 100644 --- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts @@ -357,6 +357,7 @@ class HarBackend { private async _harFindResponse(url: string, method: string, headers: HeadersArray, postData: Buffer | undefined): Promise { const harLog = this._harFile.log; const visited = new Set(); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { const entries: har.Entry[] = []; for (const candidate of harLog.entries) { diff --git a/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts b/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts index 48af5fe876..54a455ec2f 100644 --- a/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts @@ -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)) { diff --git a/packages/playwright-core/src/server/dom.ts b/packages/playwright-core/src/server/dom.ts index 0f59c21d73..cf9366d8c1 100644 --- a/packages/playwright-core/src/server/dom.ts +++ b/packages/playwright-core/src/server/dom.ts @@ -183,6 +183,7 @@ export class ElementHandle extends js.JSHandle { } async isIframeElement(): Promise { + // 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 extends js.JSHandle { } 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); } diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index a8aa2adbef..2e77a38c35 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -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}`); diff --git a/packages/playwright-core/src/server/firefox/ffBrowser.ts b/packages/playwright-core/src/server/firefox/ffBrowser.ts index 0da66ea2da..1a8edeefc7 100644 --- a/packages/playwright-core/src/server/firefox/ffBrowser.ts +++ b/packages/playwright-core/src/server/firefox/ffBrowser.ts @@ -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}`); diff --git a/packages/playwright-core/src/server/firefox/ffInput.ts b/packages/playwright-core/src/server/firefox/ffInput.ts index 05168ecfdb..4c13a17565 100644 --- a/packages/playwright-core/src/server/firefox/ffInput.ts +++ b/packages/playwright-core/src/server/firefox/ffInput.ts @@ -38,16 +38,20 @@ function toModifiersMask(modifiers: Set): 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): number { diff --git a/packages/playwright-core/src/server/frameSelectors.ts b/packages/playwright-core/src/server/frameSelectors.ts index 3497d4a7ca..e5223c2474 100644 --- a/packages/playwright-core/src/server/frameSelectors.ts +++ b/packages/playwright-core/src/server/frameSelectors.ts @@ -103,7 +103,7 @@ export class FrameSelectors { const targetContext = await resolved.frame._mainContext(); const result: Promise>[] = []; for (const property of properties.values()) { - const elementHandle = property.asElement() as ElementHandle; + const elementHandle = property.asElement() as ElementHandle | null; if (elementHandle) { result.push(adoptIfNeeded(elementHandle, targetContext)); } else { diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index b212e2b2ae..042f07c544 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -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) { diff --git a/packages/playwright-core/src/server/injected/ariaSnapshot.ts b/packages/playwright-core/src/server/injected/ariaSnapshot.ts index 69a9ffb0b7..20300fb87c 100644 --- a/packages/playwright-core/src/server/injected/ariaSnapshot.ts +++ b/packages/playwright-core/src/server/injected/ariaSnapshot.ts @@ -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, ''); } diff --git a/packages/playwright-core/src/server/injected/clock.ts b/packages/playwright-core/src/server/injected/clock.ts index 576591bb9c..a3b132ec1c 100644 --- a/packages/playwright-core/src/server/injected/clock.ts +++ b/packages/playwright-core/src/server/injected/clock.ts @@ -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!)); } diff --git a/packages/playwright-core/src/server/injected/domUtils.ts b/packages/playwright-core/src/server/injected/domUtils.ts index 397021b8cf..5c7c3ac3e2 100644 --- a/packages/playwright-core/src/server/injected/domUtils.ts +++ b/packages/playwright-core/src/server/injected/domUtils.ts @@ -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; diff --git a/packages/playwright-core/src/server/injected/highlight.ts b/packages/playwright-core/src/server/injected/highlight.ts index 69b4bcad27..790a086368 100644 --- a/packages/playwright-core/src/server/injected/highlight.ts +++ b/packages/playwright-core/src/server/injected/highlight.ts @@ -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); } diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts index 0c8f8aed2f..ebb0d5559f 100644 --- a/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/packages/playwright-core/src/server/injected/injectedScript.ts @@ -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; } diff --git a/packages/playwright-core/src/server/injected/roleUtils.ts b/packages/playwright-core/src/server/injected/roleUtils.ts index 611bbcefee..ed75c7e9f0 100644 --- a/packages/playwright-core/src/server/injected/roleUtils.ts +++ b/packages/playwright-core/src/server/injected/roleUtils.ts @@ -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); diff --git a/packages/playwright-core/src/server/injected/selectorEvaluator.ts b/packages/playwright-core/src/server/injected/selectorEvaluator.ts index 30dfa264ae..c41928254f 100644 --- a/packages/playwright-core/src/server/injected/selectorEvaluator.ts +++ b/packages/playwright-core/src/server/injected/selectorEvaluator.ts @@ -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(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 */) { diff --git a/packages/playwright-core/src/server/injected/selectorGenerator.ts b/packages/playwright-core/src/server/injected/selectorGenerator.ts index d581336f61..68983cb9ad 100644 --- a/packages/playwright-core/src/server/injected/selectorGenerator.ts +++ b/packages/playwright-core/src/server/injected/selectorGenerator.ts @@ -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]; diff --git a/packages/playwright-core/src/server/injected/selectorUtils.ts b/packages/playwright-core/src/server/injected/selectorUtils.ts index ce5b900466..12f02eaeb2 100644 --- a/packages/playwright-core/src/server/injected/selectorUtils.ts +++ b/packages/playwright-core/src/server/injected/selectorUtils.ts @@ -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); } diff --git a/packages/playwright-core/src/server/injected/xpathSelectorEngine.ts b/packages/playwright-core/src/server/injected/xpathSelectorEngine.ts index ff705335e1..48d33dce0c 100644 --- a/packages/playwright-core/src/server/injected/xpathSelectorEngine.ts +++ b/packages/playwright-core/src/server/injected/xpathSelectorEngine.ts @@ -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; } diff --git a/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts b/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts index 5c1c5d5d4b..9ec461769b 100644 --- a/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts +++ b/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts @@ -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; } diff --git a/packages/playwright-core/src/server/launchApp.ts b/packages/playwright-core/src/server/launchApp.ts index 55f63f4cf4..b1c49ba7cd 100644 --- a/packages/playwright-core/src/server/launchApp.ts +++ b/packages/playwright-core/src/server/launchApp.ts @@ -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; } diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 4ceded9999..26d21ffa76 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -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()) { diff --git a/packages/playwright-core/src/server/recorder.ts b/packages/playwright-core/src/server/recorder.ts index 5e846b3595..aad553eaff 100644 --- a/packages/playwright-core/src/server/recorder.ts +++ b/packages/playwright-core/src/server/recorder.ts @@ -63,7 +63,7 @@ export class Recorder implements InstrumentationListener, IRecorder { } static show(codegenMode: 'actions' | 'trace-events', context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextEnableRecorderParams): Promise { - let recorderPromise = (context as any)[recorderSymbol] as Promise; + let recorderPromise = (context as any)[recorderSymbol] as Promise | 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; diff --git a/packages/playwright-core/src/server/recorder/chat.ts b/packages/playwright-core/src/server/recorder/chat.ts index 5d36506ed4..5bbcfc231e 100644 --- a/packages/playwright-core/src/server/recorder/chat.ts +++ b/packages/playwright-core/src/server/recorder/chat.ts @@ -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) { diff --git a/packages/playwright-core/src/server/recorder/contextRecorder.ts b/packages/playwright-core/src/server/recorder/contextRecorder.ts index 26c71f2f64..f9fc6181d7 100644 --- a/packages/playwright-core/src/server/recorder/contextRecorder.ts +++ b/packages/playwright-core/src/server/recorder/contextRecorder.ts @@ -283,13 +283,14 @@ export class ContextRecorder extends EventEmitter { export async function generateFrameSelector(frame: Frame): Promise { const selectorPromises: Promise[] = []; - 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; } diff --git a/packages/playwright-core/src/server/recorder/recorderCollection.ts b/packages/playwright-core/src/server/recorder/recorderCollection.ts index 4c5a1b2009..714537c957 100644 --- a/packages/playwright-core/src/server/recorder/recorderCollection.ts +++ b/packages/playwright-core/src/server/recorder/recorderCollection.ts @@ -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; diff --git a/packages/playwright-core/src/server/recorder/recorderUtils.ts b/packages/playwright-core/src/server/recorder/recorderUtils.ts index 4ba5bac30a..a8c28eb64e 100644 --- a/packages/playwright-core/src/server/recorder/recorderUtils.ts +++ b/packages/playwright-core/src/server/recorder/recorderUtils.ts @@ -98,7 +98,7 @@ export function callMetadataForAction(pageAliases: Map, 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)); diff --git a/packages/playwright-core/src/server/registry/browserFetcher.ts b/packages/playwright-core/src/server/registry/browserFetcher.ts index a302c2927d..ea0604f2b9 100644 --- a/packages/playwright-core/src/server/registry/browserFetcher.ts +++ b/packages/playwright-core/src/server/registry/browserFetcher.ts @@ -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) => { diff --git a/packages/playwright-core/src/server/screenshotter.ts b/packages/playwright-core/src/server/screenshotter.ts index 925aa5b31c..3e31d0065e 100644 --- a/packages/playwright-core/src/server/screenshotter.ts +++ b/packages/playwright-core/src/server/screenshotter.ts @@ -188,6 +188,7 @@ export class Screenshotter { private async _fullPageSize(progress: Progress): Promise { 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; } diff --git a/packages/playwright-core/src/server/selectors.ts b/packages/playwright-core/src/server/selectors.ts index 309f250d28..dd89c440cd 100644 --- a/packages/playwright-core/src/server/selectors.ts +++ b/packages/playwright-core/src/server/selectors.ts @@ -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, }; diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts index b819510c75..948aee5398 100644 --- a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts +++ b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts @@ -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; diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts index ad74db9b5a..e6da2c882a 100644 --- a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts +++ b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts @@ -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; diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index aef7d867a3..d53bd96f81 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -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. diff --git a/packages/playwright-core/src/server/transport.ts b/packages/playwright-core/src/server/transport.ts index abb5363b91..68e1eef455 100644 --- a/packages/playwright-core/src/server/transport.ts +++ b/packages/playwright-core/src/server/transport.ts @@ -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'; })); diff --git a/packages/playwright-core/src/server/webkit/wkBrowser.ts b/packages/playwright-core/src/server/webkit/wkBrowser.ts index b5bf747e2c..d78389cf7b 100644 --- a/packages/playwright-core/src/server/webkit/wkBrowser.ts +++ b/packages/playwright-core/src/server/webkit/wkBrowser.ts @@ -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; diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 5056971db3..108452a4fa 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -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.'); } diff --git a/packages/playwright-core/src/server/webkit/wkWorkers.ts b/packages/playwright-core/src/server/webkit/wkWorkers.ts index f587d2c5a1..bd968d0845 100644 --- a/packages/playwright-core/src/server/webkit/wkWorkers.ts +++ b/packages/playwright-core/src/server/webkit/wkWorkers.ts @@ -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; } diff --git a/packages/playwright-core/src/utils/comparators.ts b/packages/playwright-core/src/utils/comparators.ts index 8521071806..60574d051a 100644 --- a/packages/playwright-core/src/utils/comparators.ts +++ b/packages/playwright-core/src/utils/comparators.ts @@ -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.' }; } diff --git a/packages/playwright-core/src/utils/headers.ts b/packages/playwright-core/src/utils/headers.ts index 6c0370fea9..2169ceffe7 100644 --- a/packages/playwright-core/src/utils/headers.ts +++ b/packages/playwright-core/src/utils/headers.ts @@ -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; } diff --git a/packages/playwright-core/src/utils/isomorphic/ariaSnapshot.ts b/packages/playwright-core/src/utils/isomorphic/ariaSnapshot.ts index c2af9845ef..7880eb85d2 100644 --- a/packages/playwright-core/src/utils/isomorphic/ariaSnapshot.ts +++ b/packages/playwright-core/src/utils/isomorphic/ariaSnapshot.ts @@ -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() === '[') { diff --git a/packages/playwright-core/src/utils/isomorphic/cssParser.ts b/packages/playwright-core/src/utils/isomorphic/cssParser.ts index c4dc750afe..4d2f4b5abf 100644 --- a/packages/playwright-core/src/utils/isomorphic/cssParser.ts +++ b/packages/playwright-core/src/utils/isomorphic/cssParser.ts @@ -132,6 +132,7 @@ export function parseCSS(selector: string, customNames: Set): { 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): { selector } else { result.simples.push({ selector: consumeSimpleSelector(), combinator: '' }); } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { skipWhitespace(); if (isClauseCombinator()) { diff --git a/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts b/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts index 72b21e1898..2e460621d0 100644 --- a/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts +++ b/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts @@ -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) { diff --git a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts index ee62d70b89..aa6eef41d3 100644 --- a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts +++ b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts @@ -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); diff --git a/packages/playwright-core/src/utils/isomorphic/locatorParser.ts b/packages/playwright-core/src/utils/isomorphic/locatorParser.ts index 904cf88b5a..dffa943f02 100644 --- a/packages/playwright-core/src/utils/isomorphic/locatorParser.ts +++ b/packages/playwright-core/src/utils/isomorphic/locatorParser.ts @@ -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) { diff --git a/packages/playwright-core/src/utils/isomorphic/selectorParser.ts b/packages/playwright-core/src/utils/isomorphic/selectorParser.ts index 8da49aaad7..31b04d0aa2 100644 --- a/packages/playwright-core/src/utils/isomorphic/selectorParser.ts +++ b/packages/playwright-core/src/utils/isomorphic/selectorParser.ts @@ -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'); } diff --git a/packages/playwright-core/src/utils/isomorphic/urlMatch.ts b/packages/playwright-core/src/utils/isomorphic/urlMatch.ts index 386a55bc85..7abb311d54 100644 --- a/packages/playwright-core/src/utils/isomorphic/urlMatch.ts +++ b/packages/playwright-core/src/utils/isomorphic/urlMatch.ts @@ -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); diff --git a/packages/playwright-core/src/utils/network.ts b/packages/playwright-core/src/utils/network.ts index 5e9a933643..c40cf349fa 100644 --- a/packages/playwright-core/src/utils/network.ts +++ b/packages/playwright-core/src/utils/network.ts @@ -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); } diff --git a/packages/playwright-core/src/utils/timeoutRunner.ts b/packages/playwright-core/src/utils/timeoutRunner.ts index c44ce0ffff..8752d256db 100644 --- a/packages/playwright-core/src/utils/timeoutRunner.ts +++ b/packages/playwright-core/src/utils/timeoutRunner.ts @@ -36,6 +36,7 @@ export async function pollAgainstDeadline(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) { diff --git a/packages/playwright-core/src/utils/zipFile.ts b/packages/playwright-core/src/utils/zipFile.ts index d9c95398bf..bcea3da753 100644 --- a/packages/playwright-core/src/utils/zipFile.ts +++ b/packages/playwright-core/src/utils/zipFile.ts @@ -51,13 +51,14 @@ export class ZipFile { async read(entryPath: string): Promise { 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; diff --git a/packages/playwright-ct-core/src/vitePlugin.ts b/packages/playwright-ct-core/src/vitePlugin.ts index b339b826ec..7df627e737 100644 --- a/packages/playwright-ct-core/src/vitePlugin.ts +++ b/packages/playwright-ct-core/src/vitePlugin.ts @@ -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; diff --git a/packages/playwright-ct-react/registerSource.mjs b/packages/playwright-ct-react/registerSource.mjs index a3572e6978..49df35c138 100644 --- a/packages/playwright-ct-react/registerSource.mjs +++ b/packages/playwright-ct-react/registerSource.mjs @@ -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; diff --git a/packages/playwright-ct-react17/registerSource.mjs b/packages/playwright-ct-react17/registerSource.mjs index 12fbe189a3..3e19b87842 100644 --- a/packages/playwright-ct-react17/registerSource.mjs +++ b/packages/playwright-ct-react17/registerSource.mjs @@ -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; diff --git a/packages/playwright-ct-svelte/registerSource.mjs b/packages/playwright-ct-svelte/registerSource.mjs index 06a324a197..2ed6ce99ae 100644 --- a/packages/playwright-ct-svelte/registerSource.mjs +++ b/packages/playwright-ct-svelte/registerSource.mjs @@ -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'); } diff --git a/packages/playwright/bundles/expect/third_party/asymmetricMatchers.ts b/packages/playwright/bundles/expect/third_party/asymmetricMatchers.ts index 6767e28ccb..23149203e6 100644 --- a/packages/playwright/bundles/expect/third_party/asymmetricMatchers.ts +++ b/packages/playwright/bundles/expect/third_party/asymmetricMatchers.ts @@ -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); } diff --git a/packages/playwright/bundles/expect/third_party/index.ts b/packages/playwright/bundles/expect/third_party/index.ts index 7328c7eb5c..fd52f4d494 100644 --- a/packages/playwright/bundles/expect/third_party/index.ts +++ b/packages/playwright/bundles/expect/third_party/index.ts @@ -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): void { const error = new Error(); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (Error.captureStackTrace) { Error.captureStackTrace(error, hasAssertions); } diff --git a/packages/playwright/src/common/configLoader.ts b/packages/playwright/src/common/configLoader.ts index 4b650e941d..d4c484732f 100644 --- a/packages/playwright/src/common/configLoader.ts +++ b/packages/playwright/src/common/configLoader.ts @@ -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`); } } diff --git a/packages/playwright/src/common/fixtures.ts b/packages/playwright/src/common/fixtures.ts index c631ee55e4..b9a74c86d6 100644 --- a/packages/playwright/src/common/fixtures.ts +++ b/packages/playwright/src/common/fixtures.ts @@ -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) : ''; } @@ -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'; diff --git a/packages/playwright/src/common/test.ts b/packages/playwright/src/common/test.ts index c5bbdfdc24..5a34980584 100644 --- a/packages/playwright/src/common/test.ts +++ b/packages/playwright/src/common/test.ts @@ -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; diff --git a/packages/playwright/src/common/testType.ts b/packages/playwright/src/common/testType.ts index bb9288fb47..5fecdb2258 100644 --- a/packages/playwright/src/common/testType.ts +++ b/packages/playwright/src/common/testType.ts @@ -337,7 +337,7 @@ export const rootTestType = new TestTypeImpl([]); export function mergeTests(...tests: TestType[]) { 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?`); } diff --git a/packages/playwright/src/index.ts b/packages/playwright/src/index.ts index ec44f6f7de..e503374537 100644 --- a/packages/playwright/src/index.ts +++ b/packages/playwright/src/index.ts @@ -77,6 +77,7 @@ const playwrightFixtures: Fixtures = ({ 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 = ({ 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 = ({ if (serviceWorkers !== undefined) { options.serviceWorkers = serviceWorkers; } + /* eslint-enable @typescript-eslint/no-unnecessary-condition */ await use({ ...contextOptions, ...options, @@ -454,7 +457,7 @@ const playwrightFixtures: Fixtures = ({ } // 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(); } diff --git a/packages/playwright/src/isomorphic/teleReceiver.ts b/packages/playwright/src/isomorphic/teleReceiver.ts index c6026cd494..84e855eb04 100644 --- a/packages/playwright/src/isomorphic/teleReceiver.ts +++ b/packages/playwright/src/isomorphic/teleReceiver.ts @@ -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; diff --git a/packages/playwright/src/isomorphic/testTree.ts b/packages/playwright/src/isomorphic/testTree.ts index 0763f36967..071121277c 100644 --- a/packages/playwright/src/isomorphic/testTree.ts +++ b/packages/playwright/src/isomorphic/testTree.ts @@ -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'; } diff --git a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts index 408e9ee44f..a000d558e3 100644 --- a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts @@ -66,7 +66,7 @@ export async function toMatchAriaSnapshot( } else if (expectedParam.name) { expectedPath = testInfo.snapshotPath(sanitizeFilePathBeforeExtension(expectedParam.name)); } else { - let snapshotNames = (testInfo as any)[snapshotNamesSymbol] as SnapshotNames; + let snapshotNames = (testInfo as any)[snapshotNamesSymbol] as SnapshotNames | undefined; if (!snapshotNames) { snapshotNames = { anonymousSnapshotIndex: 0 }; (testInfo as any)[snapshotNamesSymbol] = snapshotNames; @@ -107,6 +107,7 @@ export async function toMatchAriaSnapshot( const receivedText = typedReceived.raw; const message = () => { if (pass) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (notFound) { return messagePrefix + `Expected: not ${this.utils.printExpected(expected)}\nReceived: ${receivedText}` + callLogText(log); } @@ -114,6 +115,7 @@ export async function toMatchAriaSnapshot( return messagePrefix + `Expected: not ${this.utils.printExpected(expected)}\nReceived: ${printedReceived}` + callLogText(log); } else { const labelExpected = `Expected`; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (notFound) { return messagePrefix + `${labelExpected}: ${this.utils.printExpected(expected)}\nReceived: ${receivedText}` + callLogText(log); } diff --git a/packages/playwright/src/matchers/toMatchSnapshot.ts b/packages/playwright/src/matchers/toMatchSnapshot.ts index d5b746bbf2..57d1520d90 100644 --- a/packages/playwright/src/matchers/toMatchSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchSnapshot.ts @@ -198,6 +198,7 @@ class SnapshotHelper { message: () => message, log, }; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition return Object.fromEntries(Object.entries(unfiltered).filter(([_, v]) => v !== undefined)) as ImageMatcherResult; } diff --git a/packages/playwright/src/matchers/toMatchText.ts b/packages/playwright/src/matchers/toMatchText.ts index 2f8bc34b21..3789ebda57 100644 --- a/packages/playwright/src/matchers/toMatchText.ts +++ b/packages/playwright/src/matchers/toMatchText.ts @@ -45,6 +45,7 @@ export async function toMatchText( if ( !(typeof expected === 'string') && + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition !(expected && typeof expected.test === 'function') ) { // Same format as jest's matcherErrorMessage diff --git a/packages/playwright/src/plugins/gitCommitInfoPlugin.ts b/packages/playwright/src/plugins/gitCommitInfoPlugin.ts index f30c0b2a7c..3322644d64 100644 --- a/packages/playwright/src/plugins/gitCommitInfoPlugin.ts +++ b/packages/playwright/src/plugins/gitCommitInfoPlugin.ts @@ -36,6 +36,7 @@ export const gitCommitInfo = (options?: GitCommitInfoPluginOptions): TestRunnerP info['revision.timestamp'] = timestamp.getTime(); } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition config.metadata = config.metadata || {}; Object.assign(config.metadata, info); }, diff --git a/packages/playwright/src/reporters/json.ts b/packages/playwright/src/reporters/json.ts index 8e5cd87068..fd924791dc 100644 --- a/packages/playwright/src/reporters/json.ts +++ b/packages/playwright/src/reporters/json.ts @@ -157,6 +157,7 @@ class JSONReporter implements ReporterV2 { } } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition for (const spec of from.specs || []) { const toSpec = to.specs.find(s => s.title === spec.title && s.file === toPosixPath(path.relative(this.config.rootDir, spec.file)) && s.line === spec.line && s.column === spec.column); if (toSpec) { diff --git a/packages/playwright/src/reporters/merge.ts b/packages/playwright/src/reporters/merge.ts index d0cee62fba..d71da31a45 100644 --- a/packages/playwright/src/reporters/merge.ts +++ b/packages/playwright/src/reporters/merge.ts @@ -363,6 +363,7 @@ class UniqueFileNameGenerator { const extension = path.extname(name); name = name.substring(0, name.length - extension.length); let index = 0; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { const candidate = `${name}-${++index}${extension}`; if (!this._usedNames.has(candidate)) { @@ -412,6 +413,7 @@ class IdsPatcher { } private _onProject(project: JsonProject) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition project.metadata ??= {}; project.suites.forEach(suite => this._updateTestIds(suite)); } diff --git a/packages/playwright/src/reporters/reporterV2.ts b/packages/playwright/src/reporters/reporterV2.ts index 60cb63a96a..3fadbeb774 100644 --- a/packages/playwright/src/reporters/reporterV2.ts +++ b/packages/playwright/src/reporters/reporterV2.ts @@ -40,6 +40,7 @@ type StdIOChunk = { export function wrapReporterAsV2(reporter: Reporter | ReporterV2): ReporterV2 { try { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if ('version' in reporter && reporter.version() === 'v2') { return reporter as ReporterV2; } diff --git a/packages/playwright/src/runner/dispatcher.ts b/packages/playwright/src/runner/dispatcher.ts index 98e43fc055..b1993787a4 100644 --- a/packages/playwright/src/runner/dispatcher.ts +++ b/packages/playwright/src/runner/dispatcher.ts @@ -375,7 +375,7 @@ class JobDispatcher { } private _onAttach(params: AttachmentPayload) { - const data = this._dataByTestId.get(params.testId)!; + const data = this._dataByTestId.get(params.testId); if (!data) { // The test has finished, but attachments are still coming. Just ignore them. return; diff --git a/packages/playwright/src/runner/lastRun.ts b/packages/playwright/src/runner/lastRun.ts index f48542b3f8..50ea122f89 100644 --- a/packages/playwright/src/runner/lastRun.ts +++ b/packages/playwright/src/runner/lastRun.ts @@ -18,7 +18,7 @@ import fs from 'fs'; import path from 'path'; import type { FullResult, Suite } from '../../types/testReporter'; import { filterProjects } from './projectUtils'; -import type { FullConfigInternal } from '../common/config'; +import type { FullConfigInternal, FullProjectInternal } from '../common/config'; import type { ReporterV2 } from '../reporters/reporterV2'; type LastRunInfo = { @@ -33,7 +33,7 @@ export class LastRunReporter implements ReporterV2 { constructor(config: FullConfigInternal) { this._config = config; - const [project] = filterProjects(config.projects, config.cliProjectFilter); + const project = filterProjects(config.projects, config.cliProjectFilter)[0] as FullProjectInternal | undefined; if (project) { this._lastRunFile = path.join(project.project.outputDir, '.last-run.json'); } diff --git a/packages/playwright/src/runner/rebase.ts b/packages/playwright/src/runner/rebase.ts index 097b9f4db1..50da4e98e0 100644 --- a/packages/playwright/src/runner/rebase.ts +++ b/packages/playwright/src/runner/rebase.ts @@ -20,7 +20,7 @@ import type { T } from '../transform/babelBundle'; import { types, traverse, babelParse } from '../transform/babelBundle'; import { MultiMap } from 'playwright-core/lib/utils'; import { colors, diff } from 'playwright-core/lib/utilsBundle'; -import type { FullConfigInternal } from '../common/config'; +import type { FullConfigInternal, FullProjectInternal } from '../common/config'; import { filterProjects } from './projectUtils'; import type { InternalReporter } from '../reporters/internalReporter'; const t: typeof T = types; @@ -50,7 +50,7 @@ export async function applySuggestedRebaselines(config: FullConfigInternal, repo if (!suggestedRebaselines.size) { return; } - const [project] = filterProjects(config.projects, config.cliProjectFilter); + const project = filterProjects(config.projects, config.cliProjectFilter)[0] as FullProjectInternal | undefined; if (!project) { return; } @@ -197,6 +197,7 @@ function applyPatchWithConflictMarkers(oldText: string, newText: string) { } }); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (conflict) { result += '>>>>>>> SNAPSHOT\n'; } diff --git a/packages/playwright/src/runner/tasks.ts b/packages/playwright/src/runner/tasks.ts index 405f7a5c91..8ab8acb078 100644 --- a/packages/playwright/src/runner/tasks.ts +++ b/packages/playwright/src/runner/tasks.ts @@ -216,6 +216,7 @@ function createRemoveOutputDirsTask(): Task { projects.forEach(p => outputDirs.add(p.project.outputDir)); await Promise.all(Array.from(outputDirs).map(outputDir => removeFolders([outputDir]).then(async ([error]) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!error) { return; } diff --git a/packages/playwright/src/runner/testServer.ts b/packages/playwright/src/runner/testServer.ts index d56f1dece1..1dcf3a4be4 100644 --- a/packages/playwright/src/runner/testServer.ts +++ b/packages/playwright/src/runner/testServer.ts @@ -423,6 +423,7 @@ export class TestServerDispatcher implements TestServerInterface { // Preserve plugin instances between setup and build. if (!this._plugins) { webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p })); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition this._plugins = config.plugins || []; } else { config.plugins.splice(0, config.plugins.length, ...this._plugins); diff --git a/packages/playwright/src/runner/watchMode.ts b/packages/playwright/src/runner/watchMode.ts index bb3172554f..3c4ac01f60 100644 --- a/packages/playwright/src/runner/watchMode.ts +++ b/packages/playwright/src/runner/watchMode.ts @@ -140,6 +140,7 @@ export async function runWatchModeLoop(configLocation: ConfigLocation, initialOp let lastRun: { type: 'changed' | 'regular' | 'failed', failedTestIds?: string[], dirtyTestIds?: string[] } = { type: 'regular' }; let result: FullResult['status'] = 'passed'; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { if (bufferMode) { printBufferPrompt(dirtyTestFiles, teleSuiteUpdater.config!.rootDir); @@ -244,9 +245,9 @@ export async function runWatchModeLoop(configLocation: ConfigLocation, initialOp if (command === 'repeat') { if (lastRun.type === 'regular') { await runTests(options, testServerConnection, { title: 're-running tests' }); - continue; } else if (lastRun.type === 'changed') { await runTests(options, testServerConnection, { title: 're-running tests', testIds: lastRun.dirtyTestIds }); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (lastRun.type === 'failed') { await runTests({}, testServerConnection, { title: 're-running tests', testIds: lastRun.failedTestIds }); } diff --git a/packages/playwright/src/transform/transform.ts b/packages/playwright/src/transform/transform.ts index 3c602abcbd..93b125c80b 100644 --- a/packages/playwright/src/transform/transform.ts +++ b/packages/playwright/src/transform/transform.ts @@ -96,6 +96,7 @@ function loadAndValidateTsconfigsForFolder(folder: string): ParsedTsConfigData[] const foldersWithConfig: string[] = []; let currentFolder = path.resolve(folder); let result: ParsedTsConfigData[] | undefined; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { const cached = cachedTSConfigs.get(currentFolder); if (cached) { @@ -299,6 +300,7 @@ function installTransformIfNeeded() { const originalResolveFilename = (Module as any)._resolveFilename; function resolveFilename(this: any, specifier: string, parent: Module, ...rest: any[]) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (parent) { const resolved = resolveHook(parent.filename, specifier); if (resolved !== undefined) { diff --git a/packages/playwright/src/worker/fixtureRunner.ts b/packages/playwright/src/worker/fixtureRunner.ts index 1789d573af..6bcb8c04fe 100644 --- a/packages/playwright/src/worker/fixtureRunner.ts +++ b/packages/playwright/src/worker/fixtureRunner.ts @@ -41,6 +41,7 @@ class Fixture { this.registration = registration; this.value = null; const shouldGenerateStep = !this.registration.box && !this.registration.option; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const isUserFixture = this.registration.location && filterStackFile(this.registration.location.file); const title = this.registration.customTitle || this.registration.name; const location = isUserFixture ? this.registration.location : undefined; diff --git a/packages/playwright/src/worker/testInfo.ts b/packages/playwright/src/worker/testInfo.ts index 0c6740034a..5139605563 100644 --- a/packages/playwright/src/worker/testInfo.ts +++ b/packages/playwright/src/worker/testInfo.ts @@ -228,6 +228,7 @@ export class TestInfoImpl implements TestInfo { } else if (type === 'skip' || type === 'fixme') { this.expectedStatus = 'skipped'; throw new SkipError('Test is skipped: ' + (description || '')); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (type === 'fail') { if (this.expectedStatus !== 'skipped') { this.expectedStatus = 'failed'; @@ -330,6 +331,7 @@ export class TestInfoImpl implements TestInfo { location: data.location, }; this._onStepBegin(payload); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition this._tracing.appendBeforeActionForStep(stepId, parentStep?.stepId, data.apiName || data.title, data.params, data.location ? [data.location] : []); return step; } diff --git a/packages/playwright/src/worker/testTracing.ts b/packages/playwright/src/worker/testTracing.ts index ccd7a5c181..0ae8dcb099 100644 --- a/packages/playwright/src/worker/testTracing.ts +++ b/packages/playwright/src/worker/testTracing.ts @@ -98,6 +98,7 @@ export class TestTracing { } else if (typeof value === 'string') { this._options = { ...defaultTraceOptions, mode: value === 'retry-with-trace' ? 'on-first-retry' : value as TraceMode }; } else { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const mode = value.mode || 'off'; this._options = { ...defaultTraceOptions, ...value, mode: (mode as string) === 'retry-with-trace' ? 'on-first-retry' : mode }; } @@ -235,7 +236,7 @@ export class TestTracing { appendForError(error: TestInfoErrorImpl) { const rawStack = error.stack?.split('\n') || []; - const stack = rawStack ? filteredStackTrace(rawStack) : []; + const stack = filteredStackTrace(rawStack); this._appendTraceEvent({ type: 'error', message: this._formatError(error), diff --git a/packages/playwright/src/worker/timeoutManager.ts b/packages/playwright/src/worker/timeoutManager.ts index 1b6776e34f..916030ed86 100644 --- a/packages/playwright/src/worker/timeoutManager.ts +++ b/packages/playwright/src/worker/timeoutManager.ts @@ -99,6 +99,7 @@ export class TimeoutManager { running.timeoutPromise, ]); } finally { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (running.timer) { clearTimeout(running.timer); } diff --git a/packages/playwright/src/worker/workerMain.ts b/packages/playwright/src/worker/workerMain.ts index 270e55a187..1e48cb21a8 100644 --- a/packages/playwright/src/worker/workerMain.ts +++ b/packages/playwright/src/worker/workerMain.ts @@ -187,6 +187,7 @@ export class WorkerMain extends ProcessRunner { } private async _loadIfNeeded() { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (this._config) { return; } @@ -215,6 +216,7 @@ export class WorkerMain extends ProcessRunner { const tests = suite.allTests(); for (let i = 0; i < tests.length; i++) { // Do not run tests after full cleanup, because we are entirely done. + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (this._isStopped && this._didRunFullCleanup) { break; } @@ -362,6 +364,7 @@ export class WorkerMain extends ProcessRunner { testFunctionParams = await this._fixtureRunner.resolveParametersForFunction(test.fn, testInfo, 'test', { type: 'test' }); }); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (testFunctionParams === null) { // Fixture setup failed or was skipped, we should not run the test now. return; diff --git a/packages/recorder/src/recorder.tsx b/packages/recorder/src/recorder.tsx index 7f092b6c6b..4f2b25040a 100644 --- a/packages/recorder/src/recorder.tsx +++ b/packages/recorder/src/recorder.tsx @@ -164,7 +164,7 @@ export const Recorder: React.FC = ({ window.dispatch({ event: 'setMode', params: { mode: mode === 'assertingSnapshot' ? 'recording' : 'assertingSnapshot' } }); }}> - { + { copy(source.text); }}> { @@ -182,7 +182,7 @@ export const Recorder: React.FC = ({ setSelectedFileId(fileId); window.dispatch({ event: 'fileChanged', params: { file: fileId } }); }} /> - { + { window.dispatch({ event: 'clear' }); }}> toggleTheme()}> diff --git a/packages/recorder/src/recorderTypes.ts b/packages/recorder/src/recorderTypes.ts index 4822dda46f..7c6b637e8f 100644 --- a/packages/recorder/src/recorderTypes.ts +++ b/packages/recorder/src/recorderTypes.ts @@ -43,7 +43,8 @@ export type EventData = { | 'pause' | 'setMode' | 'highlightRequested' - | 'fileChanged'; + | 'fileChanged' + | 'runTask'; params: any; }; diff --git a/packages/trace-viewer/bundle.ts b/packages/trace-viewer/bundle.ts index 709d11ff7d..ebcae02c9f 100644 --- a/packages/trace-viewer/bundle.ts +++ b/packages/trace-viewer/bundle.ts @@ -21,6 +21,7 @@ export function bundle(): Plugin { name: 'playwright-bundle', transformIndexHtml: { handler(html, ctx) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!ctx || !ctx.bundle) { return html; } diff --git a/packages/trace-viewer/src/index.tsx b/packages/trace-viewer/src/index.tsx index 808cf70e9a..2efeeb7e86 100644 --- a/packages/trace-viewer/src/index.tsx +++ b/packages/trace-viewer/src/index.tsx @@ -26,6 +26,7 @@ import { WorkbenchLoader } from './ui/workbenchLoader'; if (window.location.href.includes('isUnderTest=true')) { await new Promise(f => setTimeout(f, 1000)); } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!navigator.serviceWorker) { throw new Error(`Service workers are not supported.\nMake sure to serve the Trace Viewer (${window.location}) via HTTPS or localhost.`); } diff --git a/packages/trace-viewer/src/recorder.tsx b/packages/trace-viewer/src/recorder.tsx index 7f645c348f..4a959150f0 100644 --- a/packages/trace-viewer/src/recorder.tsx +++ b/packages/trace-viewer/src/recorder.tsx @@ -24,6 +24,7 @@ import { RecorderView } from './ui/recorder/recorderView'; applyTheme(); if (window.location.protocol !== 'file:') { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!navigator.serviceWorker) { throw new Error(`Service workers are not supported.\nMake sure to serve the Recorder (${window.location}) via HTTPS or localhost.`); } diff --git a/packages/trace-viewer/src/sw/snapshotRenderer.ts b/packages/trace-viewer/src/sw/snapshotRenderer.ts index 4f30895500..5d83c82f19 100644 --- a/packages/trace-viewer/src/sw/snapshotRenderer.ts +++ b/packages/trace-viewer/src/sw/snapshotRenderer.ts @@ -358,6 +358,7 @@ function snapshotScript(viewport: ViewportSize, ...targetIds: (string | undefine { const body = root.querySelector(`body[__playwright_custom_elements__]`); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (body && window.customElements) { const customElements = (body.getAttribute('__playwright_custom_elements__') || '').split(','); for (const elementName of customElements) { @@ -415,6 +416,7 @@ frameBoundingRectsInfo.frames.get(element)!.scrollLeft = element.scrollTop; const pointY = +search.get('pointY')!; const hasInputTarget = search.has('hasInputTarget'); const hasTargetElements = targetElements.length > 0; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const roots = document.documentElement ? [document.documentElement] : []; for (const target of (hasTargetElements ? targetElements : roots)) { const pointElement = document.createElement('x-pw-pointer'); diff --git a/packages/trace-viewer/src/ui/callTab.tsx b/packages/trace-viewer/src/ui/callTab.tsx index f3f87c4be6..12221402cd 100644 --- a/packages/trace-viewer/src/ui/callTab.tsx +++ b/packages/trace-viewer/src/ui/callTab.tsx @@ -131,6 +131,7 @@ function parseSerializedValue(value: SerializedValue, handles: any[] | undefined if (value.v === '-Infinity') { return -Infinity; } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (value.v === '-0') { return -0; } diff --git a/packages/trace-viewer/src/ui/consoleTab.tsx b/packages/trace-viewer/src/ui/consoleTab.tsx index 5761242fa1..0001e63a48 100644 --- a/packages/trace-viewer/src/ui/consoleTab.tsx +++ b/packages/trace-viewer/src/ui/consoleTab.tsx @@ -202,6 +202,7 @@ function format(args: { preview: string, value: any }[]): JSX.Element[] { } else if (specifier === 'c') { tokens = []; const format = tail[argIndex++]; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const styleObject = format ? parseCSSStyle(format.preview) : {}; formatted.push({tokens}); } diff --git a/packages/trace-viewer/src/ui/filmStrip.tsx b/packages/trace-viewer/src/ui/filmStrip.tsx index b8bcbdc040..d111e1932f 100644 --- a/packages/trace-viewer/src/ui/filmStrip.tsx +++ b/packages/trace-viewer/src/ui/filmStrip.tsx @@ -58,6 +58,7 @@ export const FilmStrip: React.FunctionComponent<{ width: Math.min(800, (window.innerWidth / 2) | 0), height: Math.min(800, (window.innerHeight / 2) | 0), }; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition previewSize = previewImage ? inscribe({ width: previewImage.width, height: previewImage.height }, fitInto) : undefined; } diff --git a/packages/trace-viewer/src/ui/modelUtil.ts b/packages/trace-viewer/src/ui/modelUtil.ts index 75d5ebcf16..3d694533d2 100644 --- a/packages/trace-viewer/src/ui/modelUtil.ts +++ b/packages/trace-viewer/src/ui/modelUtil.ts @@ -115,7 +115,7 @@ export class MultiTraceModel { private _errorDescriptorsFromActions(): ErrorDescription[] { const errors: ErrorDescription[] = []; - for (const action of this.actions || []) { + for (const action of this.actions) { if (!action.error?.message) { continue; } @@ -130,7 +130,7 @@ export class MultiTraceModel { private _errorDescriptorsFromTestRunner(): ErrorDescription[] { const errors: ErrorDescription[] = []; - for (const error of this.errors || []) { + for (const error of this.errors) { if (!error.message) { continue; } @@ -396,13 +396,14 @@ export function stats(action: ActionTraceEvent): { errors: number, warnings: num } export function eventsForAction(action: ActionTraceEvent): (trace.EventTraceEvent | trace.ConsoleMessageTraceEvent)[] { - let result: (trace.EventTraceEvent | trace.ConsoleMessageTraceEvent)[] = (action as any)[eventsSymbol]; + let result = (action as any)[eventsSymbol] as Array | undefined; if (result) { return result; } const nextAction = nextInContext(action); result = context(action).events.filter(event => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition return event.time >= action.startTime && (!nextAction || event.time < nextAction.startTime); }); (action as any)[eventsSymbol] = result; diff --git a/packages/trace-viewer/src/ui/networkTab.tsx b/packages/trace-viewer/src/ui/networkTab.tsx index 8e57c91bb0..4428b3251f 100644 --- a/packages/trace-viewer/src/ui/networkTab.tsx +++ b/packages/trace-viewer/src/ui/networkTab.tsx @@ -264,6 +264,7 @@ class ContextIdMap { private _apiRequestContextId(resource: Entry): string { const contextEntry = context(resource); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!contextEntry) { return ''; } diff --git a/packages/trace-viewer/src/ui/recorder/actionListView.tsx b/packages/trace-viewer/src/ui/recorder/actionListView.tsx index 8e9fa0df45..b7517d215a 100644 --- a/packages/trace-viewer/src/ui/recorder/actionListView.tsx +++ b/packages/trace-viewer/src/ui/recorder/actionListView.tsx @@ -50,6 +50,7 @@ export const ActionListView: React.FC<{ export const renderAction = (sdkLanguage: Language, action: actionTypes.ActionInContext) => { const { method, apiName, params } = traceParamsForAction(action); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const locator = params.selector ? asLocator(sdkLanguage || 'javascript', params.selector) : undefined; return <> diff --git a/packages/trace-viewer/src/ui/snapshotTab.tsx b/packages/trace-viewer/src/ui/snapshotTab.tsx index 449b7fc55b..0450df6988 100644 --- a/packages/trace-viewer/src/ui/snapshotTab.tsx +++ b/packages/trace-viewer/src/ui/snapshotTab.tsx @@ -322,7 +322,7 @@ export function collectSnapshots(action: ActionTraceEvent | undefined): Snapshot // if the action has no beforeSnapshot, use the last available afterSnapshot. let beforeSnapshot: Snapshot | undefined = action.beforeSnapshot ? { action, snapshotName: action.beforeSnapshot } : undefined; - let a = action; + let a = action as ActionTraceEvent | undefined; while (!beforeSnapshot && a) { a = prevInList(a); beforeSnapshot = a.afterSnapshot ? { action: a, snapshotName: a.afterSnapshot } : undefined; diff --git a/packages/trace-viewer/src/ui/uiModeTraceView.tsx b/packages/trace-viewer/src/ui/uiModeTraceView.tsx index 1acb69a780..22dca6992b 100644 --- a/packages/trace-viewer/src/ui/uiModeTraceView.tsx +++ b/packages/trace-viewer/src/ui/uiModeTraceView.tsx @@ -53,7 +53,7 @@ export const TraceView: React.FC<{ } // Test finished. - const attachment = result && result.duration >= 0 && result.attachments.find(a => a.name === 'trace'); + const attachment = result.duration >= 0 && result.attachments.find(a => a.name === 'trace'); if (attachment && attachment.path) { loadSingleTraceFile(attachment.path).then(model => setModel({ model, isLive: false })); return; diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx index 1eab9901a5..fd8cf7204e 100644 --- a/packages/trace-viewer/src/ui/uiModeView.tsx +++ b/packages/trace-viewer/src/ui/uiModeView.tsx @@ -168,6 +168,7 @@ export const UIModeView: React.FC<{}> = ({ throttleTimer = undefined; if (immediate) { setTestModel(teleSuiteUpdater.asModel()); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (!throttleTimer) { throttleTimer = setTimeout(() => { setTestModel(teleSuiteUpdater.asModel()); @@ -284,6 +285,7 @@ export const UIModeView: React.FC<{}> = ({ // Clear test results. { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition for (const test of testModel.rootSuite.allTests() || []) { if (testIds.has(test.id)) { test.results = []; @@ -312,6 +314,7 @@ export const UIModeView: React.FC<{}> = ({ trace: 'on', }); // Clear pending tests in case of interrupt. + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition for (const test of testModel.rootSuite.allTests() || []) { if (test.results[0]?.duration === -1) { test.results = []; @@ -525,6 +528,7 @@ export const UIModeView: React.FC<{}> = ({ setFilterText={setFilterText} onRevealSource={onRevealSource} /> + {/* eslint-disable-next-line @typescript-eslint/no-unnecessary-condition */} {showTestingOptions && <> setTestingOptionsVisible(!testingOptionsVisible)}> setTimeout(f, 1000)); } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!navigator.serviceWorker) { throw new Error(`Service workers are not supported.\nMake sure to serve the website (${window.location}) via HTTPS or localhost.`); } diff --git a/packages/web/src/components/codeMirrorWrapper.tsx b/packages/web/src/components/codeMirrorWrapper.tsx index d64773d4d1..f7bbc2dfb2 100644 --- a/packages/web/src/components/codeMirrorWrapper.tsx +++ b/packages/web/src/components/codeMirrorWrapper.tsx @@ -190,7 +190,7 @@ export const CodeMirrorWrapper: React.FC = ({ codemirror.scrollIntoView({ line: Math.max(0, revealLine - 1), ch: 0 }, 50); } - let changeListener: () => void | undefined; + let changeListener: (() => void | undefined) | undefined; if (onChange) { changeListener = () => onChange(codemirror.getValue()); codemirror.on('change', changeListener); diff --git a/packages/web/src/components/splitView.tsx b/packages/web/src/components/splitView.tsx index 5833e44031..7490ca8bf4 100644 --- a/packages/web/src/components/splitView.tsx +++ b/packages/web/src/components/splitView.tsx @@ -52,12 +52,12 @@ export const SplitView: React.FC = ({ let size: number; if (orientation === 'vertical') { size = vSize / window.devicePixelRatio; - if (measure && measure.height < size) { + if (measure.height < size) { size = measure.height - 10; } } else { size = hSize / window.devicePixelRatio; - if (measure && measure.width < size) { + if (measure.width < size) { size = measure.width - 10; } } diff --git a/packages/web/src/components/treeView.tsx b/packages/web/src/components/treeView.tsx index 1f77066ae5..dffd429680 100644 --- a/packages/web/src/components/treeView.tsx +++ b/packages/web/src/components/treeView.tsx @@ -235,7 +235,7 @@ export function TreeItemHeader({ isKeyboardNavigation, setIsKeyboardNavigation }: TreeItemHeaderProps) { const groupId = React.useId(); - const itemRef = React.useRef(null); + const itemRef = React.useRef(null); React.useEffect(() => { if (selectedItem === item && isKeyboardNavigation && itemRef.current) { diff --git a/packages/web/src/shared/resizeView.tsx b/packages/web/src/shared/resizeView.tsx index 72b58882df..984313d77f 100644 --- a/packages/web/src/shared/resizeView.tsx +++ b/packages/web/src/shared/resizeView.tsx @@ -68,6 +68,7 @@ export const ResizeView: React.FC<{ onPaneMouseMove={event => { if (!event.buttons) { setResizing(null); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (resizing) { const delta = orientation === 'horizontal' ? event.clientX - resizing.clientX : event.clientY - resizing.clientY; const newOffset = resizing.offset + delta; diff --git a/packages/web/src/theme.ts b/packages/web/src/theme.ts index b709daef60..b80924a5bb 100644 --- a/packages/web/src/theme.ts +++ b/packages/web/src/theme.ts @@ -51,9 +51,7 @@ export function toggleTheme() { const oldTheme = currentTheme(); const newTheme = oldTheme === 'dark-mode' ? 'light-mode' : 'dark-mode'; - if (oldTheme) { - document.body.classList.remove(oldTheme); - } + document.body.classList.remove(oldTheme); document.body.classList.add(newTheme); settings.setString('theme', newTheme); for (const listener of listeners) {