Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use dynamic fixtures instead of hardcoded #11

Merged
merged 4 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,36 @@ class MyTestSuite {
```

## 📝 Documentation
### `@suite()`, `@suite(options)` => `describe()`
Mark class as a test suite.
Runs all class methods decorated by `@test` inside `options.name` context (`describe(options.name, all_methods_decorated_by_@test`).
### Creating a test suite: `@suite(options?)`
Decorate a class with `@suite()` or `@suite(options)` to create a test suite.
Under the hood, decorator creates a `describe` block and runs all methods decorated by `@test` inside it.

```ts
import { suite } from 'playwright-decorators';

@suite() // <-- Decorate class with @suite() or @suite(options)
class MyTestSuite {
// ...
}
```

#### Options
- `name` (optional) - name of the test suite. By default, name of the class.

### `@test()`, `@test(options)` => `test()`
Mark method as a test. Run method using `test(option.name, method)`.
### Creating a test: `@test(options?)`
You can create a test by decorating a method with `@test()` or `@test(options)` decorator.

```ts
import { suite, test } from 'playwright-decorators';

@suite()
class MyTestSuite {
@test() // <-- Decorate test method with @test() or @test(options)
async myTest({ page }) {
// ...
}
}
```

#### Options
- `name` (optional) - name of the test. By default, name of the method.
6 changes: 5 additions & 1 deletion dts-bundle-generator.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ const config = {
entries: [
{
filePath: './lib/index.ts',
outFile: './dist/index.d.ts'
outFile: './dist/index.d.ts',
output: {
exportReferencedTypes: false,
noBanner: true
}
}
]
};
Expand Down
17 changes: 17 additions & 0 deletions lib/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {TestInfo} from "@playwright/test";

export type PlaywrightTestFunction = (args: any, testInfo: TestInfo ) => Promise<void> | void;

Check warning on line 3 in lib/helpers.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
export type TestDecoratorFunction = (testFunction: PlaywrightTestFunction) => PlaywrightTestFunction;

/**
* Wrap a playwright test function with class method, and make it visible externally as original one (function description).
* It is required, as @playwright/test function do not accept rest parameters.
*/
export const decoratePlaywrightTest = (testFunction: PlaywrightTestFunction, decorationFunction: TestDecoratorFunction) => {
const decoratedTestFunction = decorationFunction(testFunction);

// expose original function description
decoratedTestFunction.toString = () => testFunction.toString();

return decoratedTestFunction;
}
4 changes: 2 additions & 2 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './suite.decorator';
export * from './test.decorator';
export { suite } from './suite.decorator';
export { test } from './test.decorator';
13 changes: 11 additions & 2 deletions lib/suite.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import playwright from '@playwright/test';

type Constructor = { new (...args: any[]): any };

Check warning on line 3 in lib/suite.decorator.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type

Check warning on line 3 in lib/suite.decorator.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type

interface SuiteDecoratorOptions {
/**
Expand All @@ -18,10 +18,13 @@
Object.assign(this, options);
}

private runSuite(userSuiteCode: () => Promise<any>) {

Check warning on line 21 in lib/suite.decorator.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
return userSuiteCode();
}


/**
* Run playwright.describe function using all collected data.
*/
run() {
playwright.describe(this.name, () => {
return this.runSuite(() => new this.suiteClass())
Expand All @@ -33,10 +36,16 @@

/**
* Mark class as test suite.
* Decorator creates a `describe` block and runs all methods decorated by `@test` inside it.
*
* Behaviour of decorator can be modified by other decorators using injected `suiteDecorator` property.
*/
export const suite = (options: SuiteDecoratorOptions = {}) => function<T extends Constructor>(constructor: T, context?: ClassMethodDecoratorContext) {
const suiteDecorator = new SuiteDecorator(constructor, options);


/**
* Decorate class by `suiteDecorator` property, to allow other decorators to modify suite behaviour / options.
*/
Object.assign(constructor, { suiteDecorator });

context?.addInitializer(() => {
Expand Down
27 changes: 20 additions & 7 deletions lib/test.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import playwright from '@playwright/test';
import {decoratePlaywrightTest, TestDecoratorFunction} from "./helpers";

interface TestDecoratorOptions {
/**
Expand All @@ -10,28 +11,40 @@
class TestDecorator implements TestDecoratorOptions {
name: string;

constructor(private testMethod: any, options: TestDecoratorOptions) {

Check warning on line 14 in lib/test.decorator.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
this.name = testMethod.name;

Object.assign(this, options);
}


/**
* Run playwright.test function using all collected data.
*/
run(executionContext: any) {

Check warning on line 23 in lib/test.decorator.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
// playwright function do not accept ...rest arguments, so we need to request all of them and pass to the testMethod manually
playwright(this.name, ({playwright, context, browserName, browser, contextOptions, connectOptions, page, testIdAttribute, launchOptions, defaultBrowserType, baseURL, channel, acceptDownloads, bypassCSP, deviceScaleFactor, extraHTTPHeaders, httpCredentials, ignoreHTTPSErrors, geolocation, hasTouch, headless, isMobile, javaScriptEnabled, locale, navigationTimeout, actionTimeout, offline, permissions, proxy, request, serviceWorkers, screenshot, trace, storageState, timezoneId, video, viewport, userAgent, colorScheme
}, ...args) => {
return this.testMethod.call(executionContext, {playwright, context, browserName, browser, contextOptions, connectOptions, page, testIdAttribute, launchOptions, defaultBrowserType, baseURL, channel, acceptDownloads, bypassCSP, deviceScaleFactor, extraHTTPHeaders, httpCredentials, ignoreHTTPSErrors, geolocation, hasTouch, headless, isMobile, javaScriptEnabled, locale, navigationTimeout, actionTimeout, offline, permissions, proxy, request, serviceWorkers, screenshot, trace, storageState, timezoneId, video, viewport, userAgent, colorScheme}, ...args);
})
const decoratedTest: TestDecoratorFunction = (testFunction) => (...args) => {
// set correct executionContext (test class)
return testFunction.call(executionContext, ...args);
};

const decoratedTestMethod = decoratePlaywrightTest(
this.testMethod,
decoratedTest
);

playwright(this.name, decoratedTestMethod);
}
}

export type TestDecoratedMethod = { testDecorator: TestDecorator };

/**
* Mark method as test.
* Method class should be marked with @suite decorator
* Decorator creates a `test` block and runs method inside it.
* Target class should be marked by @suite decorator.
*
* Behaviour of decorator can be modified by other decorators using injected `testDecorator` property.
*/
export const test = (options: TestDecoratorOptions = {}) => function(originalMethod: any, context: any) {

Check warning on line 47 in lib/test.decorator.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type

Check warning on line 47 in lib/test.decorator.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
const testDecorator = new TestDecorator(originalMethod, options);

Object.assign(originalMethod, { testDecorator });
Expand Down