Skip to content

Commit

Permalink
feat: use strict typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
jrandolf committed May 30, 2022
1 parent c7aa07e commit 5268be4
Show file tree
Hide file tree
Showing 43 changed files with 609 additions and 475 deletions.
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -49,7 +49,7 @@
"generate-api-docs-for-testing": "commonmark docs/api.md > docs/api.html",
"clean-lib": "rimraf lib",
"build": "npm run tsc && npm run generate-d-ts && npm run generate-esm-package-json",
"tsc": "npm run clean-lib && tsc --version && (npm run tsc-cjs & npm run tsc-esm) && (npm run tsc-compat-cjs & npm run tsc-compat-esm)",
"tsc": "npm run clean-lib && tsc --version && (npm run tsc-cjs && npm run tsc-esm) && (npm run tsc-compat-cjs && npm run tsc-compat-esm)",
"tsc-cjs": "tsc -b src/tsconfig.cjs.json",
"tsc-esm": "tsc -b src/tsconfig.esm.json",
"tsc-compat-cjs": "tsc -b compat/cjs/tsconfig.json",
Expand Down Expand Up @@ -107,6 +107,7 @@
"@types/rimraf": "3.0.2",
"@types/sinon": "10.0.11",
"@types/tar-fs": "2.0.1",
"@types/unbzip2-stream": "1.4.0",

This comment has been minimized.

Copy link
@OrKoN

OrKoN May 30, 2022

Collaborator

why is it needed?

This comment has been minimized.

Copy link
@jrandolf

jrandolf May 30, 2022

Author Contributor

Because unbzip2-stream does not have types.

"@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "5.23.0",
"@typescript-eslint/parser": "5.22.0",
Expand Down
2 changes: 1 addition & 1 deletion scripts/ensure-pinned-deps.ts
Expand Up @@ -21,7 +21,7 @@ const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
const invalidDeps = new Map<string, string>();

for (const [depKey, depValue] of Object.entries(allDeps)) {
if (/[0-9]/.test(depValue[0])) {
if (/[0-9]/.test(depValue[0]!)) {
continue;
}

Expand Down
12 changes: 6 additions & 6 deletions src/common/Accessibility.ts
Expand Up @@ -180,30 +180,30 @@ export class Accessibility {
*/
public async snapshot(
options: SnapshotOptions = {}
): Promise<SerializedAXNode> {
): Promise<SerializedAXNode | null> {

This comment has been minimized.

Copy link
@OrKoN

OrKoN May 30, 2022

Collaborator

the API doc says that should not be null?

This comment has been minimized.

Copy link
@jrandolf

jrandolf May 30, 2022

Author Contributor

See line 199; this is not guaranteed.

const { interestingOnly = true, root = null } = options;
const { nodes } = await this._client.send('Accessibility.getFullAXTree');
let backendNodeId = null;
let backendNodeId: number | undefined;
if (root) {
const { node } = await this._client.send('DOM.describeNode', {
objectId: root._remoteObject.objectId,
});
backendNodeId = node.backendNodeId;
}
const defaultRoot = AXNode.createTree(nodes);
let needle = defaultRoot;
let needle: AXNode | null = defaultRoot;
if (backendNodeId) {
needle = defaultRoot.find(
(node) => node.payload.backendDOMNodeId === backendNodeId
);
if (!needle) return null;
}
if (!interestingOnly) return this.serializeTree(needle)[0];
if (!interestingOnly) return this.serializeTree(needle)[0] ?? null;

const interestingNodes = new Set<AXNode>();
this.collectInterestingNodes(interestingNodes, defaultRoot, false);
if (!interestingNodes.has(needle)) return null;
return this.serializeTree(needle, interestingNodes)[0];
return this.serializeTree(needle, interestingNodes)[0] ?? null;
}

private serializeTree(
Expand Down Expand Up @@ -496,7 +496,7 @@ class AXNode {
nodeById.set(payload.nodeId, new AXNode(payload));
for (const node of nodeById.values()) {
for (const childId of node.payload.childIds || [])
node.children.push(nodeById.get(childId));
node.children.push(nodeById.get(childId)!);
}
return nodeById.values().next().value;
}
Expand Down
32 changes: 23 additions & 9 deletions src/common/AriaQueryHandler.ts
Expand Up @@ -19,6 +19,7 @@ import { ElementHandle, JSHandle } from './JSHandle.js';
import { Protocol } from 'devtools-protocol';
import { CDPSession } from './Connection.js';
import { DOMWorld, PageBinding, WaitForSelectorOptions } from './DOMWorld.js';
import { assert } from './assert.js';

async function queryAXTree(
client: CDPSession,
Expand All @@ -32,7 +33,8 @@ async function queryAXTree(
role,
});
const filteredNodes: Protocol.Accessibility.AXNode[] = nodes.filter(
(node: Protocol.Accessibility.AXNode) => node.role.value !== 'StaticText'
(node: Protocol.Accessibility.AXNode) =>
!node.role || node.role.value !== 'StaticText'
);
return filteredNodes;
}
Expand All @@ -43,6 +45,13 @@ const knownAttributes = new Set(['name', 'role']);
const attributeRegexp =
/\[\s*(?<attribute>\w+)\s*=\s*(?<quote>"|')(?<value>\\.|.*?(?=\k<quote>))\k<quote>\s*\]/g;

type ARIAQueryOption = { name?: string; role?: string };
function isKnownAttribute(
attribute: string
): attribute is keyof ARIAQueryOption {
return knownAttributes.has(attribute);
}

/*
* The selectors consist of an accessible name to query for and optionally
* further aria attributes on the form `[<attribute>=<value>]`.
Expand All @@ -53,15 +62,16 @@ const attributeRegexp =
* - 'label' queries for elements with name 'label' and any role.
* - '[name=""][role="button"]' queries for elements with no name and role 'button'.
*/
type ariaQueryOption = { name?: string; role?: string };
function parseAriaSelector(selector: string): ariaQueryOption {
const queryOptions: ariaQueryOption = {};
function parseAriaSelector(selector: string): ARIAQueryOption {
const queryOptions: ARIAQueryOption = {};
const defaultName = selector.replace(
attributeRegexp,
(_, attribute: string, quote: string, value: string) => {
(_, attribute: string, _quote: string, value: string) => {
attribute = attribute.trim();
if (!knownAttributes.has(attribute))
throw new Error(`Unknown aria attribute "${attribute}" in selector`);
assert(
isKnownAttribute(attribute),
`Unknown aria attribute "${attribute}" in selector`
);
queryOptions[attribute] = normalizeValue(value);
return '';
}
Expand All @@ -78,17 +88,21 @@ const queryOne = async (
const exeCtx = element.executionContext();
const { name, role } = parseAriaSelector(selector);
const res = await queryAXTree(exeCtx._client, element, name, role);
if (res.length < 1) {
if (!res[0] || !res[0].backendDOMNodeId) {
return null;
}
return exeCtx._adoptBackendNodeId(res[0].backendDOMNodeId);
};

declare global {
function ariaQuerySelector(selector: string): void;
}

const waitFor = async (
domWorld: DOMWorld,
selector: string,
options: WaitForSelectorOptions
): Promise<ElementHandle<Element>> => {
): Promise<ElementHandle<Element> | null> => {
const binding: PageBinding = {
name: 'ariaQuerySelector',
pptrFunction: async (selector: string) => {
Expand Down
29 changes: 10 additions & 19 deletions src/common/Browser.ts
Expand Up @@ -246,7 +246,7 @@ export class Browser extends EventEmitter {
private _connection: Connection;
private _closeCallback: BrowserCloseCallback;
private _targetFilterCallback: TargetFilterCallback;
private _isPageTargetCallback: IsPageTargetCallback;
private _isPageTargetCallback!: IsPageTargetCallback;
private _defaultContext: BrowserContext;
private _contexts: Map<string, BrowserContext>;
private _screenshotTaskQueue: TaskQueue;
Expand Down Expand Up @@ -572,33 +572,24 @@ export class Browser extends EventEmitter {
): Promise<Target> {
const { timeout = 30000 } = options;
let resolve: (value: Target | PromiseLike<Target>) => void;
let isResolved = false;
const targetPromise = new Promise<Target>((x) => (resolve = x));
this.on(BrowserEmittedEvents.TargetCreated, check);
this.on(BrowserEmittedEvents.TargetChanged, check);
try {
if (!timeout) return await targetPromise;
return await helper.waitWithTimeout<Target>(
Promise.race([
targetPromise,
(async () => {
for (const target of this.targets()) {
if (await predicate(target)) {
return target;
}
}
await targetPromise;
})(),
]),
'target',
timeout
);
this.targets().forEach(check);
return await helper.waitWithTimeout(targetPromise, 'target', timeout);
} finally {
this.removeListener(BrowserEmittedEvents.TargetCreated, check);
this.removeListener(BrowserEmittedEvents.TargetChanged, check);
this.off(BrowserEmittedEvents.TargetCreated, check);
this.off(BrowserEmittedEvents.TargetChanged, check);
}

async function check(target: Target): Promise<void> {
if (await predicate(target)) resolve(target);
if (!isResolved && (await predicate(target))) {
isResolved = true;
resolve(target);
}
}
}

Expand Down
12 changes: 7 additions & 5 deletions src/common/BrowserConnector.ts
Expand Up @@ -93,7 +93,7 @@ export const connectToBrowser = async (
'Exactly one of browserWSEndpoint, browserURL or transport must be passed to puppeteer.connect'
);

let connection = null;
let connection!: Connection;
if (transport) {
connection = new Connection('', transport, slowMo);
} else if (browserWSEndpoint) {
Expand All @@ -117,7 +117,7 @@ export const connectToBrowser = async (
browserContextIds,
ignoreHTTPSErrors,
defaultViewport,
null,
undefined,
() => connection.send('Browser.close').catch(debugError),
targetFilter,
isPageTarget
Expand All @@ -138,9 +138,11 @@ async function getWSEndpoint(browserURL: string): Promise<string> {
const data = await result.json();
return data.webSocketDebuggerUrl;
} catch (error) {
error.message =
`Failed to fetch browser webSocket URL from ${endpointURL}: ` +
error.message;
if (error instanceof Error) {
error.message =
`Failed to fetch browser webSocket URL from ${endpointURL}: ` +
error.message;
}
throw error;
}
}
2 changes: 0 additions & 2 deletions src/common/BrowserWebSocketTransport.ts
Expand Up @@ -41,8 +41,6 @@ export class BrowserWebSocketTransport implements ConnectionTransport {
});
// Silently ignore all errors - we don't know what to do with them.
this._ws.addEventListener('error', () => {});
this.onmessage = null;
this.onclose = null;
}

send(message: string): void {
Expand Down
2 changes: 1 addition & 1 deletion src/common/ConsoleMessage.ts
Expand Up @@ -111,7 +111,7 @@ export class ConsoleMessage {
* @returns The location of the console message.
*/
location(): ConsoleMessageLocation {
return this._stackTraceLocations.length ? this._stackTraceLocations[0] : {};
return this._stackTraceLocations[0] ?? {};
}

/**
Expand Down
13 changes: 9 additions & 4 deletions src/common/Coverage.ts
Expand Up @@ -395,10 +395,12 @@ export class CSSCoverage {
});
}

const coverage = [];
const coverage: CoverageEntry[] = [];
for (const styleSheetId of this._stylesheetURLs.keys()) {
const url = this._stylesheetURLs.get(styleSheetId);
assert(url);
const text = this._stylesheetSources.get(styleSheetId);
assert(text);
const ranges = convertToDisjointRanges(
styleSheetIdToCoverage.get(styleSheetId) || []
);
Expand Down Expand Up @@ -432,16 +434,19 @@ function convertToDisjointRanges(
});

const hitCountStack = [];
const results = [];
const results: Array<{
start: number;
end: number;
}> = [];
let lastOffset = 0;
// Run scanning line to intersect all ranges.
for (const point of points) {
if (
hitCountStack.length &&
lastOffset < point.offset &&
hitCountStack[hitCountStack.length - 1] > 0
hitCountStack[hitCountStack.length - 1]! > 0
) {
const lastResult = results.length ? results[results.length - 1] : null;
const lastResult = results[results.length - 1];
if (lastResult && lastResult.end === lastOffset)
lastResult.end = point.offset;
else results.push({ start: lastOffset, end: point.offset });
Expand Down
29 changes: 15 additions & 14 deletions src/common/DOMWorld.ts
Expand Up @@ -501,37 +501,37 @@ export class DOMWorld {
): Promise<void> {
const handle = await this.$(selector);
assert(handle, 'No node found for selector: ' + selector);
await handle!.click(options);
await handle!.dispose();
await handle.click(options);
await handle.dispose();
}

async focus(selector: string): Promise<void> {
const handle = await this.$(selector);
assert(handle, 'No node found for selector: ' + selector);
await handle!.focus();
await handle!.dispose();
await handle.focus();
await handle.dispose();
}

async hover(selector: string): Promise<void> {
const handle = await this.$(selector);
assert(handle, 'No node found for selector: ' + selector);
await handle!.hover();
await handle!.dispose();
await handle.hover();
await handle.dispose();
}

async select(selector: string, ...values: string[]): Promise<string[]> {
const handle = await this.$(selector);
assert(handle, 'No node found for selector: ' + selector);
const result = await handle!.select(...values);
await handle!.dispose();
const result = await handle.select(...values);
await handle.dispose();
return result;
}

async tap(selector: string): Promise<void> {
const handle = await this.$(selector);
assert(handle, 'No node found for selector: ' + selector);
await handle!.tap();
await handle!.dispose();
await handle.tap();
await handle.dispose();
}

async type(
Expand All @@ -541,8 +541,8 @@ export class DOMWorld {
): Promise<void> {
const handle = await this.$(selector);
assert(handle, 'No node found for selector: ' + selector);
await handle!.type(text, options);
await handle!.dispose();
await handle.type(text, options);
await handle.dispose();
}

async waitForSelector(
Expand Down Expand Up @@ -981,6 +981,7 @@ async function waitForPredicatePageFunction(
if (polling === 'raf') return await pollRaf();
if (polling === 'mutation') return await pollMutation();
if (typeof polling === 'number') return await pollInterval(polling);
assert(false);

/**
* @returns {!Promise<*>}
Expand Down Expand Up @@ -1023,7 +1024,7 @@ async function waitForPredicatePageFunction(
await onRaf();
return result;

async function onRaf(): Promise<unknown> {
async function onRaf(): Promise<void> {
if (timedOut) {
fulfill();
return;
Expand All @@ -1042,7 +1043,7 @@ async function waitForPredicatePageFunction(
await onTimeout();
return result;

async function onTimeout(): Promise<unknown> {
async function onTimeout(): Promise<void> {
if (timedOut) {
fulfill();
return;
Expand Down
7 changes: 6 additions & 1 deletion src/common/Debug.ts
Expand Up @@ -16,6 +16,11 @@

import { isNode } from '../environment.js';

declare global {
// eslint-disable-next-line no-var
var __PUPPETEER_DEBUG: string;
}

/**
* A debug function that can be used in any environment.
*
Expand Down Expand Up @@ -60,7 +65,7 @@ export const debug = (prefix: string): ((...args: unknown[]) => void) => {
}

return (...logArgs: unknown[]): void => {
const debugLevel = globalThis.__PUPPETEER_DEBUG as string;
const debugLevel = globalThis.__PUPPETEER_DEBUG;
if (!debugLevel) return;

const everythingShouldBeLogged = debugLevel === '*';
Expand Down

0 comments on commit 5268be4

Please sign in to comment.