feat(trace viewer): add "Copy as Playwright Request" button (#33298)
This commit is contained in:
Родитель
f1ddd379f3
Коммит
1d3605d1fc
|
@ -0,0 +1,302 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
import type * as har from '@trace/har';
|
||||
|
||||
interface APIRequestCodegen {
|
||||
generatePlaywrightRequestCall(request: har.Request, body: string | undefined): string;
|
||||
}
|
||||
|
||||
class JSCodeGen implements APIRequestCodegen {
|
||||
generatePlaywrightRequestCall(request: har.Request, body: string | undefined): string {
|
||||
let method = request.method.toLowerCase();
|
||||
const url = new URL(request.url);
|
||||
const urlParam = `${url.origin}${url.pathname}`;
|
||||
const options: any = {};
|
||||
if (!['delete', 'get', 'head', 'post', 'put', 'patch'].includes(method)) {
|
||||
options.method = method;
|
||||
method = 'fetch';
|
||||
}
|
||||
if (url.searchParams.size)
|
||||
options.params = Object.fromEntries(url.searchParams.entries());
|
||||
if (body)
|
||||
options.data = body;
|
||||
if (request.headers.length)
|
||||
options.headers = Object.fromEntries(request.headers.map(header => [header.name, header.value]));
|
||||
|
||||
const params = [`'${urlParam}'`];
|
||||
const hasOptions = Object.keys(options).length > 0;
|
||||
if (hasOptions)
|
||||
params.push(this.prettyPrintObject(options));
|
||||
return `await page.request.${method}(${params.join(', ')});`;
|
||||
}
|
||||
|
||||
private prettyPrintObject(obj: any, indent = 2, level = 0): string {
|
||||
// Handle null and undefined
|
||||
if (obj === null)
|
||||
return 'null';
|
||||
if (obj === undefined)
|
||||
return 'undefined';
|
||||
|
||||
// Handle primitive types
|
||||
if (typeof obj !== 'object') {
|
||||
if (typeof obj === 'string')
|
||||
return this.stringLiteral(obj);
|
||||
return String(obj);
|
||||
}
|
||||
|
||||
// Handle arrays
|
||||
if (Array.isArray(obj)) {
|
||||
if (obj.length === 0)
|
||||
return '[]';
|
||||
const spaces = ' '.repeat(level * indent);
|
||||
const nextSpaces = ' '.repeat((level + 1) * indent);
|
||||
|
||||
const items = obj.map(item =>
|
||||
`${nextSpaces}${this.prettyPrintObject(item, indent, level + 1)}`
|
||||
).join(',\n');
|
||||
|
||||
return `[\n${items}\n${spaces}]`;
|
||||
}
|
||||
|
||||
// Handle regular objects
|
||||
if (Object.keys(obj).length === 0)
|
||||
return '{}';
|
||||
const spaces = ' '.repeat(level * indent);
|
||||
const nextSpaces = ' '.repeat((level + 1) * indent);
|
||||
|
||||
const entries = Object.entries(obj).map(([key, value]) => {
|
||||
const formattedValue = this.prettyPrintObject(value, indent, level + 1);
|
||||
// Handle keys that need quotes
|
||||
const formattedKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ?
|
||||
key :
|
||||
this.stringLiteral(key);
|
||||
return `${nextSpaces}${formattedKey}: ${formattedValue}`;
|
||||
}).join(',\n');
|
||||
|
||||
return `{\n${entries}\n${spaces}}`;
|
||||
}
|
||||
|
||||
private stringLiteral(v: string): string {
|
||||
v = v.replace(/\\/g, '\\\\').replace(/'/g, '\\\'');
|
||||
if (v.includes('\n') || v.includes('\r') || v.includes('\t'))
|
||||
return '`' + v + '`';
|
||||
return `'${v}'`;
|
||||
}
|
||||
}
|
||||
|
||||
class PythonCodeGen implements APIRequestCodegen {
|
||||
generatePlaywrightRequestCall(request: har.Request, body: string | undefined): string {
|
||||
const url = new URL(request.url);
|
||||
const urlParam = `${url.origin}${url.pathname}`;
|
||||
const params: string[] = [`"${urlParam}"`];
|
||||
|
||||
|
||||
let method = request.method.toLowerCase();
|
||||
if (!['delete', 'get', 'head', 'post', 'put', 'patch'].includes(method)) {
|
||||
params.push(`method="${method}"`);
|
||||
method = 'fetch';
|
||||
}
|
||||
|
||||
if (url.searchParams.size)
|
||||
params.push(`params=${this.prettyPrintObject(Object.fromEntries(url.searchParams.entries()))}`);
|
||||
if (body)
|
||||
params.push(`data=${this.prettyPrintObject(body)}`);
|
||||
if (request.headers.length)
|
||||
params.push(`headers=${this.prettyPrintObject(Object.fromEntries(request.headers.map(header => [header.name, header.value])))}`);
|
||||
|
||||
const paramsString = params.length === 1 ? params[0] : `\n${params.map(p => this.indent(p, 2)).join(',\n')}\n`;
|
||||
return `await page.request.${method}(${paramsString})`;
|
||||
}
|
||||
|
||||
private indent(v: string, level: number): string {
|
||||
return v.split('\n').map(s => ' '.repeat(level) + s).join('\n');
|
||||
}
|
||||
|
||||
private prettyPrintObject(obj: any, indent = 2, level = 0): string {
|
||||
// Handle null and undefined
|
||||
if (obj === null)
|
||||
return 'None';
|
||||
if (obj === undefined)
|
||||
return 'None';
|
||||
|
||||
// Handle primitive types
|
||||
if (typeof obj !== 'object') {
|
||||
if (typeof obj === 'string')
|
||||
return this.stringLiteral(obj);
|
||||
if (typeof obj === 'boolean')
|
||||
return obj ? 'True' : 'False';
|
||||
return String(obj);
|
||||
}
|
||||
|
||||
// Handle arrays
|
||||
if (Array.isArray(obj)) {
|
||||
if (obj.length === 0)
|
||||
return '[]';
|
||||
const spaces = ' '.repeat(level * indent);
|
||||
const nextSpaces = ' '.repeat((level + 1) * indent);
|
||||
|
||||
const items = obj.map(item =>
|
||||
`${nextSpaces}${this.prettyPrintObject(item, indent, level + 1)}`
|
||||
).join(',\n');
|
||||
|
||||
return `[\n${items}\n${spaces}]`;
|
||||
}
|
||||
|
||||
// Handle regular objects
|
||||
if (Object.keys(obj).length === 0)
|
||||
return '{}';
|
||||
const spaces = ' '.repeat(level * indent);
|
||||
const nextSpaces = ' '.repeat((level + 1) * indent);
|
||||
|
||||
const entries = Object.entries(obj).map(([key, value]) => {
|
||||
const formattedValue = this.prettyPrintObject(value, indent, level + 1);
|
||||
return `${nextSpaces}${this.stringLiteral(key)}: ${formattedValue}`;
|
||||
}).join(',\n');
|
||||
|
||||
return `{\n${entries}\n${spaces}}`;
|
||||
}
|
||||
|
||||
private stringLiteral(v: string): string {
|
||||
return JSON.stringify(v);
|
||||
}
|
||||
}
|
||||
|
||||
class CSharpCodeGen implements APIRequestCodegen {
|
||||
generatePlaywrightRequestCall(request: har.Request, body: string | undefined): string {
|
||||
const url = new URL(request.url);
|
||||
const urlParam = `${url.origin}${url.pathname}`;
|
||||
const options: any = {};
|
||||
|
||||
const initLines: string[] = [];
|
||||
|
||||
let method = request.method.toLowerCase();
|
||||
if (!['delete', 'get', 'head', 'post', 'put', 'patch'].includes(method)) {
|
||||
options.Method = method;
|
||||
method = 'fetch';
|
||||
}
|
||||
|
||||
if (url.searchParams.size)
|
||||
options.Params = Object.fromEntries(url.searchParams.entries());
|
||||
if (body)
|
||||
options.Data = body;
|
||||
if (request.headers.length)
|
||||
options.Headers = Object.fromEntries(request.headers.map(header => [header.name, header.value]));
|
||||
|
||||
const params = [`"${urlParam}"`];
|
||||
const hasOptions = Object.keys(options).length > 0;
|
||||
if (hasOptions)
|
||||
params.push(this.prettyPrintObject(options));
|
||||
|
||||
return `${initLines.join('\n')}${initLines.length ? '\n' : ''}await request.${this.toFunctionName(method)}(${params.join(', ')});`;
|
||||
}
|
||||
|
||||
private toFunctionName(method: string): string {
|
||||
return method[0].toUpperCase() + method.slice(1) + 'Async';
|
||||
}
|
||||
|
||||
private prettyPrintObject(obj: any, indent = 2, level = 0): string {
|
||||
// Handle null and undefined
|
||||
if (obj === null)
|
||||
return 'null';
|
||||
if (obj === undefined)
|
||||
return 'null';
|
||||
|
||||
// Handle primitive types
|
||||
if (typeof obj !== 'object') {
|
||||
if (typeof obj === 'string')
|
||||
return this.stringLiteral(obj);
|
||||
if (typeof obj === 'boolean')
|
||||
return obj ? 'true' : 'false';
|
||||
return String(obj);
|
||||
}
|
||||
|
||||
// Handle arrays
|
||||
if (Array.isArray(obj)) {
|
||||
if (obj.length === 0)
|
||||
return 'new object[] {}';
|
||||
const spaces = ' '.repeat(level * indent);
|
||||
const nextSpaces = ' '.repeat((level + 1) * indent);
|
||||
|
||||
const items = obj.map(item =>
|
||||
`${nextSpaces}${this.prettyPrintObject(item, indent, level + 1)}`
|
||||
).join(',\n');
|
||||
|
||||
return `new object[] {\n${items}\n${spaces}}`;
|
||||
}
|
||||
|
||||
// Handle regular objects
|
||||
if (Object.keys(obj).length === 0)
|
||||
return 'new {}';
|
||||
const spaces = ' '.repeat(level * indent);
|
||||
const nextSpaces = ' '.repeat((level + 1) * indent);
|
||||
|
||||
const entries = Object.entries(obj).map(([key, value]) => {
|
||||
const formattedValue = this.prettyPrintObject(value, indent, level + 1);
|
||||
const formattedKey = level === 0 ? key : `[${this.stringLiteral(key)}]`;
|
||||
return `${nextSpaces}${formattedKey} = ${formattedValue}`;
|
||||
}).join(',\n');
|
||||
|
||||
return `new() {\n${entries}\n${spaces}}`;
|
||||
}
|
||||
|
||||
private stringLiteral(v: string): string {
|
||||
return JSON.stringify(v);
|
||||
}
|
||||
}
|
||||
|
||||
class JavaCodeGen implements APIRequestCodegen {
|
||||
generatePlaywrightRequestCall(request: har.Request, body: string | undefined): string {
|
||||
const url = new URL(request.url);
|
||||
const params = [`"${url.origin}${url.pathname}"`];
|
||||
|
||||
const options: string[] = [];
|
||||
|
||||
let method = request.method.toLowerCase();
|
||||
if (!['delete', 'get', 'head', 'post', 'put', 'patch'].includes(method)) {
|
||||
options.push(`setMethod("${method}")`);
|
||||
method = 'fetch';
|
||||
}
|
||||
|
||||
for (const [key, value] of url.searchParams)
|
||||
options.push(`setQueryParam(${this.stringLiteral(key)}, ${this.stringLiteral(value)})`);
|
||||
if (body)
|
||||
options.push(`setData(${this.stringLiteral(body)})`);
|
||||
for (const header of request.headers)
|
||||
options.push(`setHeader(${this.stringLiteral(header.name)}, ${this.stringLiteral(header.value)})`);
|
||||
|
||||
if (options.length > 0)
|
||||
params.push(`RequestOptions.create()\n .${options.join('\n .')}\n`);
|
||||
return `request.${method}(${params.join(', ')});`;
|
||||
}
|
||||
|
||||
private stringLiteral(v: string): string {
|
||||
return JSON.stringify(v);
|
||||
}
|
||||
}
|
||||
|
||||
export function getAPIRequestCodeGen(language: Language): APIRequestCodegen {
|
||||
if (language === 'javascript')
|
||||
return new JSCodeGen();
|
||||
if (language === 'python')
|
||||
return new PythonCodeGen();
|
||||
if (language === 'csharp')
|
||||
return new CSharpCodeGen();
|
||||
if (language === 'java')
|
||||
return new JavaCodeGen();
|
||||
throw new Error('Unsupported language: ' + language);
|
||||
}
|
|
@ -22,11 +22,14 @@ import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper';
|
|||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import { generateCurlCommand, generateFetchCall } from '../third_party/devtools';
|
||||
import { CopyToClipboardTextButton } from './copyToClipboard';
|
||||
import { getAPIRequestCodeGen } from './codegen';
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
|
||||
export const NetworkResourceDetails: React.FunctionComponent<{
|
||||
resource: ResourceSnapshot;
|
||||
onClose: () => void;
|
||||
}> = ({ resource, onClose }) => {
|
||||
sdkLanguage: Language;
|
||||
}> = ({ resource, onClose, sdkLanguage }) => {
|
||||
const [selectedTab, setSelectedTab] = React.useState('request');
|
||||
|
||||
return <TabbedPane
|
||||
|
@ -36,7 +39,7 @@ export const NetworkResourceDetails: React.FunctionComponent<{
|
|||
{
|
||||
id: 'request',
|
||||
title: 'Request',
|
||||
render: () => <RequestTab resource={resource}/>,
|
||||
render: () => <RequestTab resource={resource} sdkLanguage={sdkLanguage} />,
|
||||
},
|
||||
{
|
||||
id: 'response',
|
||||
|
@ -55,7 +58,8 @@ export const NetworkResourceDetails: React.FunctionComponent<{
|
|||
|
||||
const RequestTab: React.FunctionComponent<{
|
||||
resource: ResourceSnapshot;
|
||||
}> = ({ resource }) => {
|
||||
sdkLanguage: Language;
|
||||
}> = ({ resource, sdkLanguage }) => {
|
||||
const [requestBody, setRequestBody] = React.useState<{ text: string, mimeType?: string } | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -96,6 +100,7 @@ const RequestTab: React.FunctionComponent<{
|
|||
<div className='network-request-details-copy'>
|
||||
<CopyToClipboardTextButton description='Copy as cURL' value={() => generateCurlCommand(resource)} />
|
||||
<CopyToClipboardTextButton description='Copy as Fetch' value={() => generateFetchCall(resource)} />
|
||||
<CopyToClipboardTextButton description='Copy as Playwright' value={async () => getAPIRequestCodeGen(sdkLanguage).generatePlaywrightRequestCall(resource.request, requestBody?.text)} />
|
||||
</div>
|
||||
|
||||
{requestBody && <div className='network-request-details-header'>Request Body</div>}
|
||||
|
|
|
@ -26,6 +26,7 @@ import { GridView, type RenderedGridCell } from '@web/components/gridView';
|
|||
import { SplitView } from '@web/components/splitView';
|
||||
import type { ContextEntry } from '../types/entries';
|
||||
import { NetworkFilters, defaultFilterState, type FilterState, type ResourceType } from './networkFilters';
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
|
||||
type NetworkTabModel = {
|
||||
resources: Entry[],
|
||||
|
@ -66,7 +67,8 @@ export const NetworkTab: React.FunctionComponent<{
|
|||
boundaries: Boundaries,
|
||||
networkModel: NetworkTabModel,
|
||||
onEntryHovered?: (entry: Entry | undefined) => void,
|
||||
}> = ({ boundaries, networkModel, onEntryHovered }) => {
|
||||
sdkLanguage: Language,
|
||||
}> = ({ boundaries, networkModel, onEntryHovered, sdkLanguage }) => {
|
||||
const [sorting, setSorting] = React.useState<Sorting | undefined>(undefined);
|
||||
const [selectedEntry, setSelectedEntry] = React.useState<RenderedEntry | undefined>(undefined);
|
||||
const [filterState, setFilterState] = React.useState(defaultFilterState);
|
||||
|
@ -115,7 +117,7 @@ export const NetworkTab: React.FunctionComponent<{
|
|||
sidebarIsFirst={true}
|
||||
orientation='horizontal'
|
||||
settingName='networkResourceDetails'
|
||||
main={<NetworkResourceDetails resource={selectedEntry.resource} onClose={() => setSelectedEntry(undefined)} />}
|
||||
main={<NetworkResourceDetails resource={selectedEntry.resource} onClose={() => setSelectedEntry(undefined)} sdkLanguage={sdkLanguage} />}
|
||||
sidebar={grid}
|
||||
/>}
|
||||
</>;
|
||||
|
|
|
@ -238,7 +238,7 @@ const PropertiesView: React.FunctionComponent<{
|
|||
id: 'network',
|
||||
title: 'Network',
|
||||
count: networkModel.resources.length,
|
||||
render: () => <NetworkTab boundaries={boundaries} networkModel={networkModel} />
|
||||
render: () => <NetworkTab boundaries={boundaries} networkModel={networkModel} sdkLanguage={sdkLanguage} />
|
||||
};
|
||||
|
||||
const tabs: TabbedPaneTabModel[] = [
|
||||
|
|
|
@ -231,7 +231,7 @@ export const Workbench: React.FunctionComponent<{
|
|||
id: 'network',
|
||||
title: 'Network',
|
||||
count: networkModel.resources.length,
|
||||
render: () => <NetworkTab boundaries={boundaries} networkModel={networkModel} onEntryHovered={setHighlightedEntry}/>
|
||||
render: () => <NetworkTab boundaries={boundaries} networkModel={networkModel} onEntryHovered={setHighlightedEntry} sdkLanguage={model?.sdkLanguage ?? 'javascript'} />
|
||||
};
|
||||
const attachmentsTab: TabbedPaneTabModel = {
|
||||
id: 'attachments',
|
||||
|
|
|
@ -0,0 +1,675 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { getAPIRequestCodeGen } from '../../../packages/trace-viewer/src/ui/codegen';
|
||||
|
||||
test.describe('javascript', () => {
|
||||
const impl = getAPIRequestCodeGen('javascript');
|
||||
|
||||
test('generatePlaywrightRequestCall', () => {
|
||||
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz',
|
||||
method: 'GET',
|
||||
headers: [{ name: 'User-Agent', value: 'Mozilla/5.0' }, { name: 'Date', value: '2021-01-01' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, 'foo')).toEqual(`
|
||||
await page.request.get('http://example.com/foo', {
|
||||
params: {
|
||||
bar: 'baz'
|
||||
},
|
||||
data: 'foo',
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0',
|
||||
Date: '2021-01-01'
|
||||
}
|
||||
});`.trim());
|
||||
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz',
|
||||
method: 'OPTIONS',
|
||||
headers: [],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.fetch('http://example.com/foo', {
|
||||
method: 'options',
|
||||
params: {
|
||||
bar: 'baz'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with POST method and no body', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'POST',
|
||||
headers: [{ name: 'Content-Type', value: 'application/json' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.post('http://example.com/foo', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with PUT method and JSON body', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'PUT',
|
||||
headers: [{ name: 'Content-Type', value: 'application/json' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, '{"key":"value"}')).toEqual(`
|
||||
await page.request.put('http://example.com/foo', {
|
||||
data: '{"key":"value"}',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with PATCH method and form data', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'PATCH',
|
||||
headers: [{ name: 'Content-Type', value: 'application/x-www-form-urlencoded' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, 'key=value')).toEqual(`
|
||||
await page.request.patch('http://example.com/foo', {
|
||||
data: 'key=value',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with DELETE method and custom header', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'DELETE',
|
||||
headers: [{ name: 'Authorization', value: 'Bearer token' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.delete('http://example.com/foo', {
|
||||
headers: {
|
||||
Authorization: 'Bearer token'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with HEAD method', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'HEAD',
|
||||
headers: [],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.head('http://example.com/foo');`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with complex query parameters', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz&qux=quux',
|
||||
method: 'GET',
|
||||
headers: [],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.get('http://example.com/foo', {
|
||||
params: {
|
||||
bar: 'baz',
|
||||
qux: 'quux'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with multiple headers', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'GET',
|
||||
headers: [
|
||||
{ name: 'User-Agent', value: 'Mozilla/5.0' },
|
||||
{ name: 'Accept', value: 'application/json' },
|
||||
{ name: 'Authorization', value: 'Bearer token' }
|
||||
],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.get('http://example.com/foo', {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0',
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer token'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('escape sequences', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'GET',
|
||||
headers: [
|
||||
{ name: 'F\\o', value: 'B\\r' },
|
||||
],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.get('http://example.com/foo', {
|
||||
headers: {
|
||||
'F\\\\o': 'B\\\\r'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test.describe('python', () => {
|
||||
const impl = getAPIRequestCodeGen('python');
|
||||
|
||||
test('generatePlaywrightRequestCall', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz',
|
||||
method: 'GET',
|
||||
headers: [{ name: 'User-Agent', value: 'Mozilla/5.0' }, { name: 'Date', value: '2021-01-01' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, 'foo')).toEqual(`
|
||||
await page.request.get(
|
||||
"http://example.com/foo",
|
||||
params={
|
||||
"bar": "baz"
|
||||
},
|
||||
data="foo",
|
||||
headers={
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
"Date": "2021-01-01"
|
||||
}
|
||||
)`.trim());
|
||||
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz',
|
||||
method: 'OPTIONS',
|
||||
headers: [],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.fetch(
|
||||
"http://example.com/foo",
|
||||
method="options",
|
||||
params={
|
||||
"bar": "baz"
|
||||
}
|
||||
)`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with POST method and no body', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'POST',
|
||||
headers: [{ name: 'Content-Type', value: 'application/json' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.post(
|
||||
"http://example.com/foo",
|
||||
headers={
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
)`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with PUT method and JSON body', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'PUT',
|
||||
headers: [{ name: 'Content-Type', value: 'application/json' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, '{"key":"value"}')).toEqual(`
|
||||
await page.request.put(
|
||||
"http://example.com/foo",
|
||||
data="{\\"key\\":\\"value\\"}",
|
||||
headers={
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
)`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with PATCH method and form data', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'PATCH',
|
||||
headers: [{ name: 'Content-Type', value: 'application/x-www-form-urlencoded' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, 'key=value')).toEqual(`
|
||||
await page.request.patch(
|
||||
"http://example.com/foo",
|
||||
data="key=value",
|
||||
headers={
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
}
|
||||
)`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with DELETE method and custom header', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'DELETE',
|
||||
headers: [{ name: 'Authorization', value: 'Bearer token' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.delete(
|
||||
"http://example.com/foo",
|
||||
headers={
|
||||
"Authorization": "Bearer token"
|
||||
}
|
||||
)`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with HEAD method', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'HEAD',
|
||||
headers: [],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.head("http://example.com/foo")`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with complex query parameters', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz&qux=quux',
|
||||
method: 'GET',
|
||||
headers: [],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.get(
|
||||
"http://example.com/foo",
|
||||
params={
|
||||
"bar": "baz",
|
||||
"qux": "quux"
|
||||
}
|
||||
)`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with multiple headers', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'GET',
|
||||
headers: [
|
||||
{ name: 'User-Agent', value: 'Mozilla/5.0' },
|
||||
{ name: 'Accept', value: 'application/json' },
|
||||
{ name: 'Authorization', value: 'Bearer token' }
|
||||
],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.get(
|
||||
"http://example.com/foo",
|
||||
headers={
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
"Accept": "application/json",
|
||||
"Authorization": "Bearer token"
|
||||
}
|
||||
)`.trim());
|
||||
});
|
||||
|
||||
test('escape sequences', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'GET',
|
||||
headers: [
|
||||
{ name: 'F\\o', value: 'B\\r' },
|
||||
],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.get(
|
||||
"http://example.com/foo",
|
||||
headers={
|
||||
"F\\\\o": "B\\\\r"
|
||||
}
|
||||
)`.trim());
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test.describe('csharp', () => {
|
||||
const impl = getAPIRequestCodeGen('csharp');
|
||||
|
||||
test('generatePlaywrightRequestCall', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz',
|
||||
method: 'GET',
|
||||
headers: [{ name: 'User-Agent', value: 'Mozilla/5.0' }, { name: 'Date', value: '2021-01-01' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, 'foo')).toEqual(`
|
||||
await request.GetAsync("http://example.com/foo", new() {
|
||||
Params = new() {
|
||||
["bar"] = "baz"
|
||||
},
|
||||
Data = "foo",
|
||||
Headers = new() {
|
||||
["User-Agent"] = "Mozilla/5.0",
|
||||
["Date"] = "2021-01-01"
|
||||
}
|
||||
});`.trim());
|
||||
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz',
|
||||
method: 'OPTIONS',
|
||||
headers: [],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await request.FetchAsync("http://example.com/foo", new() {
|
||||
Method = "options",
|
||||
Params = new() {
|
||||
["bar"] = "baz"
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with POST method and no body', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'POST',
|
||||
headers: [{ name: 'Content-Type', value: 'application/json' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await request.PostAsync("http://example.com/foo", new() {
|
||||
Headers = new() {
|
||||
["Content-Type"] = "application/json"
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with PUT method and JSON body', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'PUT',
|
||||
headers: [{ name: 'Content-Type', value: 'application/json' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, '{"key":"value"}')).toEqual(`
|
||||
await request.PutAsync("http://example.com/foo", new() {
|
||||
Data = "{\\"key\\":\\"value\\"}",
|
||||
Headers = new() {
|
||||
["Content-Type"] = "application/json"
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
test('escape sequences', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'GET',
|
||||
headers: [
|
||||
{ name: 'F\\o', value: 'B\\r' },
|
||||
],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await request.GetAsync("http://example.com/foo", new() {
|
||||
Headers = new() {
|
||||
["F\\\\o"] = "B\\\\r"
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('java', () => {
|
||||
const impl = getAPIRequestCodeGen('java');
|
||||
|
||||
test('generatePlaywrightRequestCall', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz',
|
||||
method: 'GET',
|
||||
headers: [{ name: 'User-Agent', value: 'Mozilla/5.0' }, { name: 'Date', value: '2021-01-01' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, 'foo')).toEqual(`
|
||||
request.get("http://example.com/foo", RequestOptions.create()
|
||||
.setQueryParam("bar", "baz")
|
||||
.setData("foo")
|
||||
.setHeader("User-Agent", "Mozilla/5.0")
|
||||
.setHeader("Date", "2021-01-01")
|
||||
);`.trim());
|
||||
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz',
|
||||
method: 'OPTIONS',
|
||||
headers: [],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
request.fetch("http://example.com/foo", RequestOptions.create()
|
||||
.setMethod("options")
|
||||
.setQueryParam("bar", "baz")
|
||||
);`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with POST method and no body', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'POST',
|
||||
headers: [{ name: 'Content-Type', value: 'application/json' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
request.post("http://example.com/foo", RequestOptions.create()
|
||||
.setHeader("Content-Type", "application/json")
|
||||
);`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with PUT method and JSON body', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'PUT',
|
||||
headers: [{ name: 'Content-Type', value: 'application/json' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, '{"key":"value"}')).toEqual(`
|
||||
request.put("http://example.com/foo", RequestOptions.create()
|
||||
.setData("{\\"key\\":\\"value\\"}")
|
||||
.setHeader("Content-Type", "application/json")
|
||||
);`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with PATCH method and form data', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'PATCH',
|
||||
headers: [{ name: 'Content-Type', value: 'application/x-www-form-urlencoded' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, 'key=value')).toEqual(`
|
||||
request.patch("http://example.com/foo", RequestOptions.create()
|
||||
.setData("key=value")
|
||||
.setHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
);`.trim());
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with DELETE method and custom header', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'DELETE',
|
||||
headers: [{ name: 'Authorization', value: 'Bearer token' }],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
request.delete("http://example.com/foo", RequestOptions.create()
|
||||
.setHeader("Authorization", "Bearer token")
|
||||
);`.trim());
|
||||
});
|
||||
|
||||
test('escape sequences', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'GET',
|
||||
headers: [
|
||||
{ name: 'F\\o', value: 'B\\r' },
|
||||
],
|
||||
httpVersion: '1.1',
|
||||
cookies: [],
|
||||
queryString: [],
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
request.get(\"http://example.com/foo\", RequestOptions.create()
|
||||
.setHeader(\"F\\\\o\", \"B\\\\r\")
|
||||
);`.trim());
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче