Skip to content

Commit f4dc849

Browse files
authored
[minor] Gherkin support (#49)
1 parent 136e11e commit f4dc849

26 files changed

+17036
-8189
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
/integration/downloads
1414
/integration/screenshots
1515
/integration/e2e/temp/
16+
/integration/cucumber/temp/
1617

1718
# coverage and test reports
1819
/reports
@@ -28,4 +29,10 @@
2829
/allure-results-jest
2930
/allure-report
3031
/filtered_tests.json
32+
33+
34+
# grep
35+
spec_pattern*.json
36+
filter*.json
37+
3138
adapter-ctl.sh

README.pack.md

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Some settings were taken from [@shelex/cypress-allure-plugin](https://www.npmjs.
2020
- Wraps custom commands into parent step, so report is cleaner
2121
- has interface to set status message for tests - `cy.allure().testDetails({ message: "This test is skipped because needs to be reviewed" })`
2222
- writes test files after each test - so you can watch execution in Allure TestOps
23+
- [gherkin support](./docs/gherkin.md)
2324

2425
Example report is here - [Allure Report example](https://mmisty.github.io/cypress-allure-adapter-example/)
2526

@@ -157,6 +158,7 @@ That's it! :tada:
157158
| **issuePrefix** <br/>_type: string_<br/><br/>ex: `http://jira.com` or `http://jira.com/PROJECT-1/*/browse` | The same as tmsPrefix - for issue `cy.allure().issue('PROJ-02')` |
158159
| **allureShowDuplicateWarn**<br/>_type: boolean_<br/>_default: false_ | Show console warnings about test duplicates. |
159160
| **allureShowTagsInTitle**<br/>_type: boolean_<br/>_default: undefined_ | Whether to show tags in test title or not. When undefined will keep title as is |
161+
| **allureAddNonSpecialTags**<br/>_type: boolean_<br/>_default: true_ | Whether to add non-special tags to tests |
160162

161163

162164
### tmsPrefix and issuePrefix

cypress.config.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { defineConfig } from 'cypress';
22
import { setupPlugins } from './integration/plugins';
33

44
const cypressFolder = 'integration';
5-
6-
export default defineConfig({
5+
export const commonConfig: Cypress.ConfigOptions = {
76
e2e: {
87
specPattern: `${cypressFolder}/e2e/**/*.(cy|test|spec).(j|t)s`,
98
supportFile: `${cypressFolder}/support/index.ts`,
@@ -32,6 +31,7 @@ export default defineConfig({
3231
allureAddVideoOnPass: 'true',
3332
allureShowDuplicateWarn: 'true',
3433
allureShowTagsInTitle: false,
34+
allureAddNonSpecialTags: 'true',
3535
// allureWrapCustomCommands: '!qaId,!cust',
3636
// allureWrapCustomCommands: 'qaId,cust',
3737
// allureLogCyCommands: 'false',
@@ -40,10 +40,12 @@ export default defineConfig({
4040
issuePrefix: 'http://jira/*',
4141
},
4242

43-
setupNodeEvents(on, config) {
44-
setupPlugins(on, config);
43+
async setupNodeEvents(on, config) {
44+
await setupPlugins(on, config);
4545

4646
return config;
4747
},
4848
},
49-
});
49+
};
50+
51+
export default defineConfig(commonConfig);

cypress.cucumber-test.config.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { defineConfig } from 'cypress';
2+
import { commonConfig } from './cypress.config';
3+
4+
export default defineConfig({
5+
...commonConfig,
6+
e2e: {
7+
...commonConfig.e2e,
8+
specPattern: 'integration/cucumber/temp/**/*.feature',
9+
env: {
10+
allure: true,
11+
...(commonConfig.e2e?.env || {}),
12+
cucumber: 'true',
13+
omitFiltered: true,
14+
filterSpecs: true,
15+
},
16+
},
17+
});

cypress.cucumber.config.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { defineConfig } from 'cypress';
2+
import { commonConfig } from './cypress.config';
3+
4+
export default defineConfig({
5+
...commonConfig,
6+
e2e: {
7+
...commonConfig.e2e,
8+
specPattern: 'integration/cucumber/**/**/*.feature',
9+
env: {
10+
...(commonConfig.e2e?.env || {}),
11+
cucumber: 'true',
12+
omitFiltered: true,
13+
filterSpecs: true,
14+
},
15+
},
16+
});

docs/gherkin.md

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## Gherkin support
2+
3+
This plugin supports gherkin support.
4+
5+
If you use `@badeball/cypress-cucumber-preprocessor` plugin
6+
then also there would be tags support available.
7+
8+
Special tags will add meta info to report.
9+
10+
Example(in this example it will add several links to test):
11+
```gherkin
12+
@issue("ABC-001")
13+
@tms("ABC-002")
14+
@tms("ABC-003","Description__of__ticket")
15+
Scenario: should have several tms tags
16+
Given I log message "test"
17+
```
18+
19+
Special tags are the same as allure interface items, see [interface](./interface.md)

integration/cucumber-steps/common.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { When, Then, Given } from '@badeball/cypress-cucumber-preprocessor';
2+
3+
When('I visit site', () => {
4+
cy.log('visit site');
5+
});
6+
7+
Then('I should see a search bar {string}', (text: string) => {
8+
cy.log(text);
9+
});
10+
11+
Given('I log message {string}', (text: string) => {
12+
cy.log(text);
13+
});

integration/cucumber/first.feature

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Feature: test cucumber
2+
Background:
3+
Given I visit site
4+
5+
Scenario: 01 visiting the frontpage
6+
Then I should see a search bar "hello"
7+
8+
Scenario: 02 visiting the frontpage
9+
Then I should see a search bar "hello"
10+
11+
Scenario Outline: 03 visiting the frontpage
12+
Then I should see a search bar "<text>"
13+
Examples:
14+
| text |
15+
| hello |
16+
| bye |
17+
18+
@P1
19+
@failOnPurpose
20+
Scenario: 04 visiting the frontpage
21+
Then This step should fail

integration/cucumber/first.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Then } from '@badeball/cypress-cucumber-preprocessor';
2+
3+
Then('This step should fail', () => {
4+
cy.wrap({ a: 1 }).then(t => {
5+
expect(t).eq(2);
6+
});
7+
});

integration/cucumber/tags.feature

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
@release1.1
2+
@parentSuite("Other")
3+
@epic("Cucumber")
4+
Feature: duckduckgo.com
5+
Background:
6+
Given I visit site
7+
8+
@suite("Suite")
9+
@subSuite("Child")
10+
Scenario: 01.1 should have suite,subsuite
11+
Given I log message "test with tag"
12+
13+
@P1
14+
Scenario: 01 should have tag
15+
Given I log message "test with tag"
16+
17+
@issue("ABC-454")
18+
Scenario: 02 should have issue tag
19+
Given I log message "test with issue"
20+
21+
@issue("ABC-454","issue__link__description")
22+
Scenario: 03 should have issue tag with description
23+
Given I log message "test"
24+
25+
@issue("ABC-123")
26+
@issue("ABC-456")
27+
Scenario: 04 should have several issue tags
28+
Given I log message "test"
29+
30+
@tms("ABC-454")
31+
Scenario: 05 should have tms tag
32+
Given I log message "test with issue"
33+
34+
@tms("ABC-454","issue__link__description")
35+
Scenario: 06 should have tms tag with description
36+
Given I log message "test"
37+
38+
@tms("ABC-123")
39+
@tms("ABC-456")
40+
Scenario: 07 should have several tms tags
41+
Given I log message "test"
42+
43+
@link("https://example.com")
44+
Scenario: 08 should have link tag
45+
Given I log message "test with issue"
46+
47+
@link("https://example.com","example__text")
48+
Scenario: 09 should have link tag with text
49+
Given I log message "test with issue"
50+
51+
@link("https://example.com","example__text","issue")
52+
Scenario: 10 should have link tag with text and 'issue' type
53+
Given I log message "test with issue"
54+
55+
@link("https://example.com","example__text","tms")
56+
Scenario: 11 should have link tag with text and 'tms' type
57+
Given I log message "test with issue"
58+
59+
@feature("Tags")
60+
@story("Special__tags")
61+
Scenario: 12 should have epic/feature/story tag
62+
Given I log message "test with issue"
63+
64+
65+
@severity("minor")
66+
@owner("T__P")
67+
@fullName("Full__name")
68+
@allureId("123")
69+
@language("javascript")
70+
@thread("01")
71+
@lead("AP")
72+
@host("MACBOOK")
73+
@layer("API")
74+
@browser("Chrome")
75+
@device("COMP")
76+
@os("MAC")
77+
@label("path","value")
78+
Scenario: 13 should have meta - severity,owner,etc
79+
Given I log message "test with issue"
80+
81+
Scenario Outline: 14 with examples
82+
Then I should see a search bar "<text>"
83+
@simple
84+
Examples:
85+
| text |
86+
| hello |
87+
88+
@complex
89+
Examples:
90+
| text |
91+
| bye |
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { EventEmitter } from 'events';
2+
3+
export class EventForwarder {
4+
private emitter: EventEmitter;
5+
private task: Cypress.Tasks;
6+
public on: Cypress.PluginEvents;
7+
8+
public constructor() {
9+
this.emitter = new EventEmitter();
10+
this.task = {};
11+
12+
this.on = (action, arg) => {
13+
if (action === 'task') {
14+
Object.assign(this.task, arg);
15+
} else {
16+
this.emitter.on(action, arg as () => void);
17+
}
18+
};
19+
}
20+
21+
public forward(on: Cypress.PluginEvents): void {
22+
for (const event of this.emitter.eventNames()) {
23+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
24+
on(event as any, async (...args: unknown[]) => {
25+
for (const listener of this.emitter.listeners(event)) {
26+
// eslint-disable-next-line no-await-in-loop
27+
await listener(...args);
28+
}
29+
});
30+
}
31+
on('task', this.task);
32+
}
33+
}

integration/plugins/index.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { redirectLogBrowser } from 'cypress-redirect-browser-log/plugins';
99
import { configureAllureAdapterPlugins } from '@src/plugins';
1010
import { startTestServer } from './test-server';
1111
import { Server } from 'http';
12+
import { addCucumberPreprocessorPlugin } from '@badeball/cypress-cucumber-preprocessor';
13+
import { EventForwarder } from './events-forwarder';
1214

1315
/**
1416
* Clear compiled js files from previous runs, otherwise coverage will be messed up
@@ -25,8 +27,10 @@ const clearJsFiles = () => {
2527
const isCoverage = (config: PluginConfigOptions) => {
2628
return process.env[COVERAGE] === 'true' || config.env[COVERAGE] === true;
2729
};
30+
const eventForwarder = new EventForwarder();
2831

29-
export const setupPlugins = (on: PluginEvents, config: PluginConfigOptions) => {
32+
export const setupPlugins = async (cyOn: PluginEvents, config: PluginConfigOptions) => {
33+
const { on } = eventForwarder;
3034
clearJsFiles();
3135
const isCov = isCoverage(config);
3236

@@ -43,7 +47,12 @@ export const setupPlugins = (on: PluginEvents, config: PluginConfigOptions) => {
4347
return browserHandler(browser, opts);
4448
});
4549

50+
cyOn('file:preprocessor', preprocessor(isCov, config));
51+
52+
await addCucumberPreprocessorPlugin(on, config);
53+
4654
const allure = configureAllureAdapterPlugins(on, config);
55+
4756
on('before:run', details => {
4857
allure?.writeEnvironmentInfo({
4958
info: {
@@ -53,13 +62,12 @@ export const setupPlugins = (on: PluginEvents, config: PluginConfigOptions) => {
5362
});
5463
});
5564

56-
on('file:preprocessor', preprocessor(isCov));
57-
5865
console.log('CYPRESS ENV:');
5966
console.log(config.env);
6067

6168
// register grep plugin
6269
pluginGrep(on, config);
70+
6371
let server: Server;
6472

6573
on('task', {
@@ -104,6 +112,8 @@ export const setupPlugins = (on: PluginEvents, config: PluginConfigOptions) => {
104112
console.log(`SCREENSHOT: ${details.path}`);
105113
});
106114

115+
eventForwarder.forward(cyOn);
116+
107117
// It's IMPORTANT to return the config object
108118
// with any changed environment variables
109119
return config;

integration/plugins/ts-preprocessor.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
22
import path from 'path';
33
import type { Configuration } from 'webpack';
4+
import createBundler from '@bahmutov/cypress-esbuild-preprocessor';
45

5-
export const preprocessor = (isCoverage: boolean) => {
6+
// eslint-disable-next-line @typescript-eslint/no-var-requires
7+
const createEsbuildPlugin = require('@badeball/cypress-cucumber-preprocessor/esbuild').createEsbuildPlugin;
8+
9+
export const preprocessor = (isCoverage: boolean, config: Cypress.PluginConfigOptions) => {
610
// eslint-disable-next-line @typescript-eslint/no-var-requires
711
const tsconfigPath = path.resolve(__dirname, '../tsconfig.json');
812

@@ -28,6 +32,14 @@ export const preprocessor = (isCoverage: boolean) => {
2832
],
2933
};
3034

35+
const cucumberBundler = createBundler({
36+
define: {
37+
global: 'window',
38+
},
39+
tsconfig: tsconfigPath,
40+
plugins: [createEsbuildPlugin(config)],
41+
});
42+
3143
const rules = isCoverage ? [coverageRule, tsRule] : [tsRule];
3244

3345
const webpackOptions: Configuration = {
@@ -55,5 +67,9 @@ export const preprocessor = (isCoverage: boolean) => {
5567
webpackOptions,
5668
};
5769

58-
return wp(options);
70+
if (config.env['cucumber'] === 'true') {
71+
console.log('Will Run cucumber tests');
72+
}
73+
74+
return config.env['cucumber'] === 'true' ? cucumberBundler : wp(options);
5975
};

0 commit comments

Comments
 (0)