refactor to support multiple languages
This commit is contained in:
parent
6ffab68dfa
commit
7ef5d8d5d8
|
|
@ -14,9 +14,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
import type * as har from '@trace/har';
|
||||
|
||||
export function generatePlaywrightRequestCall(request: har.Request, body: string | undefined): string {
|
||||
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}`;
|
||||
|
|
@ -35,11 +41,11 @@ export function generatePlaywrightRequestCall(request: har.Request, body: string
|
|||
const params = [`'${urlParam}'`];
|
||||
const hasOptions = Object.keys(options).length > 0;
|
||||
if (hasOptions)
|
||||
params.push(prettyPrintObject(options));
|
||||
params.push(this.prettyPrintObject(options));
|
||||
return `await page.request.${method}(${params.join(', ')});`;
|
||||
}
|
||||
}
|
||||
|
||||
function prettyPrintObject(obj: any, indent = 2, level = 0): string {
|
||||
private prettyPrintObject(obj: any, indent = 2, level = 0): string {
|
||||
// Handle null and undefined
|
||||
if (obj === null)
|
||||
return 'null';
|
||||
|
|
@ -61,7 +67,7 @@ function prettyPrintObject(obj: any, indent = 2, level = 0): string {
|
|||
const nextSpaces = ' '.repeat((level + 1) * indent);
|
||||
|
||||
const items = obj.map(item =>
|
||||
`${nextSpaces}${prettyPrintObject(item, indent, level + 1)}`
|
||||
`${nextSpaces}${this.prettyPrintObject(item, indent, level + 1)}`
|
||||
).join(',\n');
|
||||
|
||||
return `[\n${items}\n${spaces}]`;
|
||||
|
|
@ -74,7 +80,7 @@ function prettyPrintObject(obj: any, indent = 2, level = 0): string {
|
|||
const nextSpaces = ' '.repeat((level + 1) * indent);
|
||||
|
||||
const entries = Object.entries(obj).map(([key, value]) => {
|
||||
const formattedValue = prettyPrintObject(value, indent, level + 1);
|
||||
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 :
|
||||
|
|
@ -83,4 +89,11 @@ function prettyPrintObject(obj: any, indent = 2, level = 0): string {
|
|||
}).join(',\n');
|
||||
|
||||
return `{\n${entries}\n${spaces}}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function getAPIRequestCodeGen(language: Language): APIRequestCodegen {
|
||||
if (language === 'javascript')
|
||||
return new JSCodeGen();
|
||||
throw new Error('Unsupported language: ' + language);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper';
|
|||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import { generateCurlCommand, generateFetchCall } from '../third_party/devtools';
|
||||
import { CopyToClipboardTextButton } from './copyToClipboard';
|
||||
import { generatePlaywrightRequestCall } from '@isomorphic/codegen';
|
||||
import { getAPIRequestCodeGen } from './codegen';
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
|
||||
export const NetworkResourceDetails: React.FunctionComponent<{
|
||||
resource: ResourceSnapshot;
|
||||
onClose: () => void;
|
||||
sdkLanguage?: Language;
|
||||
sdkLanguage: Language;
|
||||
}> = ({ resource, onClose, sdkLanguage }) => {
|
||||
const [selectedTab, setSelectedTab] = React.useState('request');
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ export const NetworkResourceDetails: React.FunctionComponent<{
|
|||
|
||||
const RequestTab: React.FunctionComponent<{
|
||||
resource: ResourceSnapshot;
|
||||
sdkLanguage?: Language;
|
||||
sdkLanguage: Language;
|
||||
}> = ({ resource, sdkLanguage }) => {
|
||||
const [requestBody, setRequestBody] = React.useState<{ text: string, mimeType?: string } | null>(null);
|
||||
|
||||
|
|
@ -100,7 +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)} />
|
||||
{sdkLanguage === 'javascript' && <CopyToClipboardTextButton description='Copy as Playwright' value={async () => generatePlaywrightRequestCall(resource.request, requestBody?.text)} />}
|
||||
<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>}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export const NetworkTab: React.FunctionComponent<{
|
|||
boundaries: Boundaries,
|
||||
networkModel: NetworkTabModel,
|
||||
onEntryHovered?: (entry: Entry | undefined) => void,
|
||||
sdkLanguage?: Language,
|
||||
sdkLanguage: Language,
|
||||
}> = ({ boundaries, networkModel, onEntryHovered, sdkLanguage }) => {
|
||||
const [sorting, setSorting] = React.useState<Sorting | undefined>(undefined);
|
||||
const [selectedEntry, setSelectedEntry] = React.useState<RenderedEntry | undefined>(undefined);
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ export const Workbench: React.FunctionComponent<{
|
|||
id: 'network',
|
||||
title: 'Network',
|
||||
count: networkModel.resources.length,
|
||||
render: () => <NetworkTab boundaries={boundaries} networkModel={networkModel} onEntryHovered={setHighlightedEntry} sdkLanguage={model?.sdkLanguage} />
|
||||
render: () => <NetworkTab boundaries={boundaries} networkModel={networkModel} onEntryHovered={setHighlightedEntry} sdkLanguage={model?.sdkLanguage ?? 'javascript'} />
|
||||
};
|
||||
const attachmentsTab: TabbedPaneTabModel = {
|
||||
id: 'attachments',
|
||||
|
|
|
|||
|
|
@ -15,10 +15,14 @@
|
|||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { generatePlaywrightRequestCall } from '../../../packages/trace-viewer/src/ui/codegen';
|
||||
import { getAPIRequestCodeGen } from '../../../packages/trace-viewer/src/ui/codegen';
|
||||
|
||||
test('generatePlaywrightRequestCall', () => {
|
||||
expect(generatePlaywrightRequestCall({
|
||||
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' }],
|
||||
|
|
@ -40,7 +44,7 @@ await page.request.get('http://example.com/foo', {
|
|||
}
|
||||
});`.trim());
|
||||
|
||||
expect(generatePlaywrightRequestCall({
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz',
|
||||
method: 'OPTIONS',
|
||||
headers: [],
|
||||
|
|
@ -57,10 +61,10 @@ await page.request.fetch('http://example.com/foo', {
|
|||
bar: 'baz'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with POST method and no body', () => {
|
||||
expect(generatePlaywrightRequestCall({
|
||||
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' }],
|
||||
|
|
@ -76,10 +80,10 @@ await page.request.post('http://example.com/foo', {
|
|||
'Content-Type': 'application/json'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with PUT method and JSON body', () => {
|
||||
expect(generatePlaywrightRequestCall({
|
||||
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' }],
|
||||
|
|
@ -96,10 +100,10 @@ await page.request.put('http://example.com/foo', {
|
|||
'Content-Type': 'application/json'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with PATCH method and form data', () => {
|
||||
expect(generatePlaywrightRequestCall({
|
||||
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' }],
|
||||
|
|
@ -116,10 +120,10 @@ await page.request.patch('http://example.com/foo', {
|
|||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with DELETE method and custom header', () => {
|
||||
expect(generatePlaywrightRequestCall({
|
||||
test('generatePlaywrightRequestCall with DELETE method and custom header', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'DELETE',
|
||||
headers: [{ name: 'Authorization', value: 'Bearer token' }],
|
||||
|
|
@ -135,10 +139,10 @@ await page.request.delete('http://example.com/foo', {
|
|||
Authorization: 'Bearer token'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with HEAD method', () => {
|
||||
expect(generatePlaywrightRequestCall({
|
||||
test('generatePlaywrightRequestCall with HEAD method', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'HEAD',
|
||||
headers: [],
|
||||
|
|
@ -150,10 +154,10 @@ test('generatePlaywrightRequestCall with HEAD method', () => {
|
|||
comment: '',
|
||||
}, undefined)).toEqual(`
|
||||
await page.request.head('http://example.com/foo');`.trim());
|
||||
});
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with complex query parameters', () => {
|
||||
expect(generatePlaywrightRequestCall({
|
||||
test('generatePlaywrightRequestCall with complex query parameters', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo?bar=baz&qux=quux',
|
||||
method: 'GET',
|
||||
headers: [],
|
||||
|
|
@ -170,10 +174,10 @@ await page.request.get('http://example.com/foo', {
|
|||
qux: 'quux'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
});
|
||||
|
||||
test('generatePlaywrightRequestCall with multiple headers', () => {
|
||||
expect(generatePlaywrightRequestCall({
|
||||
test('generatePlaywrightRequestCall with multiple headers', () => {
|
||||
expect(impl.generatePlaywrightRequestCall({
|
||||
url: 'http://example.com/foo',
|
||||
method: 'GET',
|
||||
headers: [
|
||||
|
|
@ -195,4 +199,6 @@ await page.request.get('http://example.com/foo', {
|
|||
Authorization: 'Bearer token'
|
||||
}
|
||||
});`.trim());
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue