feat(codegen): add NUnit/MSTest (#16803)
This commit is contained in:
parent
2c1723b6f5
commit
74ab343e2b
|
|
@ -69,7 +69,7 @@ Examples:
|
||||||
commandWithOpenOptions('codegen [url]', 'open page and generate code for user actions',
|
commandWithOpenOptions('codegen [url]', 'open page and generate code for user actions',
|
||||||
[
|
[
|
||||||
['-o, --output <file name>', 'saves the generated script to a file'],
|
['-o, --output <file name>', 'saves the generated script to a file'],
|
||||||
['--target <language>', `language to generate, one of javascript, test, python, python-async, pytest, csharp, java`, language()],
|
['--target <language>', `language to generate, one of javascript, test, python, python-async, pytest, csharp, csharp-mstest, csharp-nunit, java`, language()],
|
||||||
['--save-trace <filename>', 'record a trace for the session and save it to a file'],
|
['--save-trace <filename>', 'record a trace for the session and save it to a file'],
|
||||||
]).action(function(url, options) {
|
]).action(function(url, options) {
|
||||||
codegen(options, url, options.target, options.output).catch(logErrorAndExit);
|
codegen(options, url, options.target, options.output).catch(logErrorAndExit);
|
||||||
|
|
|
||||||
|
|
@ -362,12 +362,14 @@ class ContextRecorder extends EventEmitter {
|
||||||
setOutput(language: string, outputFile: string | undefined) {
|
setOutput(language: string, outputFile: string | undefined) {
|
||||||
const languages = new Set([
|
const languages = new Set([
|
||||||
new JavaLanguageGenerator(),
|
new JavaLanguageGenerator(),
|
||||||
new JavaScriptLanguageGenerator(false),
|
new JavaScriptLanguageGenerator(/* isPlaywrightTest */false),
|
||||||
new JavaScriptLanguageGenerator(true),
|
new JavaScriptLanguageGenerator(/* isPlaywrightTest */true),
|
||||||
new PythonLanguageGenerator(false, true),
|
new PythonLanguageGenerator(/* isAsync */false, /* isPytest */true),
|
||||||
new PythonLanguageGenerator(false, false),
|
new PythonLanguageGenerator(/* isAsync */false, /* isPytest */false),
|
||||||
new PythonLanguageGenerator(true, false),
|
new PythonLanguageGenerator(/* isAsync */true, /* isPytest */false),
|
||||||
new CSharpLanguageGenerator(),
|
new CSharpLanguageGenerator('mstest'),
|
||||||
|
new CSharpLanguageGenerator('nunit'),
|
||||||
|
new CSharpLanguageGenerator('library'),
|
||||||
]);
|
]);
|
||||||
const primaryLanguage = [...languages].find(l => l.id === language);
|
const primaryLanguage = [...languages].find(l => l.id === language);
|
||||||
if (!primaryLanguage)
|
if (!primaryLanguage)
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,46 @@ import { toModifiers } from './utils';
|
||||||
import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
|
import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
|
||||||
import deviceDescriptors from '../deviceDescriptors';
|
import deviceDescriptors from '../deviceDescriptors';
|
||||||
|
|
||||||
|
type CSharpLanguageMode = 'library' | 'mstest' | 'nunit';
|
||||||
|
|
||||||
export class CSharpLanguageGenerator implements LanguageGenerator {
|
export class CSharpLanguageGenerator implements LanguageGenerator {
|
||||||
id = 'csharp';
|
id: string;
|
||||||
groupName = '.NET';
|
groupName = '.NET C#';
|
||||||
name = 'Library C#';
|
name: string;
|
||||||
highlighter = 'csharp';
|
highlighter = 'csharp';
|
||||||
|
_mode: CSharpLanguageMode;
|
||||||
|
|
||||||
|
constructor(mode: CSharpLanguageMode) {
|
||||||
|
if (mode === 'library') {
|
||||||
|
this.name = 'Library';
|
||||||
|
this.id = 'csharp';
|
||||||
|
} else if (mode === 'mstest') {
|
||||||
|
this.name = 'MSTest';
|
||||||
|
this.id = 'csharp-mstest';
|
||||||
|
} else if (mode === 'nunit') {
|
||||||
|
this.name = 'NUnit';
|
||||||
|
this.id = 'csharp-nunit';
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unknown C# language mode: ${mode}`);
|
||||||
|
}
|
||||||
|
this._mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
generateAction(actionInContext: ActionInContext): string {
|
generateAction(actionInContext: ActionInContext): string {
|
||||||
|
const action = this._generateActionInner(actionInContext);
|
||||||
|
if (action)
|
||||||
|
return action + '\n';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateActionInner(actionInContext: ActionInContext): string {
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
const pageAlias = actionInContext.frame.pageAlias;
|
if (this._mode !== 'library' && (action.name === 'openPage' || action.name === 'closePage'))
|
||||||
|
return '';
|
||||||
|
let pageAlias = actionInContext.frame.pageAlias;
|
||||||
|
if (this._mode !== 'library')
|
||||||
|
pageAlias = pageAlias.replace('page', 'Page');
|
||||||
const formatter = new CSharpFormatter(8);
|
const formatter = new CSharpFormatter(8);
|
||||||
formatter.newLine();
|
|
||||||
formatter.add('// ' + actionTitle(action));
|
formatter.add('// ' + actionTitle(action));
|
||||||
|
|
||||||
if (action.name === 'openPage') {
|
if (action.name === 'openPage') {
|
||||||
|
|
@ -137,6 +166,12 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
generateHeader(options: LanguageGeneratorOptions): string {
|
generateHeader(options: LanguageGeneratorOptions): string {
|
||||||
|
if (this._mode === 'library')
|
||||||
|
return this.generateStandaloneHeader(options);
|
||||||
|
return this.generateTestRunnerHeader(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateStandaloneHeader(options: LanguageGeneratorOptions): string {
|
||||||
const formatter = new CSharpFormatter(0);
|
const formatter = new CSharpFormatter(0);
|
||||||
formatter.add(`
|
formatter.add(`
|
||||||
using Microsoft.Playwright;
|
using Microsoft.Playwright;
|
||||||
|
|
@ -150,6 +185,30 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
||||||
using var playwright = await Playwright.CreateAsync();
|
using var playwright = await Playwright.CreateAsync();
|
||||||
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject(options.launchOptions, ' ', 'BrowserTypeLaunchOptions')});
|
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject(options.launchOptions, ' ', 'BrowserTypeLaunchOptions')});
|
||||||
var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
||||||
|
formatter.newLine();
|
||||||
|
return formatter.format();
|
||||||
|
}
|
||||||
|
|
||||||
|
generateTestRunnerHeader(options: LanguageGeneratorOptions): string {
|
||||||
|
const formatter = new CSharpFormatter(0);
|
||||||
|
formatter.add(`
|
||||||
|
using Microsoft.Playwright.${this._mode === 'nunit' ? 'NUnit' : 'MSTest'};
|
||||||
|
using Microsoft.Playwright;
|
||||||
|
|
||||||
|
${this._mode === 'nunit' ? '[Parallelizable(ParallelScope.Self)]' : '[TestClass]'}
|
||||||
|
public class Tests : PageTest
|
||||||
|
{`);
|
||||||
|
const formattedContextOptions = formatContextOptions(options.contextOptions, options.deviceName);
|
||||||
|
if (formattedContextOptions) {
|
||||||
|
formatter.add(`public override BrowserNewContextOptions ContextOptions()
|
||||||
|
{
|
||||||
|
return ${formattedContextOptions};
|
||||||
|
}`);
|
||||||
|
formatter.newLine();
|
||||||
|
}
|
||||||
|
formatter.add(` [${this._mode === 'nunit' ? 'Test' : 'TestMethod'}]
|
||||||
|
public async Task MyTest()
|
||||||
|
{`);
|
||||||
return formatter.format();
|
return formatter.format();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -193,3 +193,80 @@ test('should work with --save-har', async ({ runCLI }, testInfo) => {
|
||||||
const json = JSON.parse(fs.readFileSync(harFileName, 'utf-8'));
|
const json = JSON.parse(fs.readFileSync(harFileName, 'utf-8'));
|
||||||
expect(json.log.creator.name).toBe('Playwright');
|
expect(json.log.creator.name).toBe('Playwright');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const testFramework of ['nunit', 'mstest'] as const) {
|
||||||
|
test(`should not print context options method override in ${testFramework} if no options were passed`, async ({ runCLI }) => {
|
||||||
|
const cli = runCLI([`--target=csharp-${testFramework}`, emptyHTML]);
|
||||||
|
await cli.waitFor(`Page.GotoAsync("${emptyHTML}")`);
|
||||||
|
expect(cli.text()).not.toContain('public override BrowserNewContextOptions ContextOptions()');
|
||||||
|
});
|
||||||
|
|
||||||
|
test(`should print context options method override in ${testFramework} if options were passed`, async ({ runCLI }) => {
|
||||||
|
const cli = runCLI([`--target=csharp-${testFramework}`, '--color-scheme=dark', emptyHTML]);
|
||||||
|
await cli.waitFor(`Page.GotoAsync("${emptyHTML}")`);
|
||||||
|
expect(cli.text()).toContain(` public override BrowserNewContextOptions ContextOptions()
|
||||||
|
{
|
||||||
|
return new BrowserNewContextOptions
|
||||||
|
{
|
||||||
|
ColorScheme = ColorScheme.Dark,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test(`should print a valid basic program in mstest`, async ({ runCLI }) => {
|
||||||
|
const cli = runCLI([`--target=csharp-mstest`, '--color-scheme=dark', emptyHTML]);
|
||||||
|
await cli.waitFor(`Page.GotoAsync("${emptyHTML}")`);
|
||||||
|
const expected = `using Microsoft.Playwright.MSTest;
|
||||||
|
using Microsoft.Playwright;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class Tests : PageTest
|
||||||
|
{
|
||||||
|
public override BrowserNewContextOptions ContextOptions()
|
||||||
|
{
|
||||||
|
return new BrowserNewContextOptions
|
||||||
|
{
|
||||||
|
ColorScheme = ColorScheme.Dark,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task MyTest()
|
||||||
|
{
|
||||||
|
// Go to ${emptyHTML}
|
||||||
|
await Page.GotoAsync("${emptyHTML}");
|
||||||
|
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
expect(cli.text()).toContain(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(`should print a valid basic program in nunit`, async ({ runCLI }) => {
|
||||||
|
const cli = runCLI([`--target=csharp-nunit`, '--color-scheme=dark', emptyHTML]);
|
||||||
|
await cli.waitFor(`Page.GotoAsync("${emptyHTML}")`);
|
||||||
|
const expected = `using Microsoft.Playwright.NUnit;
|
||||||
|
using Microsoft.Playwright;
|
||||||
|
|
||||||
|
[Parallelizable(ParallelScope.Self)]
|
||||||
|
public class Tests : PageTest
|
||||||
|
{
|
||||||
|
public override BrowserNewContextOptions ContextOptions()
|
||||||
|
{
|
||||||
|
return new BrowserNewContextOptions
|
||||||
|
{
|
||||||
|
ColorScheme = ColorScheme.Dark,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task MyTest()
|
||||||
|
{
|
||||||
|
// Go to ${emptyHTML}
|
||||||
|
await Page.GotoAsync("${emptyHTML}");
|
||||||
|
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
expect(cli.text()).toContain(expected);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ const codegenLang2Id: Map<string, string> = new Map([
|
||||||
['Python Async', 'python-async'],
|
['Python Async', 'python-async'],
|
||||||
['Pytest', 'pytest'],
|
['Pytest', 'pytest'],
|
||||||
['C#', 'csharp'],
|
['C#', 'csharp'],
|
||||||
|
['C# NUnit', 'csharp-nunit'],
|
||||||
|
['C# MSTest', 'csharp-mstest'],
|
||||||
['Playwright Test', 'test'],
|
['Playwright Test', 'test'],
|
||||||
]);
|
]);
|
||||||
const codegenLangId2lang = new Map([...codegenLang2Id.entries()].map(([lang, langId]) => [langId, lang]));
|
const codegenLangId2lang = new Map([...codegenLang2Id.entries()].map(([lang, langId]) => [langId, lang]));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue