From e2f390707f27b7348d7e527178b98f00702de31d Mon Sep 17 00:00:00 2001 From: Vitaliy Potapov Date: Tue, 26 Mar 2024 11:29:23 +0400 Subject: [PATCH] feature: add option --unused-steps to bddgen export, see #113 --- CHANGELOG.md | 1 + docs/cli.md | 10 +++--- src/cli/commands/export.ts | 33 +++++++++++++++---- src/cucumber/resolveFeaturePaths.ts | 2 +- src/gen/index.ts | 22 ++++++++++--- src/gen/testFile.ts | 3 ++ .../features/sample.feature | 6 ++++ test/cli-command-export/playwright.config.ts | 5 +-- test/cli-command-export/{ => steps}/steps.ts | 0 test/cli-command-export/{ => steps}/steps2.ts | 0 test/cli-command-export/test.mjs | 22 ++++++++++--- test/cli-option-verbose/test.mjs | 3 +- 12 files changed, 84 insertions(+), 23 deletions(-) create mode 100644 test/cli-command-export/features/sample.feature rename test/cli-command-export/{ => steps}/steps.ts (100%) rename test/cli-command-export/{ => steps}/steps2.ts (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac0a2bbe..e5d18d11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## dev +* feature: add option `--unused-steps` to `bddgen export`, [#113](https://github.com/vitalets/playwright-bdd/issues/113) * feature: show stdout / stderr in Cucumber reports, [#116](https://github.com/vitalets/playwright-bdd/issues/116) * feature: call step from step, [#110](https://github.com/vitalets/playwright-bdd/issues/110) diff --git a/docs/cli.md b/docs/cli.md index b2756c6a..b00d8796 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -38,17 +38,17 @@ $ npx bddgen export ``` Example output: ``` -List of all steps found by config: playwright.config.ts +Using config: playwright.config.ts +List of all steps (4): * Given I am on todo page * When I add todo {string} * When I remove todo {string} * Then visible todos count is {int} ``` -To run it with custom Playwright config use `--config` option: -``` -npx bddgen export --config path/to/playwright.config.ts -``` +#### Options +* `--config` - path to Playwright config +* `--unused-step` - output only unused steps ## `bddgen env` Displays info about current environment: diff --git a/src/cli/commands/export.ts b/src/cli/commands/export.ts index dd8a9439..a76ad18e 100644 --- a/src/cli/commands/export.ts +++ b/src/cli/commands/export.ts @@ -11,17 +11,24 @@ import { TestFilesGenerator } from '../../gen'; const logger = new Logger({ verbose: true }); +type Opts = ConfigOption & { + unusedSteps?: boolean; +}; + export const exportCommand = new Command('export') - .description('Prints all step definitions') + .description('Prints step definitions') .addOption(configOption) - .action(async (opts: ConfigOption) => { + .option('--unused-steps', 'Output only unused steps') + .action(async (opts: Opts) => { const { resolvedConfigFile } = await loadPlaywrightConfig(opts.config); - logger.log( - `List of all steps found by config: ${path.relative(process.cwd(), resolvedConfigFile)}\n`, - ); + logger.log(`Using config: ${path.relative(process.cwd(), resolvedConfigFile)}`); const configs = Object.values(getEnvConfigs()); assertConfigsCount(configs); - await showStepsForConfigs(configs); + if (opts.unusedSteps) { + await showUnusedStepsForConfigs(configs); + } else { + await showStepsForConfigs(configs); + } }); async function showStepsForConfigs(configs: BDDConfig[]) { @@ -35,6 +42,20 @@ async function showStepsForConfigs(configs: BDDConfig[]) { await Promise.all(tasks); + logger.log(`List of all steps (${steps.size}):`); + steps.forEach((stepText) => logger.log(stepText)); +} + +async function showUnusedStepsForConfigs(configs: BDDConfig[]) { + const steps = new Set(); + const tasks = configs.map(async (config) => { + const stepDefinitions = await new TestFilesGenerator(config).extractUnusedSteps(); + stepDefinitions.forEach((s) => steps.add(`* ${getStepText(s)}`)); + }); + + await Promise.all(tasks); + + logger.log(`List of unused steps (${steps.size}):`); steps.forEach((stepText) => logger.log(stepText)); } diff --git a/src/cucumber/resolveFeaturePaths.ts b/src/cucumber/resolveFeaturePaths.ts index b1932c90..885718bd 100644 --- a/src/cucumber/resolveFeaturePaths.ts +++ b/src/cucumber/resolveFeaturePaths.ts @@ -22,7 +22,7 @@ import { resolvePackageRoot } from '../utils'; export async function resovleFeaturePaths( runConfiguration: IRunConfiguration, environment: IRunEnvironment = {}, -) { +): Promise<{ featurePaths: string[]; unexpandedFeaturePaths: string[] }> { const { cwd, stderr, debug } = mergeEnvironment(environment); const logger: ILogger = new ConsoleLogger(stderr, debug); const cucumberRoot = resolvePackageRoot('@cucumber/cucumber'); diff --git a/src/gen/index.ts b/src/gen/index.ts index e8ec3981..a190b8f2 100644 --- a/src/gen/index.ts +++ b/src/gen/index.ts @@ -55,6 +55,17 @@ export class TestFilesGenerator { return this.supportCodeLibrary.stepDefinitions; } + // todo: combine with extractSteps + async extractUnusedSteps() { + await this.loadCucumberConfig(); + await Promise.all([this.loadFeatures(), this.loadSteps()]); + this.buildFiles(); + return this.supportCodeLibrary.stepDefinitions.filter((stepDefinition) => { + const isUsed = this.files.some((file) => file.usedStepDefinitions.has(stepDefinition)); + return !isUsed; + }); + } + private async loadCucumberConfig() { const environment = { cwd: getPlaywrightConfigDir() }; const { runConfiguration } = await loadCucumberConfig( @@ -69,11 +80,14 @@ export class TestFilesGenerator { private async loadFeatures() { const cwd = getPlaywrightConfigDir(); - const { paths, defaultDialect } = this.runConfiguration.sources; - this.logger.log(`Loading features from: ${paths.join(', ')}`); + const { defaultDialect } = this.runConfiguration.sources; const { featurePaths } = await resovleFeaturePaths(this.runConfiguration, { cwd }); - await this.featuresLoader.load(featurePaths, { relativeTo: cwd, defaultDialect }); - this.handleParseErrors(); + this.logger.log(`Loading features from paths (${featurePaths.length}):`); + featurePaths.forEach((featurePath) => this.logger.log(featurePath)); + if (featurePaths.length) { + await this.featuresLoader.load(featurePaths, { relativeTo: cwd, defaultDialect }); + this.handleParseErrors(); + } this.logger.log(`Loaded features: ${this.featuresLoader.getDocumentsCount()}`); } diff --git a/src/gen/testFile.ts b/src/gen/testFile.ts index d3715377..07e1e599 100644 --- a/src/gen/testFile.ts +++ b/src/gen/testFile.ts @@ -70,6 +70,7 @@ export class TestFile { public hasCustomTest = false; public undefinedSteps: UndefinedStep[] = []; public featureUri: string; + public usedStepDefinitions = new Set(); constructor(private options: TestFileOptions) { this.formatter = new Formatter(options.config); @@ -332,6 +333,8 @@ export class TestFile { return this.getMissingStep(enKeyword, keywordType, pickleStep); } + this.usedStepDefinitions.add(stepDefinition); + // for cucumber-style stepConfig is undefined const stepConfig = getStepConfig(stepDefinition); if (stepConfig?.hasCustomTest) this.hasCustomTest = true; diff --git a/test/cli-command-export/features/sample.feature b/test/cli-command-export/features/sample.feature new file mode 100644 index 00000000..a08c9166 --- /dev/null +++ b/test/cli-command-export/features/sample.feature @@ -0,0 +1,6 @@ +Feature: cli-command-export + Scenario: Simple scenario + Given I am on todo page + Then visible todos count is 2 + + diff --git a/test/cli-command-export/playwright.config.ts b/test/cli-command-export/playwright.config.ts index 8d7ac80f..5db92ac3 100644 --- a/test/cli-command-export/playwright.config.ts +++ b/test/cli-command-export/playwright.config.ts @@ -7,14 +7,15 @@ export default defineConfig({ name: 'project1', testDir: defineBddConfig({ outputDir: '.features-gen/one', - require: ['steps.ts'], + paths: ['features'], + require: ['steps/steps.ts'], }), }, { name: 'project2', testDir: defineBddConfig({ outputDir: '.features-gen/two', - require: ['steps.ts', 'steps2.ts'], + require: ['steps/steps.ts', 'steps/steps2.ts'], }), }, ], diff --git a/test/cli-command-export/steps.ts b/test/cli-command-export/steps/steps.ts similarity index 100% rename from test/cli-command-export/steps.ts rename to test/cli-command-export/steps/steps.ts diff --git a/test/cli-command-export/steps2.ts b/test/cli-command-export/steps/steps2.ts similarity index 100% rename from test/cli-command-export/steps2.ts rename to test/cli-command-export/steps/steps2.ts diff --git a/test/cli-command-export/test.mjs b/test/cli-command-export/test.mjs index 60075b47..5b247855 100644 --- a/test/cli-command-export/test.mjs +++ b/test/cli-command-export/test.mjs @@ -1,12 +1,26 @@ import { expect } from '@playwright/test'; -import { test, getTestName, execPlaywrightTest, BDDGEN_CMD } from '../helpers.mjs'; +import { test, TestDir, execPlaywrightTest, BDDGEN_CMD } from '../helpers.mjs'; + +const testDir = new TestDir(import.meta); + +test(`${testDir.name} (all steps)`, () => { + const stdout = execPlaywrightTest(testDir.name, `${BDDGEN_CMD} export`); + expect(stdout).toContain('Using config: playwright.config.ts'); + expect(stdout).toContain('List of all steps (5):'); -test(getTestName(import.meta), (t) => { - const stdout = execPlaywrightTest(t.name, `${BDDGEN_CMD} export`); - expect(stdout).toContain('List of all steps found by config: playwright.config.ts'); expect(stdout).toContain('* Given I am on todo page'); expect(stdout).toContain('* When I add todo {string}'); expect(stdout).toContain('* Then visible todos count is (\\d+)'); expect(stdout).toContain('* When some step'); expect(stdout).toContain('* Given I am on another todo page'); }); + +test(`${testDir.name} (unused steps)`, () => { + const stdout = execPlaywrightTest(testDir.name, `${BDDGEN_CMD} export --unused-steps`); + expect(stdout).toContain('Using config: playwright.config.ts'); + expect(stdout).toContain(`List of unused steps (3):`); + + expect(stdout).toContain('* When I add todo {string}'); + expect(stdout).toContain('* When some step'); + expect(stdout).toContain('* Given I am on another todo page'); +}); diff --git a/test/cli-option-verbose/test.mjs b/test/cli-option-verbose/test.mjs index 0447a717..7bc8fd51 100644 --- a/test/cli-option-verbose/test.mjs +++ b/test/cli-option-verbose/test.mjs @@ -6,7 +6,8 @@ const testDir = new TestDir(import.meta); test(testDir.name, () => { const stdout = execPlaywrightTest(testDir.name, `${BDDGEN_CMD} --verbose`); - expect(stdout).toContain('Loading features from: features/*.feature'); + expect(stdout).toContain('Loading features from paths (1):'); + expect(stdout).toContain(path.normalize('cli-option-verbose/features/sample.feature')); expect(stdout).toContain('Loading steps from: steps.ts'); expect(stdout).toContain('Clearing output dir:'); expect(stdout).toContain(