diff --git a/utils/generate_types/index.js b/utils/generate_types/index.js index f6779ce06b..56c81b2032 100644 --- a/utils/generate_types/index.js +++ b/utils/generate_types/index.js @@ -69,7 +69,7 @@ let documentation; ${overrides} ${classes.map(classDesc => classToString(classDesc)).join('\n')} -${objectDefinitionsToString()} +${objectDefinitionsToString(overrides)} ${generateDevicesTypes()} `; for (const [key, value] of Object.entries(exported)) @@ -80,14 +80,20 @@ ${generateDevicesTypes()} process.exit(1); }); -function objectDefinitionsToString() { +/** + * @param {string} overriddes + */ +function objectDefinitionsToString(overriddes) { let definition; const parts = []; + const internalWords = new Set(overriddes.split(/[^\w$]/g)); while ((definition = objectDefinitions.pop())) { const {name, properties} = definition; - parts.push(`${exported[name] ? 'export ' : ''}interface ${name} {`); - parts.push(properties.map(member => `${memberJSDOC(member, ' ')}${nameForProperty(member)}${argsFromMember(member, name)}: ${typeToString(member.type, name, member.name)};`).join('\n\n')); - parts.push('}\n'); + const shouldExport = !!exported[name]; + const usedInternally = internalWords.has(name); + if (!usedInternally && !shouldExport) + continue; + parts.push(`${shouldExport ? 'export ' : ''}interface ${name} ${stringifyObjectType(properties, name, '')}\n`) } return parts.join('\n'); } @@ -141,9 +147,9 @@ function createEventDescriptions(classDesc) { return []; const descriptions = []; for (const [eventName, value] of classDesc.events) { - const type = typeToString(value && value.type, classDesc.name, eventName, 'payload'); + const type = stringifyComplexType(value && value.type, '', classDesc.name, eventName, 'payload'); const argName = argNameForType(type); - const params = argName ? `${argName} : ${type}` : ''; + const params = argName ? `${argName}: ${type}` : ''; descriptions.push({ type, params, @@ -183,8 +189,8 @@ function classBody(classDesc) { return parts.join('\n'); } const jsdoc = memberJSDOC(member, ' '); - const args = argsFromMember(member, classDesc.name); - const type = typeToString(member.type, classDesc.name, member.name); + const args = argsFromMember(member, ' ', classDesc.name); + const type = stringifyComplexType(member.type, ' ', classDesc.name, member.name); // do this late, because we still want object definitions for overridden types if (!hasOwnMethod(classDesc, member.name)) return ''; @@ -229,21 +235,32 @@ function writeComment(comment, indent = '') { /** * @param {Documentation.Type} type */ -function typeToString(type, ...namespace) { +function stringifyComplexType(type, indent, ...namespace) { if (!type) return 'void'; - // Accessibility.snapshot has a recursive data structure, so special case it here. - if (namespace[0] === 'AccessibilitySnapshot' && namespace[1] === 'children') - return 'Array'; - let typeString = stringifyType(parseType(type.name)); + let typeString = stringifySimpleType(parseType(type.name)); if (type.properties.length && typeString.indexOf('Object') !== -1) { const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join(''); - typeString = typeString.replace('Object', name); + const shouldExport = exported[name]; objectDefinitions.push({name, properties: type.properties}); + if (shouldExport) { + typeString = typeString.replace(/Object/g, name); + } else { + const objType = stringifyObjectType(type.properties, name, indent); + typeString = typeString.replace(/Object/g, objType); + } } return typeString; } +function stringifyObjectType(properties, name, indent = '') { + const parts = []; + parts.push(`{`); + parts.push(properties.map(member => `${memberJSDOC(member, indent + ' ')}${nameForProperty(member)}${argsFromMember(member, indent + ' ', name)}: ${stringifyComplexType(member.type, indent + ' ', name, member.name)};`).join('\n\n')); + parts.push(indent + '}'); + return parts.join('\n'); +} + /** * @param {string} type */ @@ -308,15 +325,15 @@ function parseType(type) { /** * @return {string} */ -function stringifyType(parsedType) { +function stringifySimpleType(parsedType) { if (!parsedType) return 'void'; if (parsedType.name === 'Object' && parsedType.template) { - const keyType = stringifyType({ + const keyType = stringifySimpleType({ ...parsedType.template, next: null }); - const valueType = stringifyType(parsedType.template.next); + const valueType = stringifySimpleType(parsedType.template.next); return `{ [key: ${keyType}]: ${valueType}; }`; } let out = parsedType.name; @@ -327,20 +344,23 @@ function stringifyType(parsedType) { const arg = args; args = args.next; arg.next = null; - stringArgs.push(stringifyType(arg)); + stringArgs.push({ + type: stringifySimpleType(arg), + name: arg.name.toLowerCase() + }); } - out = `((${stringArgs.map((type, index) => `arg${index} : ${type}`).join(', ')}) => ${stringifyType(parsedType.retType)})`; + out = `((${stringArgs.map(({name, type}) => `${name}: ${type}`).join(', ')}) => ${stringifySimpleType(parsedType.retType)})`; } else if (parsedType.name === 'function') { out = 'Function'; } if (parsedType.nullable) out = 'null|' + out; if (parsedType.template) - out += '<' + stringifyType(parsedType.template) + '>'; + out += '<' + stringifySimpleType(parsedType.template) + '>'; if (parsedType.pipe) - out += '|' + stringifyType(parsedType.pipe); + out += '|' + stringifySimpleType(parsedType.pipe); if (parsedType.next) - out += ', ' + stringifyType(parsedType.next); + out += ', ' + stringifySimpleType(parsedType.next); return out.trim(); } @@ -359,10 +379,10 @@ function matchingBracket(str, open, close) { /** * @param {Documentation.Member} member */ -function argsFromMember(member, ...namespace) { +function argsFromMember(member, indent, ...namespace) { if (member.kind === 'property') return ''; - return '(' + member.argsArray.map(arg => `${nameForProperty(arg)}: ${typeToString(arg.type, ...namespace, member.name, arg.name)}`).join(', ') + ')'; + return '(' + member.argsArray.map(arg => `${nameForProperty(arg)}: ${stringifyComplexType(arg.type, indent, ...namespace, member.name, arg.name)}`).join(', ') + ')'; } /** * @param {Documentation.Member} member diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index 490a9c8445..9b76dba3bf 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -43,8 +43,6 @@ type PageFunction = string | ((arg: Unboxed) => R | Promise); type PageFunctionOn = string | ((on: On, arg2: Unboxed) => R | Promise); type SmartHandle = T extends Node ? ElementHandle : JSHandle; type ElementHandleForTag = ElementHandle; -type HTMLOrSVGElement = SVGElement | HTMLElement; -type HTMLOrSVGElementHandle = ElementHandle; type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & { state: 'visible'|'attached'; @@ -61,28 +59,28 @@ export interface Page { evaluateHandle(pageFunction: PageFunction, arg?: any): Promise>; $(selector: K): Promise | null>; - $(selector: string): Promise; + $(selector: string): Promise | null>; $$(selector: K): Promise[]>; - $$(selector: string): Promise; + $$(selector: string): Promise[]>; $eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; waitForFunction(pageFunction: PageFunction, arg: Arg, options?: PageWaitForFunctionOptions): Promise>; waitForFunction(pageFunction: PageFunction, arg?: any, options?: PageWaitForFunctionOptions): Promise>; waitForSelector(selector: K, options?: PageWaitForSelectorOptionsNotHidden): Promise>; - waitForSelector(selector: string, options?: PageWaitForSelectorOptionsNotHidden): Promise; + waitForSelector(selector: string, options?: PageWaitForSelectorOptionsNotHidden): Promise>; waitForSelector(selector: K, options: PageWaitForSelectorOptions): Promise | null>; - waitForSelector(selector: string, options: PageWaitForSelectorOptions): Promise; + waitForSelector(selector: string, options: PageWaitForSelectorOptions): Promise>; } export interface Frame { @@ -93,28 +91,28 @@ export interface Frame { evaluateHandle(pageFunction: PageFunction, arg?: any): Promise>; $(selector: K): Promise | null>; - $(selector: string): Promise; + $(selector: string): Promise | null>; $$(selector: K): Promise[]>; - $$(selector: string): Promise; + $$(selector: string): Promise[]>; $eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; waitForFunction(pageFunction: PageFunction, arg: Arg, options?: PageWaitForFunctionOptions): Promise>; waitForFunction(pageFunction: PageFunction, arg?: any, options?: PageWaitForFunctionOptions): Promise>; waitForSelector(selector: K, options?: PageWaitForSelectorOptionsNotHidden): Promise>; - waitForSelector(selector: string, options?: PageWaitForSelectorOptionsNotHidden): Promise; + waitForSelector(selector: string, options?: PageWaitForSelectorOptionsNotHidden): Promise>; waitForSelector(selector: K, options: PageWaitForSelectorOptions): Promise | null>; - waitForSelector(selector: string, options: PageWaitForSelectorOptions): Promise; + waitForSelector(selector: string, options: PageWaitForSelectorOptions): Promise>; } export interface Worker { @@ -138,25 +136,25 @@ export interface JSHandle { export interface ElementHandle extends JSHandle { $(selector: K): Promise | null>; - $(selector: string): Promise; + $(selector: string): Promise | null>; $$(selector: K): Promise[]>; - $$(selector: string): Promise; + $$(selector: string): Promise[]>; $eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; waitForSelector(selector: K, options?: ElementHandleWaitForSelectorOptionsNotHidden): Promise>; - waitForSelector(selector: string, options?: ElementHandleWaitForSelectorOptionsNotHidden): Promise; + waitForSelector(selector: string, options?: ElementHandleWaitForSelectorOptionsNotHidden): Promise>; waitForSelector(selector: K, options: ElementHandleWaitForSelectorOptions): Promise | null>; - waitForSelector(selector: string, options: ElementHandleWaitForSelectorOptions): Promise; + waitForSelector(selector: string, options: ElementHandleWaitForSelectorOptions): Promise>; } export interface BrowserType { @@ -165,7 +163,7 @@ export interface BrowserType { export interface ChromiumBrowser extends Browser { contexts(): Array; - newContext(options?: BrowserNewContextOptions): Promise; + newContext(options?: BrowserContextOptions): Promise; } export interface CDPSession { @@ -181,7 +179,7 @@ export interface CDPSession { } type DeviceDescriptor = { - viewport: BrowserNewContextOptionsViewport; + viewport: ViewportSize; userAgent: string; deviceScaleFactor: number; isMobile: boolean; @@ -194,6 +192,49 @@ class TimeoutError extends Error {} } +export interface Accessibility { + snapshot(options?: { + /** + * Prune uninteresting nodes from the tree. Defaults to `true`. + */ + interestingOnly?: boolean; + + /** + * The root DOM element for the snapshot. Defaults to the whole page. + */ + root?: ElementHandle; + }): Promise; +} + +type AccessibilityNode = { + role: string; + name: string; + value?: string|number; + description?: string; + keyshortcuts?: string; + roledescription?: string; + valuetext?: string; + disabled?: boolean; + expanded?: boolean; + focused?: boolean; + modal?: boolean; + multiline?: boolean; + multiselectable?: boolean; + readonly?: boolean; + required?: boolean; + selected?: boolean; + checked?: boolean|"mixed"; + pressed?: boolean|"mixed"; + level?: number; + valuemin?: number; + valuemax?: number; + autocomplete?: string; + haspopup?: string; + invalid?: string; + orientation?: string; + children?: AccessibilityNode[]; +} + export const selectors: Selectors; export const devices: Devices & DeviceDescriptor[]; diff --git a/utils/generate_types/test/test.ts b/utils/generate_types/test/test.ts index 9168b88d06..a461437e15 100644 --- a/utils/generate_types/test/test.ts +++ b/utils/generate_types/test/test.ts @@ -189,6 +189,12 @@ playwright.chromium.launch().then(async browser => { const inputElement = (await page.$('input[type=submit]'))!; await inputElement.click(); + + await inputElement.setInputFiles([{ + name: 'yo', + mimeType: 'text/plain', + buffer: Buffer.from('yo') + }]) }); // Example with launch options