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

feat: Smart Tagging #122

Merged
merged 4 commits into from
Dec 8, 2018
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
36 changes: 33 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[![CircleCI](https://circleci.com/gh/TheBrainFamily/cypress-cucumber-preprocessor.svg?style=shield)](https://circleci.com/gh/TheBrainFamily/cypress-cucumber-preprocessor)
# Run cucumber/gherkin-syntaxed specs with cypress.io

Follow the Setup steps, or if you prefer to hack on a working example, take a look [https://github.com/TheBrainFamily/cypress-cucumber-example](https://github.com/TheBrainFamily/cypress-cucumber-example
Follow the Setup steps, or if you prefer to hack on a working example, take a look at [https://github.com/TheBrainFamily/cypress-cucumber-example](https://github.com/TheBrainFamily/cypress-cucumber-example
)

## Setup
Expand Down Expand Up @@ -34,12 +34,15 @@ Example: cypress/integration/Google.feature
Feature: The Facebook

I want to open a social network page


@focus
Scenario: Opening a social network page
Given I open Google page
Then I see "google" in the title
```

(the @focus tag is not necessary, but we want to you to notice it so you look it up below. *It will speed up your workflow significantly*!)

### Step definitions

#### Cypress Cucumber Preprocessor Style (recommended!)
Expand Down Expand Up @@ -93,7 +96,7 @@ The problem with the legacy structure is that everything is global. This is prob
- It makes it harder to create .feature files that read nicely - you have to make sure you are not stepping on toes of already existing step definitions. You should be able to write your tests without worrying about reusability, complex regexp matches, or anything like that. Just write a story. Explain what you want to see without getting into the details. Reuse in the .js files, not in something you should consider an always up-to-date, human-readable documentation.
- The startup times get much worse - because we have to analyze all the different step definitions so we can match the .feature files to the test.
- Hooks are problematic. If you put before() in a step definition file, you might think that it will run only for the .feature file related to that step definition. You try the feature you work on, everything seems fine and you push the code. Here comes a surprise - it will run for ALL .feature files in your whole project. Very unintuitive. And good luck debugging problems caused by that! This problem was not unique to this plugin, bo to the way cucumberjs operates.
Let's look how this differs with the proposed structure. Assuming you want to have a hook before ./Google.feature file, just create a ./Google/before.js and put the hook there. This should take care of long requested feature - (https://github.com/TheBrainFamily/cypress-cucumber-preprocessor/issues/25)[#25]
Let's look how this differs with the proposed structure. Assuming you want to have a hook before ./Google.feature file, just create a ./Google/before.js and put the hook there. This should take care of long requested feature - [https://github.com/TheBrainFamily/cypress-cucumber-preprocessor/issues/25](#25)

If you have a few tests the "oldschool" style is completely fine. But for a large enterprise-grade application, with hundreds or sometimes thousands of .feature files, the fact that everything is global becomes a maintainability nightmare.

Expand Down Expand Up @@ -224,6 +227,33 @@ In order to initialize tests using tags you will have to run cypress and pass TA
Example:
```cypress run -e TAGS='not @foo and (@bar or @zap)'```

### Smart tagging
Start your tests without setting any tags. And then put a @focus on the scenario (or scenarios) you want to focus on while development or bug fixing.

For example:
```gherkin
Feature: Smart Tagging

As a cucumber cypress plugin which handles Tags
I want to allow people to select tests to run if focused
So they can work more efficiently and have a shorter feedback loop

Scenario: This scenario should not run if @focus is on another scenario
Then this unfocused scenario should not run

@focus
Scenario: This scenario is focused and should run
Then this focused scenario should run

@this-tag-affects-nothing
Scenario: This scenario should also not run
Then this unfocused scenario should not run

@focus
Scenario: This scenario is also focused and also should run
Then this focused scenario should run
```

## Custom Parameter Type Resolves

Thanks to @Oltodo we can now use Custom Parameter Type Resolves.
Expand Down
20 changes: 20 additions & 0 deletions cypress/integration/SmartTagging.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Feature: Smart Tagging

As a cucumber cypress plugin which handles Tags
I want to allow people to select tests to run if focused
So they can work more efficiently and have a shorter feedback loop

Scenario: This scenario should not run if @focus is on another scenario
Then this unfocused scenario should not run

@focus
Scenario: This scenario is focused and should run
Then this focused scenario should run

@this-tag-affects-nothing
Scenario: This scenario should also not run
Then this unfocused scenario should not run

@focus
Scenario: This scenario is also focused and also should run
Then this focused scenario should run
11 changes: 11 additions & 0 deletions cypress/support/step_definitions/smart_tagging.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const assert = require("assert");
// eslint-disable-next-line import/no-extraneous-dependencies
const { Then } = require("cypress-cucumber-preprocessor/steps");

Then(`this focused scenario should run`, () => {
assert(true);
});

Then(`this unfocused scenario should not run`, () => {
assert(false);
});
51 changes: 34 additions & 17 deletions lib/createTestsFromFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,55 @@ const createTestsFromFeature = parsedFeature => {
const featureTags = parsedFeature.feature.tags;
const hasEnvTags = !!getEnvTags();
const hasFeatureTags = featureTags && featureTags.length > 0;
const sectionsWithTags = parsedFeature.feature.children.filter(
section => section.tags && section.tags.length
);

const sectionsWithTagsExist = sectionsWithTags.length > 0;

let featureShouldRun = true;
let everythingShouldRun = false;
let featureShouldRun = false;
let taggedScenarioShouldRun = false;
let anyFocused = false;
if (hasEnvTags) {
if (hasFeatureTags) {
featureShouldRun = shouldProceedCurrentStep(featureTags);
featureShouldRun = shouldProceedCurrentStep(featureTags);
taggedScenarioShouldRun = parsedFeature.feature.children.some(
section =>
section.tags &&
section.tags.length &&
shouldProceedCurrentStep(section.tags)
);
} else if (!hasFeatureTags && !sectionsWithTagsExist) {
everythingShouldRun = true;
} else if (sectionsWithTagsExist) {
anyFocused = sectionsWithTags.some(section =>
section.tags.find(t => t.name === "@focus")
);
if (anyFocused) {
taggedScenarioShouldRun = true;
} else {
featureShouldRun = false;
everythingShouldRun = true;
}
}

const taggedScenarioShouldRun = parsedFeature.feature.children.some(
section =>
section.tags &&
section.tags.length &&
shouldProceedCurrentStep(section.tags)
);

// eslint-disable-next-line prefer-arrow-callback
describe(parsedFeature.feature.name, function() {
if (featureShouldRun || taggedScenarioShouldRun) {
if (everythingShouldRun || featureShouldRun || taggedScenarioShouldRun) {
const backgroundSection = parsedFeature.feature.children.find(
section => section.type === "Background"
);
const otherSections = parsedFeature.feature.children.filter(
section => section.type !== "Background"
);
otherSections.forEach(section => {
const scenarioHasTags = section.tags.length > 0;
const shouldRun =
hasEnvTags && scenarioHasTags
? shouldProceedCurrentStep(section.tags.concat(featureTags)) // concat handles inheritance of tags from feature
: featureShouldRun;
let shouldRun;
if (anyFocused) {
shouldRun = section.tags.find(t => t.name === "@focus");
} else {
shouldRun =
everythingShouldRun ||
shouldProceedCurrentStep(section.tags.concat(featureTags)); // concat handles inheritance of tags from feature
}
if (shouldRun) {
createTestFromScenario(section, backgroundSection);
}
Expand Down
4 changes: 3 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ const preprocessor = (options = browserify.defaultOptions) => file => {
watcher.close();
}
watcher = chokidar
.watch(stepDefinitionPath(), { ignoreInitial: true })
.watch([`${stepDefinitionPath()}*.js`, `${stepDefinitionPath()}*.ts`], {
ignoreInitial: true
})
.on("all", () => {
touch(file.filePath);
});
Expand Down
11 changes: 11 additions & 0 deletions lib/resolveStepDefinition.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,14 @@ describe("Tags with env TAGS set", () => {
)
);
});

describe("Smart tagging", () => {
window.Cypress = {
env: () => ""
};
require("../cypress/support/step_definitions/smart_tagging");

createTestsFromFeature(
readAndParseFeatureFile("./cypress/integration/SmartTagging.feature")
);
});