diff --git a/README.md b/README.md index 2002a18..ef0ee6e 100644 --- a/README.md +++ b/README.md @@ -118,3 +118,29 @@ class MyTestSuite { } } ``` + +### Skip test or suite: `@skip(reason?: string)` +Skip single method or test suite. + +```ts +import { suite, test, skip } from 'playwright-decorators'; + +// Skip test suite +@skip() // <-- Decorate suite with @skip() +@suite() +class SkippedTestSuite { +} + +// Or skip selected test +@suite() +class MyTestSuite { + @skip() // <-- Decorate test with @skip() + @test() + async skippedTest({ page }) { + // ... + } +} +``` + +#### Options +- `reason` (optional) - reason of skipping. Will be displayed in the test report. diff --git a/lib/index.ts b/lib/index.ts index b7be5a3..101752f 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,6 +1,10 @@ +// base export { suite } from './suite.decorator'; export { test } from './test.decorator'; +// hooks export { beforeAll } from './beforeAll.decorator'; export { beforeEach } from './beforeEach.decorator'; export { afterAll } from './afterAll.decorator'; export { afterEach } from './afterEach.decorator'; +// annotations +export { skip } from './skip.decorator'; diff --git a/lib/skip.decorator.ts b/lib/skip.decorator.ts new file mode 100644 index 0000000..6fef29e --- /dev/null +++ b/lib/skip.decorator.ts @@ -0,0 +1,17 @@ +import {SuiteDecoratedMethod} from "./suite.decorator"; +import {TestDecoratedMethod} from "./test.decorator"; + +/** + * Skip @test or @suite (with optional reason). + */ +export const skip = (reason?: string) => function(originalMethod: any, context?: any) { + if ((originalMethod as SuiteDecoratedMethod)?.suiteDecorator) { + originalMethod.suiteDecorator.skip = reason || true; + return; + } + + if ((originalMethod as TestDecoratedMethod)?.testDecorator) { + originalMethod.testDecorator.skip = reason || true; + return; + } +} diff --git a/lib/suite.decorator.ts b/lib/suite.decorator.ts index 68845ec..5e96458 100644 --- a/lib/suite.decorator.ts +++ b/lib/suite.decorator.ts @@ -7,18 +7,37 @@ interface SuiteDecoratorOptions { * Name of the suite. Default: name of the suite class */ name?: string; + /** + * Skip suite (with optional reason) + */ + skip?: string | boolean; } class SuiteDecorator implements SuiteDecoratorOptions { name: string; + skip: string | boolean = false; constructor(private suiteClass: Constructor, options: SuiteDecoratorOptions) { this.name = suiteClass.name; Object.assign(this, options); } + + private handleSkip() { + if (this.skip === false) { + return; + } + + if (typeof this.skip === 'string') { + return playwright.skip(true, this.skip); + } + + playwright.skip(); + } private runSuite(userSuiteCode: () => Promise) { + this.handleSkip(); + return userSuiteCode(); } diff --git a/lib/test.decorator.ts b/lib/test.decorator.ts index 5b18dc5..c809b0c 100644 --- a/lib/test.decorator.ts +++ b/lib/test.decorator.ts @@ -6,10 +6,15 @@ interface TestDecoratorOptions { * Name of the test. Default: name of the method */ name?: string; + /** + * Skip suite (with optional reason) + */ + skip?: string | boolean; } class TestDecorator implements TestDecoratorOptions { name: string; + skip: string | boolean = false; constructor(private testMethod: any, options: TestDecoratorOptions) { this.name = testMethod.name; @@ -17,11 +22,25 @@ class TestDecorator implements TestDecoratorOptions { Object.assign(this, options); } + private handleSkip() { + if (this.skip === false) { + return; + } + + if (typeof this.skip === 'string') { + return playwright.skip(true, this.skip); + } + + playwright.skip(); + } + /** * Run playwright.test function using all collected data. */ run(executionContext: any) { const decoratedTest: TestDecoratorFunction = (testFunction) => (...args) => { + this.handleSkip(); + // set correct executionContext (test class) return testFunction.call(executionContext, ...args); }; diff --git a/tests/suite.spec.ts b/tests/suite.spec.ts index 7954be5..25eb69b 100644 --- a/tests/suite.spec.ts +++ b/tests/suite.spec.ts @@ -1,28 +1,44 @@ -import { suite } from '../lib'; +import {skip, suite, test} from '../lib'; import playwright, {expect} from '@playwright/test'; playwright.describe('@suite decorator', () => { - let withSuiteDecoratorRun = false; - let withoutSuiteDecoratorRun = false; - - @suite() - class WithSuiteDecorator { - constructor() { - withSuiteDecoratorRun = true; + playwright.describe('Class with @suite should be initialized', () => { + const called: string[] = []; + + @suite() + class WithSuiteDecorator { + constructor() { + called.push('constructor'); + } } - } - class WithoutSuiteDecorator { - constructor() { - withoutSuiteDecoratorRun = true; - } - } - - playwright('Class with @suite should be initialized', () => { - expect(withSuiteDecoratorRun).toBeTruthy(); + expect(called).toContain('constructor'); }); playwright('Class without @suite should not be initialized', () => { - expect(withoutSuiteDecoratorRun).toBeFalsy(); + const called: string[] = []; + + class WithoutSuiteDecorator { + constructor() { + called.push('constructor'); + } + } + + expect(called).not.toContain('constructor'); + }); + + playwright.describe('Class with @suite & @skip should not run any tests', () => { + const called: string[] = []; + + @skip() + @suite() + class WithSuiteDecorator { + @test() + testMethod() { + called.push('testMethod'); + } + } + + expect(called).not.toContain(['testMethod']); }); }) diff --git a/tests/test.spec.ts b/tests/test.spec.ts index 8cdc483..82c6cdd 100644 --- a/tests/test.spec.ts +++ b/tests/test.spec.ts @@ -1,5 +1,5 @@ import playwright, {expect} from "@playwright/test"; -import {suite, test} from "../lib"; +import {skip, suite, test} from "../lib"; playwright.describe('@test decorator', () => { const called: string[] = []; @@ -44,4 +44,27 @@ playwright.describe('@test decorator', () => { playwright.afterAll(() => { expect(called.length).toEqual(4); }) + + playwright.describe('with @skip', () => { + const called: string[] = []; + + @suite() + class withSkipSuite { + @test() + test() { + called.push('test') + } + + @skip() + @test() + skippedTest() { + called.push('skippedTest') + } + } + + playwright('should not run skipped tests', () => { + expect(called).toContain('test'); + expect(called).not.toContain('skippedTest'); + }) + }) })