diff --git a/docs/src/api/class-route.md b/docs/src/api/class-route.md index 139d7e95ff9a0..8b60f563a56c2 100644 --- a/docs/src/api/class-route.md +++ b/docs/src/api/class-route.md @@ -112,10 +112,18 @@ If set changes the request method (e.g. GET or POST) ### option: Route.continue.postData * since: v1.8 +* langs: js, python, java - `postData` <[string]|[Buffer]> If set changes the post data of request +### option: Route.continue.postData +* since: v1.8 +* langs: csharp +- `postData` <[Buffer]> + +If set changes the post data of request + ### option: Route.continue.headers * since: v1.8 - `headers` <[Object]<[string], [string]>> @@ -378,10 +386,18 @@ If set changes the request method (e.g. GET or POST) ### option: Route.fallback.postData * since: v1.23 +* langs: js, python, java - `postData` <[string]|[Buffer]> If set changes the post data of request +### option: Route.fallback.postData +* since: v1.23 +* langs: csharp +- `postData` <[Buffer]> + +If set changes the post data of request + ### option: Route.fallback.headers * since: v1.23 - `headers` <[Object]<[string], [string]>> diff --git a/docs/src/chrome-extensions-js-python.md b/docs/src/chrome-extensions-js-python.md index 04a84f56cc9e2..648d4b3b8574e 100644 --- a/docs/src/chrome-extensions-js-python.md +++ b/docs/src/chrome-extensions-js-python.md @@ -103,8 +103,8 @@ First, add fixtures that will load the extension: ```ts // fixtures.ts -import { test as base, expect, chromium, type BrowserContext } from "@playwright/test"; -import path from "path"; +import { test as base, expect, chromium, type BrowserContext } from '@playwright/test'; +import path from 'path'; export const test = base.extend<{ context: BrowserContext; @@ -185,16 +185,16 @@ def extension_id(context) -> Generator[str, None, None]: Then use these fixtures in a test: ```ts -import { test, expect } from "./fixtures"; +import { test, expect } from './fixtures'; -test("example test", async ({ page }) => { - await page.goto("https://example.com"); - await expect(page.locator("body")).toHaveText("Changed by my-extension"); +test('example test', async ({ page }) => { + await page.goto('https://example.com'); + await expect(page.locator('body')).toHaveText('Changed by my-extension'); }); -test("popup page", async ({ page, extensionId }) => { +test('popup page', async ({ page, extensionId }) => { await page.goto(`chrome-extension://${extensionId}/popup.html`); - await expect(page.locator("body")).toHaveText("my-extension popup"); + await expect(page.locator('body')).toHaveText('my-extension popup'); }); ``` diff --git a/utils/doclint/documentation.js b/utils/doclint/documentation.js index d912cdd4ad865..fa0f867c11937 100644 --- a/utils/doclint/documentation.js +++ b/utils/doclint/documentation.js @@ -59,6 +59,12 @@ const md = require('../markdown'); * }} Metainfo */ +/** + * @typedef {{ + * csharpOptionOverloadsShortNotation?: boolean, + * }} LanguageOptions + */ + class Documentation { /** * @param {!Array} classesArray @@ -104,13 +110,14 @@ class Documentation { /** * @param {string} lang + * @param {LanguageOptions=} options */ - filterForLanguage(lang) { + filterForLanguage(lang, options = {}) { const classesArray = []; for (const clazz of this.classesArray) { if (clazz.langs.only && !clazz.langs.only.includes(lang)) continue; - clazz.filterForLanguage(lang); + clazz.filterForLanguage(lang, options); classesArray.push(clazz); } this.classesArray = classesArray; @@ -259,13 +266,14 @@ Documentation.Class = class { /** * @param {string} lang + * @param {LanguageOptions=} options */ - filterForLanguage(lang) { + filterForLanguage(lang, options = {}) { const membersArray = []; for (const member of this.membersArray) { if (member.langs.only && !member.langs.only.includes(lang)) continue; - member.filterForLanguage(lang); + member.filterForLanguage(lang, options); membersArray.push(member); } this.membersArray = membersArray; @@ -406,31 +414,41 @@ Documentation.Member = class { } } - /** + /** * @param {string} lang + * @param {LanguageOptions=} options */ - filterForLanguage(lang) { + filterForLanguage(lang, options = {}) { if (!this.type) return; if (this.langs.aliases && this.langs.aliases[lang]) this.alias = this.langs.aliases[lang]; if (this.langs.types && this.langs.types[lang]) this.type = this.langs.types[lang]; - this.type.filterForLanguage(lang); + this.type.filterForLanguage(lang, options); const argsArray = []; for (const arg of this.argsArray) { if (arg.langs.only && !arg.langs.only.includes(lang)) continue; const overriddenArg = (arg.langs.overrides && arg.langs.overrides[lang]) || arg; - overriddenArg.filterForLanguage(lang); + overriddenArg.filterForLanguage(lang, options); // @ts-ignore if (overriddenArg.name === 'options' && !overriddenArg.type.properties.length) continue; // @ts-ignore - overriddenArg.type.filterForLanguage(lang); + overriddenArg.type.filterForLanguage(lang, options); argsArray.push(overriddenArg); } this.argsArray = argsArray; + + const optionsArg = this.argsArray.find(arg => arg.name === 'options'); + if (lang === 'csharp' && optionsArg) { + try { + patchCSharpOptionOverloads(optionsArg, options); + } catch (e) { + throw new Error(`Error processing csharp options in ${this.clazz?.name}.${this.name}: ` + e.message); + } + } } filterOutExperimental() { @@ -642,15 +660,16 @@ Documentation.Type = class { /** * @param {string} lang + * @param {LanguageOptions=} options */ - filterForLanguage(lang) { + filterForLanguage(lang, options = {}) { if (!this.properties) return; const properties = []; for (const prop of this.properties) { if (prop.langs.only && !prop.langs.only.includes(lang)) continue; - prop.filterForLanguage(lang); + prop.filterForLanguage(lang, options); properties.push(prop); } this.properties = properties; @@ -839,4 +858,73 @@ function generateSourceCodeComment(spec) { return md.render(comments, 120); } +/** + * @param {Documentation.Member} optionsArg + * @param {LanguageOptions=} options + */ +function patchCSharpOptionOverloads(optionsArg, options = {}) { + const props = optionsArg.type?.properties; + if (!props) + return; + const propsToDelete = new Set(); + const propsToAdd = []; + for (const prop of props) { + const union = prop.type?.union; + if (!union) + continue; + const isEnum = union[0].name.startsWith('"'); + const isNullable = union.length === 2 && union.some(type => type.name === 'null'); + if (isEnum || isNullable) + continue; + + const shortNotation = []; + propsToDelete.add(prop); + for (const type of union) { + const suffix = csharpOptionOverloadSuffix(prop.name, type.name); + if (options.csharpOptionOverloadsShortNotation) { + if (type.name === 'string') + shortNotation.push(prop.alias); + else + shortNotation.push(prop.alias + suffix); + continue; + } + + const newProp = prop.clone(); + newProp.name = prop.name + suffix; + newProp.alias = prop.alias + suffix; + newProp.type = type; + propsToAdd.push(newProp); + + if (type.name === 'string') { + const stringProp = prop.clone(); + stringProp.type = type; + propsToAdd.push(stringProp); + } + } + if (options.csharpOptionOverloadsShortNotation) { + const newProp = prop.clone(); + newProp.alias = newProp.name = shortNotation.join('|'); + propsToAdd.push(newProp); + } + } + for (const prop of propsToDelete) + props.splice(props.indexOf(prop), 1); + props.push(...propsToAdd); +} + +/** + * @param {string} option + * @param {string} type + */ +function csharpOptionOverloadSuffix(option, type) { + switch (type) { + case 'string': return 'String'; + case 'RegExp': return 'Regex'; + case 'function': return 'Func'; + case 'Buffer': return 'Byte'; + case 'Serializable': return 'Object'; + } + throw new Error(`CSharp option "${option}" has unsupported type overload "${type}"`); +} + module.exports = Documentation; diff --git a/utils/doclint/generateDotnetApi.js b/utils/doclint/generateDotnetApi.js index 8dc7c3460e4b3..cc39a7cd9ff84 100644 --- a/utils/doclint/generateDotnetApi.js +++ b/utils/doclint/generateDotnetApi.js @@ -287,7 +287,7 @@ function renderConstructors(name, type, out) { function renderMember(member, parent, options, out) { const name = toMemberName(member); if (member.kind === 'method') { - renderMethod(member, parent, name, { mode: 'options', trimRunAndPrefix: options.trimRunAndPrefix }, out); + renderMethod(member, parent, name, { trimRunAndPrefix: options.trimRunAndPrefix }, out); return; } @@ -348,12 +348,6 @@ function getPropertyOverloads(type, member, name, parent) { if (member.type.expression === '[string]|[float]') jsonName = `${member.name}String`; overloads.push({ type, name, jsonName }); - } else { - for (const overload of member.type.union) { - const t = translateType(overload, parent, t => generateNameDefault(member, name, t, parent)); - const suffix = toOverloadSuffix(t); - overloads.push({ type: t, name: name + suffix, jsonName: member.name + suffix }); - } } return overloads; } @@ -463,7 +457,6 @@ function generateEnumNameIfApplicable(type) { * @param {Documentation.Class | Documentation.Type} parent * @param {string} name * @param {{ - * mode: 'options'|'named'|'base', * nodocs?: boolean, * abstract?: boolean, * public?: boolean, @@ -556,16 +549,12 @@ function renderMethod(member, parent, name, options, out) { return; if (arg.name === 'options') { - if (options.mode === 'options' || options.mode === 'base') { - const optionsType = rewriteSuggestedOptionsName(member.clazz.name + name.replace('', '') + 'Options'); - if (!optionTypes.has(optionsType) || arg.type.properties.length > optionTypes.get(optionsType).properties.length) - optionTypes.set(optionsType, arg.type); - args.push(`${optionsType}? options = default`); - argTypeMap.set(`${optionsType}? options = default`, 'options'); - addParamsDoc('options', ['Call options']); - } else { - arg.type.properties.forEach(processArg); - } + const optionsType = rewriteSuggestedOptionsName(member.clazz.name + name.replace('', '') + 'Options'); + if (!optionTypes.has(optionsType) || arg.type.properties.length > optionTypes.get(optionsType).properties.length) + optionTypes.set(optionsType, arg.type); + args.push(`${optionsType}? options = default`); + argTypeMap.set(`${optionsType}? options = default`, 'options'); + addParamsDoc('options', ['Call options']); return; } @@ -632,37 +621,6 @@ function renderMethod(member, parent, name, options, out) { .sort((a, b) => b.alias === 'options' ? -1 : 0) // move options to the back to the arguments list .forEach(processArg); - let body = ';'; - if (options.mode === 'base') { - // Generate options -> named transition. - const tokens = []; - for (const arg of member.argsArray) { - if (arg.name === 'action' && options.trimRunAndPrefix) - continue; - if (arg.name !== 'options') { - tokens.push(toArgumentName(arg.name)); - continue; - } - for (const opt of arg.type.properties) { - // TODO: use translate type here? - if (opt.type.union && !opt.type.union[0].name.startsWith('"') && opt.type.union[0].name !== 'null' && opt.type.expression !== '[string]|[Buffer]') { - // Explode overloads. - for (const t of opt.type.union) { - const suffix = toOverloadSuffix(translateType(t, parent)); - tokens.push(`${opt.name}${suffix}: options.${toMemberName(opt)}${suffix}`); - } - } else { - tokens.push(`${opt.alias || opt.name}: options.${toMemberName(opt)}`); - } - } - } - body = ` -{ - options ??= new ${member.clazz.name}${name}Options(); - return ${toAsync(name, member.async)}(${tokens.join(', ')}); -}`; - } - if (!explodedArgs.length) { if (!options.nodocs) { out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth)); @@ -670,7 +628,7 @@ function renderMethod(member, parent, name, options, out) { } if (member.deprecated) out.push(`[System.Obsolete]`); - out.push(`${modifiers}${type} ${toAsync(name, member.async)}(${args.join(', ')})${body}`); + out.push(`${modifiers}${type} ${toAsync(name, member.async)}(${args.join(', ')});`); } else { let containsOptionalExplodedArgs = false; explodedArgs.forEach((explodedArg, argIndex) => { @@ -692,7 +650,7 @@ function renderMethod(member, parent, name, options, out) { overloadedArgs.push(arg); } } - out.push(`${modifiers}${type} ${toAsync(name, member.async)}(${overloadedArgs.join(', ')})${body}`); + out.push(`${modifiers}${type} ${toAsync(name, member.async)}(${overloadedArgs.join(', ')});`); if (argIndex < explodedArgs.length - 1) out.push(''); // output a special blank line }); @@ -712,7 +670,7 @@ function renderMethod(member, parent, name, options, out) { if (!options.nodocs) printArgDoc(argType, paramDocs.get(argType), out); }); - out.push(`${type} ${name}(${filteredArgs.join(', ')})${body}`); + out.push(`${type} ${name}(${filteredArgs.join(', ')});`); } } } @@ -874,14 +832,6 @@ function printArgDoc(name, value, out) { } } -/** - * @param {string} typeName - * @return {string} - */ -function toOverloadSuffix(typeName) { - return toTitleCase(typeName.replace(/[<].*[>]/, '').replace(/[^a-zA-Z]/g, '')); -} - /** * @param {string} name * @param {boolean} convert @@ -895,7 +845,7 @@ function toAsync(name, convert) { } /** - * @param {string} suggestedName + * @param {string} suggestedName * @returns {string} */ function rewriteSuggestedOptionsName(suggestedName) {