diff --git a/README.md b/README.md index 54f1a55..536e04f 100644 --- a/README.md +++ b/README.md @@ -152,14 +152,21 @@ class MyTestSuite { - `reason` (optional) - reason of skipping. Will be displayed in the test report. -### Mark test as "should fail": `@fail(reason?: string)` -Mark single `@test` as "should fail". +### Mark test or suite as "should fail": `@fail(reason?: string)` +Mark single `@test` or `@suite` as "should fail". Playwright Test runs this test and ensures that it is actually failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed. ```ts import { suite, test, fail } from 'playwright-decorators'; +// Mark suite as "fail", ensure that all tests from suite fail +@fail() // <-- Decorate suite with @fail() +@suite() +class FailTestSuite { +} + +// Or mark selected test as "fail" @suite() class MyTestSuite { @fail() // <-- Decorate test with @fail() @@ -174,6 +181,34 @@ class MyTestSuite { - `reason` (optional) - reason of marking as "should fail". Will be displayed in the test report. +### Mark test or suite as "fixme", with the intention to fix it: `@fixme(reason?: string)` +Marks a `@test` or `@suite` as "fixme", with the intention to fix (with optional reason). +Decorated tests or suites will not be run. + +```ts +import { suite, test, fixme } from 'playwright-decorators'; + +// Mark test suite as "fixme" +@fixme() // <-- Decorate suite with @fixme() +@suite() +class FixmeTestSuite { +} + +// Or mark selected test as "fixme" +@suite() +class MyTestSuite { + @fixme() // <-- Decorate test with @fixme() + @test() + async fixmeTest({ page }) { + // ... + } +} +``` + +#### Options +- `reason` (optional) - reason of marking as "fixme". Will be displayed in the test report. + + ### Mark test or suite as "slow": `@slow(reason?: string)` Mark single `@test` or `@suite` as "slow". Slow test will be given triple the default timeout. diff --git a/lib/fail.decorator.ts b/lib/fail.decorator.ts index 28aa38e..0787c2a 100644 --- a/lib/fail.decorator.ts +++ b/lib/fail.decorator.ts @@ -1,11 +1,17 @@ +import {SuiteDecoratedMethod} from "./suite.decorator"; import {TestDecoratedMethod} from "./test.decorator"; /** - * Marks a @test as "should fail". + * Marks a @test or @suite as "should fail". * Playwright Test runs this test and ensures that it is actually failing. * This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed. */ export const fail = (reason?: string) => function(originalMethod: any, context?: any) { + if ((originalMethod as SuiteDecoratedMethod)?.suiteDecorator) { + originalMethod.suiteDecorator.fail = reason || true; + return; + } + if ((originalMethod as TestDecoratedMethod)?.testDecorator) { originalMethod.testDecorator.fail = reason || true; return; diff --git a/lib/fixme.decorator.ts b/lib/fixme.decorator.ts new file mode 100644 index 0000000..93e849d --- /dev/null +++ b/lib/fixme.decorator.ts @@ -0,0 +1,18 @@ +import {SuiteDecoratedMethod} from "./suite.decorator"; +import {TestDecoratedMethod} from "./test.decorator"; + +/** + * Marks a @test or @suite as "fixme", with the intention to fix (with optional reason). + * Decorated tests or suites will not be run. + */ +export const fixme = (reason?: string) => function(originalMethod: any, context?: any) { + if ((originalMethod as SuiteDecoratedMethod)?.suiteDecorator) { + originalMethod.suiteDecorator.fixme = reason || true; + return; + } + + if ((originalMethod as TestDecoratedMethod)?.testDecorator) { + originalMethod.testDecorator.fixme = reason || true; + return; + } +} diff --git a/lib/index.ts b/lib/index.ts index 874de43..86fa54a 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -10,6 +10,7 @@ export { afterEach } from './afterEach.decorator'; export { skip } from './skip.decorator'; export { slow } from './slow.decorator'; export { fail } from './fail.decorator'; +export { fixme } from './fixme.decorator'; export { only } from './only.decorator'; // helpers export { tag } from './tag.decorator'; diff --git a/lib/suite.decorator.ts b/lib/suite.decorator.ts index c937705..3316634 100644 --- a/lib/suite.decorator.ts +++ b/lib/suite.decorator.ts @@ -16,6 +16,17 @@ interface SuiteDecoratorOptions { * Slow test will be given triple the default timeout. */ slow?: string | boolean; + /** + * Marks a suite as "should fail". + * Playwright Test runs all test from suite and ensures that they are actually failing. + * This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixe + */ + fail?: string | boolean + /** + * Marks a suite as "fixme", with the intention to fix (with optional reason). + * Decorated suite will not be run. + */ + fixme?: string | boolean; /** * Declares a focused suite. * If there are some focused @test(s) or @suite(s), all of them will be run but nothing else. @@ -27,6 +38,8 @@ class SuiteDecorator implements SuiteDecoratorOptions { name: string; skip: string | boolean = false; slow: string | boolean = false; + fail: string | boolean = false; + fixme: string | boolean = false; only = false; constructor(private suiteClass: Constructor, options: SuiteDecoratorOptions) { @@ -58,10 +71,36 @@ class SuiteDecorator implements SuiteDecoratorOptions { return playwright.slow(); } + + private handleFail() { + if (this.fail === false) { + return; + } + + if (typeof this.fail === 'string') { + return playwright.fail(true, this.fail); + } + + return playwright.fail(); + } + + private handleFixme() { + if (this.fixme === false) { + return; + } + + if (typeof this.fixme === 'string') { + return playwright.fixme(true, this.fixme); + } + + return playwright.fixme(); + } private runSuite(userSuiteCode: () => Promise) { this.handleSkip(); this.handleSlow(); + this.handleFail(); + this.handleFixme(); return userSuiteCode(); } diff --git a/lib/test.decorator.ts b/lib/test.decorator.ts index 5b6d464..8ee7cda 100644 --- a/lib/test.decorator.ts +++ b/lib/test.decorator.ts @@ -21,6 +21,11 @@ interface TestDecoratorOptions { * This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixe */ fail?: string | boolean + /** + * Marks a test as "fixme", with the intention to fix (with optional reason). + * Decorated test will not be run. + */ + fixme?: string | boolean; /** * Declares a focused test. * If there are some focused @test(s) or @suite(s), all of them will be run but nothing else. @@ -33,6 +38,7 @@ class TestDecorator implements TestDecoratorOptions { skip: string | boolean = false; slow: string | boolean = false; fail: string | boolean = false; + fixme: string | boolean = false; only = false; constructor(private testMethod: any, options: TestDecoratorOptions) { @@ -77,6 +83,18 @@ class TestDecorator implements TestDecoratorOptions { return playwright.fail(); } + private handleFixme() { + if (this.fixme === false) { + return; + } + + if (typeof this.fixme === 'string') { + return playwright.fixme(true, this.fixme); + } + + return playwright.fixme(); + } + /** * Run playwright.test function using all collected data. */ @@ -85,6 +103,7 @@ class TestDecorator implements TestDecoratorOptions { this.handleSkip(); this.handleSlow(); this.handleFail(); + this.handleFixme(); // set correct executionContext (test class) return testFunction.call(executionContext, ...args); diff --git a/tests/fail.spec.ts b/tests/fail.spec.ts index 9b3396c..3ccb847 100644 --- a/tests/fail.spec.ts +++ b/tests/fail.spec.ts @@ -2,6 +2,30 @@ import playwright, {expect} from "@playwright/test"; import {suite, test, fail} from "../lib"; playwright.describe('@fail decorator', () => { + playwright.describe('with @suite', () => { + const called: string[] = []; + + @fail() + @suite() + class SkipSuite { + @test() + async test() { + called.push('test'); + expect(true).toBe(false); + } + + @test() + async test2() { + called.push('test2'); + expect(true).toBe(false); + } + } + + playwright('@fail decorator should not throw error when tests intentionally fails', () => { + expect(called).toEqual(['test', 'test2']); + }); + }); + playwright.describe('with @test', () => { const called: string[] = []; @@ -15,7 +39,7 @@ playwright.describe('@fail decorator', () => { } } - playwright('@fail decorator should not throw error when test fails', () => { + playwright('@fail decorator should not throw error when test intentionally fails', () => { // is called and not throw error expect(called).toEqual(['failingTest']); }); diff --git a/tests/fixme.spec.ts b/tests/fixme.spec.ts new file mode 100644 index 0000000..e29ff06 --- /dev/null +++ b/tests/fixme.spec.ts @@ -0,0 +1,53 @@ +import playwright, {expect} from "@playwright/test"; +import {suite, test, slow, beforeAll, skip, fixme} from "../lib"; + +playwright.describe('@fixme decorator', () => { + playwright.describe('with @suite', () => { + const called: string[] = []; + + @fixme() + @suite() + class SkipSuite { + @test() + async test() { + called.push('test'); + } + + @test() + async test2() { + called.push('test2'); + } + } + + playwright('@fixme decorator should not call any @test methods from suite', () => { + expect(called).toEqual([]); + }); + }); + + playwright.describe('with @test', () => { + const called: string[] = []; + + @suite() + class TestSuite { + @test() + async test() { + called.push('test'); + } + + @fixme() + @test() + async fixmeTest() { + called.push('fixmeTest'); + } + + @test() + async test2() { + called.push('test2'); + } + } + + playwright('@fixme decorator should not execute @test method', () => { + expect(called).not.toContain('fixmeTest'); + }) + }); +});