Skip to content
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
27 changes: 14 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,24 @@ through the repository `features/` directory.

Implemented pieces include:

- feature discovery from a directory or a single `.feature` file
- optional support script loading with `--load`
- step definition loading from `step_definitions/*.sh` and `*.bash`
- step matching with `{token}` placeholders
- `Background`, `Scenario`, `Given` / `When` / `Then`, `And` / `But`, and `*`
- doc strings via Gherkin-style `"""` blocks exposed as `DOC_STRING`
- data tables exposed as `TABLE_HEADER` and `TABLE_ROWS` arrays
- `Scenario Outline` expansion from one `Examples` table
- colored terminal output and scenario summary
- feature discovery, validation, filtering, and scenario selection
- English Gherkin features, rules, backgrounds, scenarios, and outlines
- steps, tags, comments, doc strings, and data tables
- step definitions with named placeholders and lifecycle hooks
- optional support script loading
- fail-fast execution, deferred cleanup, and scenario summaries

### Gherkin Feature Support

| Feature | Status |
|:------------------------------|:------------|
| `Feature` | Supported |
| Feature description text | Supported |
| `Scenario` | Supported |
| `Rule` | Supported |
| `Background` | Supported |
| `Scenario` | Supported |
| `Scenario Outline` | Supported |
| `Examples` | Supported |
| `Given`, `When`, `Then` | Supported |
| `And` , `But` | Supported |
| `*` step keyword | Supported |
Expand All @@ -89,9 +89,10 @@ Implemented pieces include:
| `Before`, `After` hooks | Supported |
| `BeforeAll`, `AfterAll` hooks | Supported |
| Data tables | Supported |
| `Scenario Outline` | Supported |
| `Examples` | Supported |
| `Rule` | Unsupported |

Shellkin intentionally implements a compact English Gherkin dialect. Keyword
aliases and localization, multiple Examples blocks, tags on Examples blocks,
and escaped data-table cells are not currently supported.

Tags can be selected with `--tag` / `-t` and skipped with `--exclude-tag` / `-x`.
`@Before` and `@After` hooks are declared in step definition files and may be
Expand Down
37 changes: 33 additions & 4 deletions doc/shellkin-feature.5
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Shellkin currently supports these Gherkin keywords:
.IP \(bu 2
\f[B]Background\f[R]
.IP \(bu 2
\f[B]Rule\f[R]
.IP \(bu 2
\f[B]Scenario\f[R]
.IP \(bu 2
\f[B]Scenario Outline\f[R], \f[B]Examples\f[R]
Expand Down Expand Up @@ -55,6 +57,37 @@ in the feature.
Background:
Given I am in a temp directory
.EE
.SS Rule
Use \f[B]Rule:\f[R] to group scenarios that illustrate one business
rule.
Tags on a Rule are inherited by every scenario in that Rule.
Free\-form description text may appear below the Rule header.
.IP
.EX
\(atadmin
Rule: Administrators can manage users
Administrators have access to user management.

Scenario: Deactivate a user
When I deactivate user \(aqAlice\(aq
Then user \(aqAlice\(aq should be inactive
.EE
.PP
A Rule may have one \f[B]Background:\f[R] before its first scenario.
Feature Background steps run first, followed by Rule Background steps.
.IP
.EX
Background:
Given the current user is \(aqDana\(aq

Rule: Administrators can manage users
Background:
Given the current role is \(aqadmin\(aq

Scenario: Deactivate a user
When I deactivate user \(aqAlice\(aq
Then user \(aqAlice\(aq should be inactive
.EE
.SS Scenario Outline
Use \f[B]Scenario Outline:\f[R] with one \f[B]Examples:\f[R] table to
run the same scenario with several values.
Expand Down Expand Up @@ -169,10 +202,6 @@ Given these users exist
All rows must contain the same number of cells.
Cell values are trimmed.
Escaped pipe characters in cells are not currently supported.
.SH UNSUPPORTED CONSTRUCTS
The following common Gherkin constructs are not currently supported:
.IP \(bu 2
\f[B]Rule\f[R]
.SH EXAMPLE
.IP
.EX
Expand Down
41 changes: 34 additions & 7 deletions doc/shellkin-feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Shellkin currently supports these Gherkin keywords:

- **Feature**
- **Background**
- **Rule**
- **Scenario**
- **Scenario Outline**, **Examples**
- **Given**, **When**, **Then**
Expand Down Expand Up @@ -73,6 +74,39 @@ Background:
Given I am in a temp directory
```

Rule
--------------------------------------------------

Use **Rule:** to group scenarios that illustrate one business rule. Tags on a
Rule are inherited by every scenario in that Rule. Free-form description text
may appear below the Rule header.

```gherkin
@admin
Rule: Administrators can manage users
Administrators have access to user management.

Scenario: Deactivate a user
When I deactivate user 'Alice'
Then user 'Alice' should be inactive
```

A Rule may have one **Background:** before its first scenario. Feature
Background steps run first, followed by Rule Background steps.

```gherkin
Background:
Given the current user is 'Dana'

Rule: Administrators can manage users
Background:
Given the current role is 'admin'

Scenario: Deactivate a user
When I deactivate user 'Alice'
Then user 'Alice' should be inactive
```

Scenario Outline
--------------------------------------------------

Expand Down Expand Up @@ -196,13 +230,6 @@ Given these users exist
All rows must contain the same number of cells. Cell values are trimmed.
Escaped pipe characters in cells are not currently supported.

UNSUPPORTED CONSTRUCTS
==================================================

The following common Gherkin constructs are not currently supported:

- **Rule**

EXAMPLE
==================================================

Expand Down
30 changes: 30 additions & 0 deletions features/fixtures/rules/rules.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@access
Feature: rules
Group examples by business rule

Background:
Given the current user is 'Dana'

@elevated
Rule: Elevated roles can access protected areas
These roles have broader access.

Background:
Given the current role is 'admin'

Scenario Outline: Accessing <area>
Then the user should have access to '<area>'

Examples:
| area |
| settings |
| reports |

@standard
Rule: Standard roles cannot access protected areas

Background:
Given the current role is 'member'

Scenario: Accessing settings
Then the user should not have access to 'settings'
15 changes: 15 additions & 0 deletions features/fixtures/rules/step_definitions/core.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@Given the current user is '{name}'
CURRENT_USER=$name

@Given the current role is '{role}'
CURRENT_ROLE=$role

@Then the user should have access to '{area}'
[[ $CURRENT_USER == Dana ]]
[[ $CURRENT_ROLE == admin ]]
[[ $area == settings || $area == reports ]]

@Then the user should not have access to '{area}'
[[ $CURRENT_USER == Dana ]]
[[ $CURRENT_ROLE == member ]]
[[ $area == settings ]]
16 changes: 16 additions & 0 deletions features/test.feature
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ Scenario: Selecting one expanded scenario outline row
And the output should include '1 scenario, 0 failing'
And the exit code should mean success

Scenario: Running scenarios grouped by Rules
When I run 'shellkin features/fixtures/rules'
Then the output should include 'Feature: rules'
And the output should include 'Scenario 1: Accessing settings'
And the output should include 'Scenario 2: Accessing reports'
And the output should include 'Scenario 3: Accessing settings'
And the output should include '3 scenarios, 0 failing'
And the exit code should mean success

Scenario: Filtering scenarios by a Rule tag
When I run 'shellkin --tag @standard features/fixtures/rules'
Then the output should include 'Scenario 3: Accessing settings'
And the output should not include 'Scenario 1: Accessing settings'
And the output should include '1 scenario, 0 failing'
And the exit code should mean success

Scenario: Running a failing test
When I run 'shellkin features/fixtures/selective/failing.feature'
Then the output should include 'Feature: two'
Expand Down
84 changes: 81 additions & 3 deletions shellkin
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,13 @@ feature_parse() {
local line_number=0
local section=
local feature_seen=0
local feature_background_seen=0
local feature_scenario_seen=0
local rule_seen=0
local rule_background_seen=0
local rule_scenario_seen=0
local rule_line=0
local rule_name=
local scenario_seen=0
local scenario_outline=0
local scenario_line=0
Expand All @@ -834,11 +841,14 @@ feature_parse() {
local pending_tags_line=0
local pending_tags_context=
local -a feature_tags=()
local -a rule_tags=()
local -a pending_tags=()
local -a parsed_tags=()
local -a scenario_tags=()
local -a background_steps=()
local -a background_step_lines=()
local -a feature_background_steps=()
local -a feature_background_step_lines=()
local -a scenario_steps=()
local -a scenario_step_lines=()
local -a example_rows=()
Expand Down Expand Up @@ -934,8 +944,45 @@ feature_parse() {
fi
continue
;;
rule)
if ((feature_seen == 0)); then
feature_validation_set_error "$line_number" "Rule must appear after Feature" "$(trim "$line")"
failed=1
break
fi
if ((scenario_seen != 0)); then
feature__parsed_scenario_finish "$scenario_outline" "$scenario_line" "$examples_line" feature_tags scenario_tags background_steps background_step_lines scenario_steps scenario_step_lines example_rows example_row_lines || {
failed=1
break
}
scenario_seen=0
fi
if ((rule_seen != 0 && rule_scenario_seen == 0)); then
feature_validation_set_error "$rule_line" "Rule must contain at least one Scenario" "Rule: $rule_name"
failed=1
break
fi
if ((rule_seen == 0)); then
feature_background_steps=("${background_steps[@]}")
feature_background_step_lines=("${background_step_lines[@]}")
fi
background_steps=("${feature_background_steps[@]}")
background_step_lines=("${feature_background_step_lines[@]}")
rule_seen=1
rule_background_seen=0
rule_scenario_seen=0
rule_line=$line_number
rule_name=$FEATURE_LINE_NAME
rule_tags=("${pending_tags[@]}")
pending_tags=()
pending_tags_line=0
pending_tags_context=
section=rule
in_description=1
continue
;;
background)
if ((feature_seen == 0 || scenario_seen != 0)); then
if ((feature_seen == 0)); then
feature_validation_set_error "$line_number" "Background must appear after Feature and before the first Scenario" "$(trim "$line")"
failed=1
break
Expand All @@ -945,6 +992,21 @@ feature_parse() {
failed=1
break
fi
if ((rule_seen != 0)); then
if ((scenario_seen != 0 || rule_scenario_seen != 0 || rule_background_seen != 0)); then
feature_validation_set_error "$line_number" "Rule Background must appear before the first Scenario and only once" "$(trim "$line")"
failed=1
break
fi
rule_background_seen=1
else
if ((scenario_seen != 0 || feature_scenario_seen != 0 || feature_background_seen != 0)); then
feature_validation_set_error "$line_number" "Background must appear after Feature and before the first Scenario" "$(trim "$line")"
failed=1
break
fi
feature_background_seen=1
fi
section=background
in_description=0
continue
Expand All @@ -970,10 +1032,15 @@ feature_parse() {
scenario_line=$line_number
examples_seen=0
examples_line=0
if ((rule_seen != 0)); then
rule_scenario_seen=1
else
feature_scenario_seen=1
fi
section=scenario
in_description=0
FEATURE_SCENARIO_NAME=$FEATURE_LINE_NAME
scenario_tags=("${pending_tags[@]}")
scenario_tags=("${rule_tags[@]}" "${pending_tags[@]}")
pending_tags=()
pending_tags_line=0
pending_tags_context=
Expand Down Expand Up @@ -1034,7 +1101,7 @@ feature_parse() {
continue
;;
other)
if [[ $section == feature && $in_description == 1 ]]; then
if [[ $section == feature || $section == rule ]] && ((in_description == 1)); then
continue
fi
feature_validation_set_error "$line_number" "invalid feature syntax" "$FEATURE_LINE_NAME"
Expand All @@ -1059,6 +1126,11 @@ feature_parse() {
feature__parsed_scenario_finish "$scenario_outline" "$scenario_line" "$examples_line" feature_tags scenario_tags background_steps background_step_lines scenario_steps scenario_step_lines example_rows example_row_lines || failed=1
fi

if ((failed == 0 && rule_seen != 0 && rule_scenario_seen == 0)); then
feature_validation_set_error "$rule_line" "Rule must contain at least one Scenario" "Rule: $rule_name"
failed=1
fi

return "$failed"
}

Expand Down Expand Up @@ -1232,6 +1304,12 @@ feature_line_parse() {
return 0
fi

if [[ $line =~ ^Rule:[[:space:]]*(.*)$ ]]; then
FEATURE_LINE_KIND=rule
FEATURE_LINE_NAME=${BASH_REMATCH[1]}
return 0
fi

if [[ $line =~ ^Scenario:[[:space:]]*(.*)$ ]]; then
FEATURE_LINE_KIND=scenario
FEATURE_LINE_NAME=${BASH_REMATCH[1]}
Expand Down
Loading
Loading