manyworlds


Namemanyworlds JSON
Version 0.5.0 PyPI version JSON
download
home_pageNone
SummaryOrganize BDD scenarios as hierarchical trees for more concise and expressive feature files
upload_time2023-10-05 17:59:25
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT License Copyright (c) 2023 Ingo Weiss Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords
VCS
bugtrack_url
requirements igraph
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Manyworlds

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)
![PyPI - License](https://img.shields.io/pypi/l/manyworlds)
[![Downloads](https://static.pepy.tech/badge/manyworlds)](https://pepy.tech/project/manyworlds)
![Style Black](https://img.shields.io/badge/style-black-000000)


BDD feature files can be verbose and repetitive. Consider the following four scenarios, represented as a series of actions (A) resulting in an observed outcome (O):

```text
S¹  A¹ → A² → A³: O¹
S²  A¹ → A² → A³ → A⁴: O²
S³  A¹ → A² → A³ → A⁵: O³
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 directed graph:

```text
S¹  A¹ → A² → A³: O¹
S²             ↳ A⁴: O²
S³             ↳ A⁵: O³
S⁴                ↳ A⁶: O⁴
```
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.

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 feature file:

```Cucumber
# 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 | # inactive
When I go to "Users"
Then I see the following users:
    | Name   | Status |
    | Ben    | Active |
    | Alice  | Active |
    | Connie | Active |

    Scenario: Deactivate user
    When I click "Deactivate" for user "Ben"
    And I click "OK"
    Then I see the following users: # I no longer see Ben
        | Name   | Status |
        | Alice  | Active |
        | Connie | Active |

    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 # 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
                        | Name   | Status |
                        | Connie | Active |

                    Scenario: Cancel out of bulk deactivation of users
                    When I click "Cancel"
                    Then I see "2 users selected"
                    And I see the following users:
                        | Name   | Status |
                        | 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 indented.feature --output flat.feature
```
This will print the structure of the scenario tree(s) to the terminal …

```bash
View users
├── Deactivate user
└── Bulk operations:
    └── Select user
        ├── Deselect user
        └── Select multiple users
            ├── Deselect all users
            └── Bulk deactivate users
                ├── Confirm bulk deactivation of users
                └── Cancel out of bulk deactivation of users
```
… and write the following flat feature file:

```Cucumber
# flat.feature

Scenario: View users
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
Then I see the following users:
    | Name   | Status |
    | Ben    | Active |
    | Alice  | Active |
    | Connie | Active |

Scenario: Deactivate user
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I click "Deactivate" for user "Ben"
 And I click "OK"
Then I see the following users:
    | Name   | Status |
    | Alice  | Active |
    | Connie | Active |

Scenario: [Bulk operations] Select user
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
Then I see "1 user selected"

Scenario: [Bulk operations] Deselect user
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
 And I deselect user "Ben"
Then I see "0 users selected"

Scenario: [Bulk operations] Select multiple users
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
 And I select user "Alice"
Then I see "2 users selected"

Scenario: [Bulk operations] Deselect all users
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
 And I select user "Alice"
 And I click "Deselect all"
Then I see "0 users selected"

Scenario: [Bulk operations] Bulk deactivate users
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
 And I select user "Alice"
 And I click "Deactivate all"
Then I see a confirmation dialog

Scenario: [Bulk operations] Confirm bulk deactivation of users
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
 And I select user "Alice"
 And I click "Deactivate all"
 And I click "OK"
Then I see "0 users selected"
 And I see the following users:
    | Name   | Status |
    | Connie | Active |

Scenario: [Bulk operations] Cancel out of bulk deactivation of users
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
 And I select user "Alice"
 And I click "Deactivate all"
 And I click "Cancel"
Then I see "2 users selected"
 And I see the following users:
    | Name   | Status |
    | Ben    | Active |
    | Alice  | Active |
    | Connie | Active |
```
### Flattening Modes

By default, Manyworlds creates one scenario per _node_ in the scenario tree, resulting in scenarios with one set of "whens" followed by one set of "thens" which is generally considered best practice. This is the "strict" mode.

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. However, it does reduce repetition and will run faster:

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

This will write the following "relaxed" flat feature file:

```Cucumber
# flat_relaxed.feature

Scenario: View users > Deactivate user
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
Then I see the following users:
    | Name   | Status |
    | Ben    | Active |
    | Alice  | Active |
    | Connie | Active |
When I click "Deactivate" for user "Ben"
 And I click "OK"
Then I see the following users:
    | Name   | Status |
    | Alice  | Active |
    | Connie | Active |

Scenario: [Bulk operations] Select user > Deselect user
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
Then I see "1 user selected"
When I deselect user "Ben"
Then I see "0 users selected"

Scenario: [Bulk operations] Select multiple users > Deselect all users
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
 And I select user "Alice"
Then I see "2 users selected"
When I click "Deselect all"
Then I see "0 users selected"

Scenario: [Bulk operations] Bulk deactivate users > Confirm bulk deactivation of users
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
 And I select user "Alice"
 And I click "Deactivate all"
Then I see a confirmation dialog
When I click "OK"
Then I see "0 users selected"
 And I see the following users:
    | Name   | Status |
    | Connie | Active |

Scenario: [Bulk operations] Cancel out of bulk deactivation of users
Given the following users:
    | Name   | Status      |
    | Ben    | Active      |
    | Alice  | Active      |
    | Connie | Active      |
    | Dan    | Deactivated |
When I go to "Users"
 And I select user "Ben"
 And I select user "Alice"
 And I click "Deactivate all"
 And I click "Cancel"
Then I see "2 users selected"
 And I see the following users:
    | Name   | Status |
    | Ben    | Active |
    | Alice  | Active |
    | Connie | Active |
```

### File Size

Manyworlds feature files are significantly shorter than the conventional output 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 tree. 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, but their names are used as a "breadcrumb" in the names of their child scenarios. The "Bulk Operations" scenario in the above example is organizational.

### Comments

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 do that:

```python
import manyworlds as mw
mw.Feature.from_file('hierarchical.feature').flatten('flat.feature')
```

### Installation

```bash
pip install manyworlds
```

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

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

1. If a scenario fails in an action, the runner could mark all descendent scenarios as failing without even running them!
2. A runner could use the 'relaxed' mode under the hood to run scenarios optimized for speed, then display test results using the 'strict' mode optimized for informational clarity.
3. A runner could use network analysis tools to decide how to cleave apart the tree for optimal parallelization.

I would think that these might result in significantly faster running (and faster failing) test suites. The display of test results might also be significantly more informative compared to what we have today.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "manyworlds",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": null,
    "author": null,
    "author_email": "Ingo Weiss <ingo@ingoweiss.com>",
    "download_url": "https://files.pythonhosted.org/packages/44/d9/3475ba950dee6bd4910497346207684ea309d5af703aae9f5dc8d07be209/manyworlds-0.5.0.tar.gz",
    "platform": null,
    "description": "# Manyworlds\n\nOrganize BDD scenarios as directed graphs for more concise and expressive feature files.\n\n[![Build](https://github.com/ingoweiss/manyworlds/actions/workflows/build.yml/badge.svg)](https://github.com/ingoweiss/manyworlds/actions/workflows/build.yml)\n![PyPI - Version](https://img.shields.io/pypi/v/manyworlds)\n![PyPI - License](https://img.shields.io/pypi/l/manyworlds)\n[![Downloads](https://static.pepy.tech/badge/manyworlds)](https://pepy.tech/project/manyworlds)\n![Style Black](https://img.shields.io/badge/style-black-000000)\n\n\nBDD feature files can be verbose and repetitive. Consider the following four scenarios, represented as a series of actions (A) resulting in an observed outcome (O):\n\n```text\nS\u00b9  A\u00b9 \u2192 A\u00b2 \u2192 A\u00b3: O\u00b9\nS\u00b2  A\u00b9 \u2192 A\u00b2 \u2192 A\u00b3 \u2192 A\u2074: O\u00b2\nS\u00b3  A\u00b9 \u2192 A\u00b2 \u2192 A\u00b3 \u2192 A\u2075: O\u00b3\nS\u2074  A\u00b9 \u2192 A\u00b2 \u2192 A\u00b3 \u2192 A\u2075 \u2192 A\u2076: O\u2074\n```\nAll four scenarios (S\u00b9\u2013S\u2074) share the first three actions (A\u00b9, A\u00b2, A\u00b3). Scenarios S\u00b3 and S\u2074 share one additional action (A\u2075). This is very repetitive and makes it hard to understand how the scenarios are organized.\n\nNow consider the same scenarios represented as a directed graph:\n\n```text\nS\u00b9  A\u00b9 \u2192 A\u00b2 \u2192 A\u00b3: O\u00b9\nS\u00b2             \u21b3 A\u2074: O\u00b2\nS\u00b3             \u21b3 A\u2075: O\u00b3\nS\u2074                \u21b3 A\u2076: O\u2074\n```\nThe graph format has a few benefits:\n1. 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.\n2. We can now see how scenarios relate to each other. Specifically, we can start thinking in terms of parent and child scenarios.\n3. 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.\n\nSo 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:\n 1. Use indentation in feature files to organize scenarios as scenario trees.\n 2. Expand indented feature files into standard, flat feature files which can be run using any testing tool that understands Gherkin.\n\n## Usage\n\nHere is an example of an indented feature file:\n\n```Cucumber\n# indented.feature\n\nFeature: User Deactivation\n\n    As an administrator\n    I want to deactivate users who leave the company\n    So that only authorized users have access to the system\n\nScenario: View users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated | # inactive\nWhen I go to \"Users\"\nThen I see the following users:\n    | Name   | Status |\n    | Ben    | Active |\n    | Alice  | Active |\n    | Connie | Active |\n\n    Scenario: Deactivate user\n    When I click \"Deactivate\" for user \"Ben\"\n    And I click \"OK\"\n    Then I see the following users: # I no longer see Ben\n        | Name   | Status |\n        | Alice  | Active |\n        | Connie | Active |\n\n    Scenario: Bulk operations # on multiple users\n\n        Scenario: Select user\n        When I select user \"Ben\"\n        Then I see \"1 user selected\"\n\n            Scenario: Deselect user\n            When I deselect user \"Ben\"\n            Then I see \"0 users selected\"\n\n            Scenario: Select multiple users\n            When I select user \"Alice\"\n            Then I see \"2 users selected\"\n\n                Scenario: Deselect all users\n                When I click \"Deselect all\"\n                Then I see \"0 users selected\"\n\n                Scenario: Bulk deactivate users\n                When I click \"Deactivate all\"\n                Then I see a confirmation dialog\n\n                    Scenario: Confirm bulk deactivation of users # by clicking \"OK\"\n                    When I click \"OK\"\n                    Then I see \"0 users selected\"\n                    And I see the following users: # I no longer see Ben or Alice\n                        | Name   | Status |\n                        | Connie | Active |\n\n                    Scenario: Cancel out of bulk deactivation of users\n                    When I click \"Cancel\"\n                    Then I see \"2 users selected\"\n                    And I see the following users:\n                        | Name   | Status |\n                        | Ben    | Active | # still there\n                        | Alice  | Active | # still there\n                        | Connie | Active |\n\n                    # TODO: add a scenario for bulk activation of users\n```\n\nNow let's use Manyworlds to flatten it:\n\n```bash\npython -m manyworlds --input indented.feature --output flat.feature\n```\nThis will print the structure of the scenario tree(s) to the terminal \u2026\n\n```bash\nView users\n\u251c\u2500\u2500 Deactivate user\n\u2514\u2500\u2500 Bulk operations:\n    \u2514\u2500\u2500 Select user\n        \u251c\u2500\u2500 Deselect user\n        \u2514\u2500\u2500 Select multiple users\n            \u251c\u2500\u2500 Deselect all users\n            \u2514\u2500\u2500 Bulk deactivate users\n                \u251c\u2500\u2500 Confirm bulk deactivation of users\n                \u2514\u2500\u2500 Cancel out of bulk deactivation of users\n```\n\u2026 and write the following flat feature file:\n\n```Cucumber\n# flat.feature\n\nScenario: View users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\nThen I see the following users:\n    | Name   | Status |\n    | Ben    | Active |\n    | Alice  | Active |\n    | Connie | Active |\n\nScenario: Deactivate user\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I click \"Deactivate\" for user \"Ben\"\n And I click \"OK\"\nThen I see the following users:\n    | Name   | Status |\n    | Alice  | Active |\n    | Connie | Active |\n\nScenario: [Bulk operations] Select user\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\nThen I see \"1 user selected\"\n\nScenario: [Bulk operations] Deselect user\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\n And I deselect user \"Ben\"\nThen I see \"0 users selected\"\n\nScenario: [Bulk operations] Select multiple users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\n And I select user \"Alice\"\nThen I see \"2 users selected\"\n\nScenario: [Bulk operations] Deselect all users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\n And I select user \"Alice\"\n And I click \"Deselect all\"\nThen I see \"0 users selected\"\n\nScenario: [Bulk operations] Bulk deactivate users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\n And I select user \"Alice\"\n And I click \"Deactivate all\"\nThen I see a confirmation dialog\n\nScenario: [Bulk operations] Confirm bulk deactivation of users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\n And I select user \"Alice\"\n And I click \"Deactivate all\"\n And I click \"OK\"\nThen I see \"0 users selected\"\n And I see the following users:\n    | Name   | Status |\n    | Connie | Active |\n\nScenario: [Bulk operations] Cancel out of bulk deactivation of users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\n And I select user \"Alice\"\n And I click \"Deactivate all\"\n And I click \"Cancel\"\nThen I see \"2 users selected\"\n And I see the following users:\n    | Name   | Status |\n    | Ben    | Active |\n    | Alice  | Active |\n    | Connie | Active |\n```\n### Flattening Modes\n\nBy default, Manyworlds creates one scenario per _node_ in the scenario tree, resulting in scenarios with one set of \"whens\" followed by one set of \"thens\" which is generally considered best practice. This is the \"strict\" mode.\n\nManyworlds 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. However, it does reduce repetition and will run faster:\n\n```bash\npython -m manyworlds --input indented.feature --output flat_relaxed.feature --mode relaxed\n```\n\nThis will write the following \"relaxed\" flat feature file:\n\n```Cucumber\n# flat_relaxed.feature\n\nScenario: View users > Deactivate user\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\nThen I see the following users:\n    | Name   | Status |\n    | Ben    | Active |\n    | Alice  | Active |\n    | Connie | Active |\nWhen I click \"Deactivate\" for user \"Ben\"\n And I click \"OK\"\nThen I see the following users:\n    | Name   | Status |\n    | Alice  | Active |\n    | Connie | Active |\n\nScenario: [Bulk operations] Select user > Deselect user\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\nThen I see \"1 user selected\"\nWhen I deselect user \"Ben\"\nThen I see \"0 users selected\"\n\nScenario: [Bulk operations] Select multiple users > Deselect all users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\n And I select user \"Alice\"\nThen I see \"2 users selected\"\nWhen I click \"Deselect all\"\nThen I see \"0 users selected\"\n\nScenario: [Bulk operations] Bulk deactivate users > Confirm bulk deactivation of users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\n And I select user \"Alice\"\n And I click \"Deactivate all\"\nThen I see a confirmation dialog\nWhen I click \"OK\"\nThen I see \"0 users selected\"\n And I see the following users:\n    | Name   | Status |\n    | Connie | Active |\n\nScenario: [Bulk operations] Cancel out of bulk deactivation of users\nGiven the following users:\n    | Name   | Status      |\n    | Ben    | Active      |\n    | Alice  | Active      |\n    | Connie | Active      |\n    | Dan    | Deactivated |\nWhen I go to \"Users\"\n And I select user \"Ben\"\n And I select user \"Alice\"\n And I click \"Deactivate all\"\n And I click \"Cancel\"\nThen I see \"2 users selected\"\n And I see the following users:\n    | Name   | Status |\n    | Ben    | Active |\n    | Alice  | Active |\n    | Connie | Active |\n```\n\n### File Size\n\nManyworlds feature files are significantly shorter than the conventional output 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 tree. A factor of around 3 is not uncommon.\n\n### Organizational Scenarios\n\nScenarios 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, but their names are used as a \"breadcrumb\" in the names of their child scenarios. The \"Bulk Operations\" scenario in the above example is organizational.\n\n### Comments\n\nYou 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.\n\n### Using the Feature Class Directly\n\nIf you want to use Manyworlds in your code rather than using the cli, here's how to do that:\n\n```python\nimport manyworlds as mw\nmw.Feature.from_file('hierarchical.feature').flatten('flat.feature')\n```\n\n### Installation\n\n```bash\npip install manyworlds\n```\n\n### What If Test Runners Could Run Scenario Graphs Directly?\n\nI believe this is where it could get really interesting. A few examples:\n\n1. If a scenario fails in an action, the runner could mark all descendent scenarios as failing without even running them!\n2. A runner could use the 'relaxed' mode under the hood to run scenarios optimized for speed, then display test results using the 'strict' mode optimized for informational clarity.\n3. A runner could use network analysis tools to decide how to cleave apart the tree for optimal parallelization.\n\nI would think that these might result in significantly faster running (and faster failing) test suites. The display of test results might also be significantly more informative compared to what we have today.\n",
    "bugtrack_url": null,
    "license": "MIT License\n        \n        Copyright (c) 2023 Ingo Weiss\n        \n        Permission is hereby granted, free of charge, to any person obtaining a copy\n        of this software and associated documentation files (the \"Software\"), to deal\n        in the Software without restriction, including without limitation the rights\n        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n        copies of the Software, and to permit persons to whom the Software is\n        furnished to do so, subject to the following conditions:\n        \n        The above copyright notice and this permission notice shall be included in all\n        copies or substantial portions of the Software.\n        \n        THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n        SOFTWARE.",
    "summary": "Organize BDD scenarios as hierarchical trees for more concise and expressive feature files",
    "version": "0.5.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/ingoweiss/manyworlds/issues",
        "Changelog": "https://github.com/ingoweiss/manyworlds/blob/master/CHANGELOG.md",
        "Documentation": "https://manyworlds.ingoweiss.com/",
        "Homepage": "https://github.com/ingoweiss/manyworlds",
        "Repository": "https://github.com/ingoweiss/manyworlds.git"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "29c26dd69e3efefad8fe7aeb07ae05f24fb41df1405de1ff7a4dc29f402189b1",
                "md5": "8cfc06f541d870366820208268b734b7",
                "sha256": "5bd0cbc15ab324d7de45f69a13c71bb58672beb515d82f23634e73e348c919f1"
            },
            "downloads": -1,
            "filename": "manyworlds-0.5.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "8cfc06f541d870366820208268b734b7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 16786,
            "upload_time": "2023-10-05T17:59:22",
            "upload_time_iso_8601": "2023-10-05T17:59:22.931534Z",
            "url": "https://files.pythonhosted.org/packages/29/c2/6dd69e3efefad8fe7aeb07ae05f24fb41df1405de1ff7a4dc29f402189b1/manyworlds-0.5.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "44d93475ba950dee6bd4910497346207684ea309d5af703aae9f5dc8d07be209",
                "md5": "c94ff9e2b71c343a91b6a69a52db01a1",
                "sha256": "686b58738da699dbaab0014e9b1acdc5cfba68eaa3d40827cef1e446760ab79d"
            },
            "downloads": -1,
            "filename": "manyworlds-0.5.0.tar.gz",
            "has_sig": false,
            "md5_digest": "c94ff9e2b71c343a91b6a69a52db01a1",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 19544,
            "upload_time": "2023-10-05T17:59:25",
            "upload_time_iso_8601": "2023-10-05T17:59:25.012267Z",
            "url": "https://files.pythonhosted.org/packages/44/d9/3475ba950dee6bd4910497346207684ea309d5af703aae9f5dc8d07be209/manyworlds-0.5.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-10-05 17:59:25",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ingoweiss",
    "github_project": "manyworlds",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "igraph",
            "specs": []
        }
    ],
    "lcname": "manyworlds"
}
        
Elapsed time: 0.12499s