chore: use ReadonlyArray for input parameters (#28564)

This commit is contained in:
Max Schmitt 2023-12-12 16:22:48 -08:00 committed by GitHub
parent d2dc8eb1e3
commit 297cfdfc5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 48 deletions

View File

@ -3716,7 +3716,7 @@ export interface Page {
* labels. Option is considered matching if all specified properties match.
* @param options
*/
selectOption(selector: string, values: null|string|ElementHandle|Array<string>|{
selectOption(selector: string, values: null|string|ElementHandle|ReadonlyArray<string>|{
/**
* Matches by `option.value`. Optional.
*/
@ -3731,7 +3731,7 @@ export interface Page {
* Matches by the index. Optional.
*/
index?: number;
}|Array<ElementHandle>|Array<{
}|ReadonlyArray<ElementHandle>|ReadonlyArray<{
/**
* Matches by `option.value`. Optional.
*/
@ -3930,7 +3930,7 @@ export interface Page {
* @param files
* @param options
*/
setInputFiles(selector: string, files: string|Array<string>|{
setInputFiles(selector: string, files: string|ReadonlyArray<string>|{
/**
* File name
*/
@ -3945,7 +3945,7 @@ export interface Page {
* File content
*/
buffer: Buffer;
}|Array<{
}|ReadonlyArray<{
/**
* File name
*/
@ -6828,7 +6828,7 @@ export interface Frame {
* labels. Option is considered matching if all specified properties match.
* @param options
*/
selectOption(selector: string, values: null|string|ElementHandle|Array<string>|{
selectOption(selector: string, values: null|string|ElementHandle|ReadonlyArray<string>|{
/**
* Matches by `option.value`. Optional.
*/
@ -6843,7 +6843,7 @@ export interface Frame {
* Matches by the index. Optional.
*/
index?: number;
}|Array<ElementHandle>|Array<{
}|ReadonlyArray<ElementHandle>|ReadonlyArray<{
/**
* Matches by `option.value`. Optional.
*/
@ -7000,7 +7000,7 @@ export interface Frame {
* @param files
* @param options
*/
setInputFiles(selector: string, files: string|Array<string>|{
setInputFiles(selector: string, files: string|ReadonlyArray<string>|{
/**
* File name
*/
@ -7015,7 +7015,7 @@ export interface Frame {
* File content
*/
buffer: Buffer;
}|Array<{
}|ReadonlyArray<{
/**
* File name
*/
@ -8171,7 +8171,7 @@ export interface BrowserContext {
*
* For the cookie to apply to all subdomains as well, prefix domain with a dot, like this: ".example.com".
*/
addCookies(cookies: Array<{
addCookies(cookies: ReadonlyArray<{
name: string;
value: string;
@ -8262,7 +8262,7 @@ export interface BrowserContext {
* URLs are returned.
* @param urls Optional list of URLs.
*/
cookies(urls?: string|Array<string>): Promise<Array<Cookie>>;
cookies(urls?: string|ReadonlyArray<string>): Promise<Array<Cookie>>;
/**
* The method adds a function called `name` on the `window` object of every frame in every page in the context. When
@ -8327,7 +8327,7 @@ export interface BrowserContext {
* - `'payment-handler'`
* @param options
*/
grantPermissions(permissions: Array<string>, options?: {
grantPermissions(permissions: ReadonlyArray<string>, options?: {
/**
* The [origin] to grant permissions to, e.g. "https://example.com".
*/
@ -10082,7 +10082,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
* labels. Option is considered matching if all specified properties match.
* @param options
*/
selectOption(values: null|string|ElementHandle|Array<string>|{
selectOption(values: null|string|ElementHandle|ReadonlyArray<string>|{
/**
* Matches by `option.value`. Optional.
*/
@ -10097,7 +10097,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
* Matches by the index. Optional.
*/
index?: number;
}|Array<ElementHandle>|Array<{
}|ReadonlyArray<ElementHandle>|ReadonlyArray<{
/**
* Matches by `option.value`. Optional.
*/
@ -10224,7 +10224,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
* @param files
* @param options
*/
setInputFiles(files: string|Array<string>|{
setInputFiles(files: string|ReadonlyArray<string>|{
/**
* File name
*/
@ -10239,7 +10239,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
* File content
*/
buffer: Buffer;
}|Array<{
}|ReadonlyArray<{
/**
* File name
*/
@ -12243,7 +12243,7 @@ export interface Locator {
* labels. Option is considered matching if all specified properties match.
* @param options
*/
selectOption(values: null|string|ElementHandle|Array<string>|{
selectOption(values: null|string|ElementHandle|ReadonlyArray<string>|{
/**
* Matches by `option.value`. Optional.
*/
@ -12258,7 +12258,7 @@ export interface Locator {
* Matches by the index. Optional.
*/
index?: number;
}|Array<ElementHandle>|Array<{
}|ReadonlyArray<ElementHandle>|ReadonlyArray<{
/**
* Matches by `option.value`. Optional.
*/
@ -12422,7 +12422,7 @@ export interface Locator {
* @param files
* @param options
*/
setInputFiles(files: string|Array<string>|{
setInputFiles(files: string|ReadonlyArray<string>|{
/**
* File name
*/
@ -12437,7 +12437,7 @@ export interface Locator {
* File content
*/
buffer: Buffer;
}|Array<{
}|ReadonlyArray<{
/**
* File name
*/
@ -14931,7 +14931,7 @@ export interface AndroidInput {
x: number;
y: number;
}, segments: Array<{
}, segments: ReadonlyArray<{
x: number;
y: number;
@ -17332,7 +17332,7 @@ export interface FileChooser {
* @param files
* @param options
*/
setFiles(files: string|Array<string>|{
setFiles(files: string|ReadonlyArray<string>|{
/**
* File name
*/
@ -17347,7 +17347,7 @@ export interface FileChooser {
* File content
*/
buffer: Buffer;
}|Array<{
}|ReadonlyArray<{
/**
* File name
*/
@ -18010,7 +18010,7 @@ export interface Logger {
* @param args message arguments
* @param hints optional formatting hints
*/
log(name: string, severity: "verbose"|"info"|"warning"|"error", message: string|Error, args: Array<Object>, hints: {
log(name: string, severity: "verbose"|"info"|"warning"|"error", message: string|Error, args: ReadonlyArray<Object>, hints: {
/**
* Optional preferred logger color.
*/

View File

@ -2072,7 +2072,7 @@ export interface TestInfo {
* (i.e. `test-results/a-test-title`), otherwise it will throw.
* @param pathSegments Path segments to append at the end of the resulting path.
*/
outputPath(...pathSegments: Array<string>): string;
outputPath(...pathSegments: ReadonlyArray<string>): string;
/**
* Changes the timeout for the currently running test. Zero means no timeout. Learn more about
@ -2134,7 +2134,7 @@ export interface TestInfo {
* @param pathSegments The name of the snapshot or the path segments to define the snapshot file path. Snapshots with the same name in the
* same test file are expected to be the same.
*/
snapshotPath(...pathSegments: Array<string>): string;
snapshotPath(...pathSegments: ReadonlyArray<string>): string;
/**
* The list of annotations applicable to the current test. Includes annotations from the test, annotations from all
@ -5740,7 +5740,7 @@ interface LocatorAssertions {
* @param expected Expected substring or RegExp or a list of those.
* @param options
*/
toContainText(expected: string|RegExp|Array<string|RegExp>, options?: {
toContainText(expected: string|RegExp|ReadonlyArray<string|RegExp>, options?: {
/**
* Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular
* expression flag if specified.
@ -5831,7 +5831,7 @@ interface LocatorAssertions {
* @param expected Expected class or RegExp or a list of those.
* @param options
*/
toHaveClass(expected: string|RegExp|Array<string|RegExp>, options?: {
toHaveClass(expected: string|RegExp|ReadonlyArray<string|RegExp>, options?: {
/**
* Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestConfig.expect`.
*/
@ -5936,7 +5936,7 @@ interface LocatorAssertions {
* @param name Snapshot name.
* @param options
*/
toHaveScreenshot(name: string|Array<string>, options?: {
toHaveScreenshot(name: string|ReadonlyArray<string>, options?: {
/**
* When set to `"disabled"`, stops CSS animations, CSS transitions and Web Animations. Animations get different
* treatment depending on their duration:
@ -6153,7 +6153,7 @@ interface LocatorAssertions {
* @param expected Expected string or RegExp or a list of those.
* @param options
*/
toHaveText(expected: string|RegExp|Array<string|RegExp>, options?: {
toHaveText(expected: string|RegExp|ReadonlyArray<string|RegExp>, options?: {
/**
* Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular
* expression flag if specified.
@ -6217,7 +6217,7 @@ interface LocatorAssertions {
* @param values Expected options currently selected.
* @param options
*/
toHaveValues(values: Array<string|RegExp>, options?: {
toHaveValues(values: ReadonlyArray<string|RegExp>, options?: {
/**
* Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestConfig.expect`.
*/
@ -6266,7 +6266,7 @@ interface PageAssertions {
* @param name Snapshot name.
* @param options
*/
toHaveScreenshot(name: string|Array<string>, options?: {
toHaveScreenshot(name: string|ReadonlyArray<string>, options?: {
/**
* When set to `"disabled"`, stops CSS animations, CSS transitions and Web Animations. Animations get different
* treatment depending on their duration:
@ -6585,7 +6585,7 @@ interface SnapshotAssertions {
* @param name Snapshot name.
* @param options
*/
toMatchSnapshot(name: string|Array<string>, options?: {
toMatchSnapshot(name: string|ReadonlyArray<string>, options?: {
/**
* An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1`. Default is
* configurable with `TestConfig.expect`. Unset by default.

View File

@ -223,6 +223,20 @@ test('should compile generic matchers', async ({ runTSC }) => {
expect(result.exitCode).toBe(0);
});
test('should work when passing a ReadonlyArray', async ({ runTSC }) => {
const result = await runTSC({
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('example', async ({ page }) => {
const readonlyArray: ReadonlyArray<string> = ['1', '2', '3'];
expect(page.locator('.foo')).toHaveText(readonlyArray);
await page.locator('.foo').setInputFiles(readonlyArray);
});
`
});
expect(result.exitCode).toBe(0);
});
test('should work with expect message', async ({ runTSC }) => {
const result = await runTSC({
'a.spec.ts': `

View File

@ -246,7 +246,7 @@ class TypesGenerator {
const descriptions = [];
for (let [eventName, value] of classDesc.events) {
eventName = eventName.toLowerCase();
const type = this.stringifyComplexType(value && value.type, ' ', classDesc.name, eventName, 'payload');
const type = this.stringifyComplexType(value && value.type, 'out', ' ', classDesc.name, eventName, 'payload');
const argName = this.argNameForType(type);
const params = argName ? `${argName}: ${type}` : '';
descriptions.push({
@ -300,7 +300,7 @@ class TypesGenerator {
}
const jsdoc = this.memberJSDOC(member, indent);
const args = this.argsFromMember(member, indent, classDesc.name);
let type = this.stringifyComplexType(member.type, indent, classDesc.name, member.alias);
let type = this.stringifyComplexType(member.type, 'out', indent, classDesc.name, member.alias);
if (member.async)
type = `Promise<${type}>`;
// do this late, because we still want object definitions for overridden types
@ -373,12 +373,12 @@ class TypesGenerator {
}
/**
* @param {docs.Type} type
* @param {docs.Type|null} type
*/
stringifyComplexType(type, indent, ...namespace) {
stringifyComplexType(type, direction, indent, ...namespace) {
if (!type)
return 'void';
return this.stringifySimpleType(type, indent, ...namespace);
return this.stringifySimpleType(type, direction, indent, ...namespace);
}
/**
@ -393,7 +393,7 @@ class TypesGenerator {
parts.push(properties.map(member => {
const comment = this.memberJSDOC(member, indent + ' ');
const args = this.argsFromMember(member, indent + ' ', name);
const type = this.stringifyComplexType(member.type, indent + ' ', name, member.name);
const type = this.stringifyComplexType(member.type, 'out', indent + ' ', name, member.name);
return `${comment}${this.nameForProperty(member)}${args}: ${type};`;
}).join('\n\n'));
parts.push(indent + '}');
@ -401,21 +401,23 @@ class TypesGenerator {
}
/**
* @param {docs.Type=} type
* @param {docs.Type | null | undefined} type
* @param {'in' | 'out'} direction
* @returns{string}
*/
stringifySimpleType(type, indent = '', ...namespace) {
stringifySimpleType(type, direction, indent = '', ...namespace) {
if (!type)
return 'void';
if (type.name === 'Object' && type.templates) {
const keyType = this.stringifySimpleType(type.templates[0], indent, ...namespace);
const valueType = this.stringifySimpleType(type.templates[1], indent, ...namespace);
const keyType = this.stringifySimpleType(type.templates[0], direction, indent, ...namespace);
const valueType = this.stringifySimpleType(type.templates[1], direction, indent, ...namespace);
return `{ [key: ${keyType}]: ${valueType}; }`;
}
let out = type.name;
if (out === 'int' || out === 'float')
out = 'number';
if (out === 'Array' && direction === 'in')
out = 'ReadonlyArray';
if (type.name === 'Object' && type.properties && type.properties.length) {
const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join('');
const shouldExport = exported[name];
@ -431,10 +433,10 @@ class TypesGenerator {
if (type.args) {
const stringArgs = type.args.map(a => ({
type: this.stringifySimpleType(a, indent, ...namespace),
type: this.stringifySimpleType(a, direction, indent, ...namespace),
name: a.name.toLowerCase()
}));
out = `((${stringArgs.map(({ name, type }) => `${name}: ${type}`).join(', ')}) => ${this.stringifySimpleType(type.returnType, indent, ...namespace)})`;
out = `((${stringArgs.map(({ name, type }) => `${name}: ${type}`).join(', ')}) => ${this.stringifySimpleType(type.returnType, 'out', indent, ...namespace)})`;
} else if (type.name === 'function') {
out = 'Function';
}
@ -443,9 +445,9 @@ class TypesGenerator {
if (out === 'Any')
return 'any';
if (type.templates)
out += '<' + type.templates.map(t => this.stringifySimpleType(t, indent, ...namespace)).join(', ') + '>';
out += '<' + type.templates.map(t => this.stringifySimpleType(t, direction, indent, ...namespace)).join(', ') + '>';
if (type.union)
out = type.union.map(t => this.stringifySimpleType(t, indent, ...namespace)).join('|');
out = type.union.map(t => this.stringifySimpleType(t, direction, indent, ...namespace)).join('|');
return out.trim();
}
@ -455,7 +457,7 @@ class TypesGenerator {
argsFromMember(member, indent, ...namespace) {
if (member.kind === 'property')
return '';
return '(' + member.argsArray.map(arg => `${this.nameForProperty(arg)}: ${this.stringifyComplexType(arg.type, indent, ...namespace, member.alias, arg.alias)}`).join(', ') + ')';
return '(' + member.argsArray.map(arg => `${this.nameForProperty(arg)}: ${this.stringifyComplexType(arg.type, 'in', indent, ...namespace, member.alias, arg.alias)}`).join(', ') + ')';
}
/**