Skip to content

Commit

Permalink
Merge pull request #37 from ingoweiss/feature/36-add-support-for-comm…
Browse files Browse the repository at this point in the history
…ents-in-scenario-lines

Feature/36 add support for comments in scenario lines
  • Loading branch information
ingoweiss authored Oct 5, 2023
2 parents a9521b2 + 3ec048b commit 12ee999
Show file tree
Hide file tree
Showing 13 changed files with 303 additions and 81 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## [0.5.0] - 2023-10-05

### Added

- Support for in-line comments on scenario lines
- Support for separate line comments (ignored for output)

### Changed

- Write step comments in-line instead of on following line

## [0.4.1] - 2023-09-08

### Fixed
Expand Down
52 changes: 30 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Manyworlds

Organize BDD scenarios as hierarchical trees for more concise and expressive feature files.
Organize BDD scenarios as directed graphs for more concise and expressive feature files.

[![Build](https://github.com/ingoweiss/manyworlds/actions/workflows/build.yml/badge.svg)](https://github.com/ingoweiss/manyworlds/actions/workflows/build.yml)
![PyPI - Version](https://img.shields.io/pypi/v/manyworlds)
Expand All @@ -19,37 +19,43 @@ S⁴ A¹ → A² → A³ → A⁵ → A⁶: O⁴
```
All four scenarios (S¹–S⁴) share the first three actions (A¹, A², A³). Scenarios S³ and S⁴ share one additional action (A⁵). This is very repetitive and makes it hard to understand how the scenarios are organized.

Now consider the same scenarios represented as a tree:
Now consider the same scenarios represented as a directed graph:

```text
S¹ A¹ → A² → A³: O¹
S² ↳ A⁴: O²
S³ ↳ A⁵: O³
S⁴ ↳ A⁶: O⁴
```
The tree structure has a few benefits:
1. Many actions are now implied by their scenario's position in the tree and no longer need to be stated which eliminates repetition and noise.
The graph format has a few benefits:
1. Many actions are now implied by their scenario's position in the graph and no longer need to be stated which eliminates repetition and noise.
2. We can now see how scenarios relate to each other. Specifically, we can start thinking in terms of parent and child scenarios.
3. We now have what amounts to a decision tree of possible paths that a user can take through the app. This makes it easier to notice missing scenarios.

What if we could use _indentation_ in feature files to reap these benefits? That is the simple idea behind Manyworlds. With Manyworlds we can:
1. Use indentation in feature files to organize scenarios hierarchically.
2. Expand the hierarchical feature files into standard, flat feature files which can be run using any testing tool that understands Gherkin.
So how could we start experimenting with scenario graphs using existing tools? What if we could simply use _indentation_ in feature files? This would limit us to one type of graph (trees/forests), but maybe that would cover the vast majority of use cases? That is the idea behind Manyworlds. With Manyworlds we can:
1. Use indentation in feature files to organize scenarios as scenario trees.
2. Expand indented feature files into standard, flat feature files which can be run using any testing tool that understands Gherkin.

## Usage

Here is an example of an indented, hierarchical feature file:
Here is an example of an indented feature file:

```Cucumber
# hierarchical.feature
# indented.feature
Feature: User Deactivation
As an administrator
I want to deactivate users who leave the company
So that only authorized users have access to the system
Scenario: View users
Given the following users:
| Name | Status |
| Ben | Active |
| Alice | Active |
| Connie | Active |
| Dan | Deactivated |
| Dan | Deactivated | # inactive
When I go to "Users"
Then I see the following users:
| Name | Status |
Expand All @@ -65,29 +71,29 @@ Then I see the following users:
| Alice | Active |
| Connie | Active |
Scenario: Bulk operations
Scenario: Bulk operations # on multiple users
Scenario: Select user
When I select user "Ben"
Then I see "1 user selected"
Scenario: Deselect user
When I deselect user "Ben"
Then I see "0 users selected"
Scenario: Select multiple users
When I select user "Alice"
Then I see "2 users selected"
Scenario: Deselect all users
When I click "Deselect all"
Then I see "0 users selected"
Scenario: Bulk deactivate users
When I click "Deactivate all"
Then I see a confirmation dialog
Scenario: Confirm bulk deactivation of users
Scenario: Confirm bulk deactivation of users # by clicking "OK"
When I click "OK"
Then I see "0 users selected"
And I see the following users: # I no longer see Ben or Alice
Expand All @@ -102,12 +108,14 @@ Then I see the following users:
| Ben | Active | # still there
| Alice | Active | # still there
| Connie | Active |
# TODO: add a scenario for bulk activation of users
```

Now let's use Manyworlds to flatten it:

```bash
python -m manyworlds --input hierarchical.feature --output flat.feature
python -m manyworlds --input indented.feature --output flat.feature
```
This will print the structure of the scenario tree(s) to the terminal …

Expand Down Expand Up @@ -261,7 +269,7 @@ By default, Manyworlds creates one scenario per _node_ in the scenario tree, res
Manyworlds also supports a "relaxed" mode that creates one scenario per _leaf node_ in the scenario tree, resulting in fewer scenarios that may have multiple consecutive "when/then" pairs which is widely considered an anti-pattern. For starters, it makes it hard to name scenarios well. It does, however, reduce repetition and will therefore run faster:

```bash
python -m manyworlds --input hierarchical.feature --output flat_relaxed.feature --mode relaxed
python -m manyworlds --input indented.feature --output flat_relaxed.feature --mode relaxed
```

This will write the following "relaxed" flat feature file:
Expand Down Expand Up @@ -356,19 +364,19 @@ Then I see "2 users selected"

### File Size

Manyworlds feature files are significantly shorter than conventional feature files, which is another reason I why find them easier to maintain. The exact factor is a function mostly of the depth of the scenario hierarchy. A factor of around 3 is not uncommon.
Manyworlds feature files are significantly shorter than conventional feature files, which is another reason I why find them easier to maintain. The exact factor is a function mostly of the depth of the scenario trees. A factor of around 3 is not uncommon.

### Organizational Scenarios

Scenarios without assertions are considered "organizational" and are used to group child scenarios only. In output feature files, organizationasl scenarios will not appear as their own scenarios. Instead, their names are used to prefix the names of their child scenarios. The "Bulk Operations" scenario in the above example is organizational.

### Comments

While I was at it, I thought I'd fix one of my pet peeves about Gherkin - comments. The [Gherkin specification](https://cucumber.io/docs/gherkin/reference) allows comments on separate lines only. In Manyworlds files, you can add inline comments to just about anything: Steps, scenarios and even data table rows!
You can add inline comments to just about anything in Manyworlds input feature files: Steps, scenarios and even data table rows! This is in contrast to the [Gherkin specification](https://cucumber.io/docs/gherkin/reference) which only allows comments on separate lines. By default, comment output is turned off when writing Manyworlds output files so they validate as Gherkin. You can turn it on by using the '--write-comments' flag.

### Using the Feature Class Directly

If you want to use Manyworlds in your code rather than using the cli, here's how to to do that:
If you want to use Manyworlds in your code rather than using the cli, here's how to do that:

```python
import manyworlds as mw
Expand All @@ -381,7 +389,7 @@ mw.Feature.from_file('hierarchical.feature').flatten('flat.feature')
pip install manyworlds
```

### What If Test Runners Could Run Scenario Trees Directly?
### What If Test Runners Could Run Scenario Graphs Directly?

I believe this is where it could get really interesting. A few examples:

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = "Ingo Weiss"

# The full version, including alpha/beta/rc tags
release = "0.4.1"
release = "0.5.0"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
2 changes: 1 addition & 1 deletion manyworlds/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .feature import Feature

__all__ = ["Feature"]
__version__ = "0.4.1"
__version__ = "0.5.0"
8 changes: 6 additions & 2 deletions manyworlds/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ def main():
help="flattening mode",
)
parser.add_argument(
"--comments", "-c", default=False, action="store_true", help="output comments"
"--write-comments",
"-c",
default=False,
action="store_true",
help="output comments",
)
args = parser.parse_args()

Expand All @@ -29,7 +33,7 @@ def main():

# write flat feature file:
if args.output:
feature.flatten(args.output, mode=args.mode, comments=args.comments)
feature.flatten(args.output, mode=args.mode, write_comments=args.write_comments)


def print_feature_outline(feature: mw.Feature) -> None:
Expand Down
15 changes: 13 additions & 2 deletions manyworlds/data_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,20 @@ class DataTable:
"""A Gherkin data table"""

TABLE_ROW_PATTERN = re.compile(
r"^(?P<table_row>\| ([^|]* +\|)+)( # (?P<comment>.+))?"
r"""
^ # start of line
(?P<table_row>\| (?:[^|]+[ ]+\|)+) # pipe-delimited list of values
(?:[ ]+\#[ ](?P<comment>.+))? # optional comment
$ # end of line
""",
re.VERBOSE,
)
"""Pipe-delimited list of values, followed by an optional comment"""
"""
re.Pattern
Pattern describing a Gherkin data table row
followed by an optional comment
"""

header_row: DataTableRow
rows: List[DataTableRow]
Expand Down
Loading

0 comments on commit 12ee999

Please sign in to comment.