# todo - Rewrite the scenarios such that they use their own test specific feature files instead of setting up a large suite in the background
Feature: 'with' clause

  The *with* clause specifies filter conditions that will reduce the number of things targeted by the *from* clause. The *with* clause can take one or more blocks that will filter out any object for which the block does not evaluate to true (using 'without' instead of 'with' will have the opposite effect). Alternatively, mappings of specific *from* targets to their respective filtering blocks can be provided. The *with* clause can also take predefined filters (detailed below).

    Sample usage:
      cql_repo.query do
        select name, tags, description_text
        from features
        with { |feature| feature.name =~ /foo/ }
        with tc lt 3
      end

  This clause can be repeated multiple times. The arguments for successive clauses are simply added to the previous arguments.


  The following filters are supported for models that have tags:

  * tags - Filters out models that do not have the exact set of tags provided.
  * tc   - (tag count) Filters out models based on the number of tags that they have.

  The following filters are supported for models that have names:

  * name - Filters out models whose name does not match the name provided. Can be a string or regular expression.

  The following filters are supported for models that have steps:

  * line - Filters out models whose steps do not include the provided step (keywords and blocks are ignored). Can be a string or regular expression.
  * lc   - (line count) Filters out models based on the number of steps that they have.

  The following filters are supported for feature models:

  * sc   - (scenario count) Filters out models based on the number of scenarios that they have.
  * soc  - (scenario outline count) Filters out models based on the number of outlines that they have.
  * ssoc - (scenario and scenario outline count) Filters out models based on the total number of scenarios and outlines that they have.

  For count based filters, the following operators are available:

  * lt   (Less than)
  * lte  (Less than or equals)
  * gt   (Greater than)
  * gte  (Greater than or equals)


  Background: A sample Cucumber suite
    Given a directory "test_directory"
    And a file "test_directory/test_file_1.feature":
      """
      Feature: A test feature

        @tag_1 @tag_2
        Scenario: Test 1
          * some steps

        @special_tag @tag_2
        Scenario: Test 2
          * some other steps

        @a @b @c
        Scenario Outline: Test 3
          * some steps
          * some more steps
        Examples: First examples
          | param |
          | value |
        Examples: Second examples
          | param |
          | value |
      """
    And a file "test_directory/test_file_2.feature":
      """
      Feature: A feature with lots of scenarios

        Scenario: 1
          * different steps

        Scenario: 2
          * different steps

        Scenario: 3
          * different steps
      """
    And a file "test_directory/test_file_3.feature":
      """
      Feature: A feature with lots of outlines

        Scenario Outline: 1
          * different steps
        Examples:
          | param |
          | value |

        Scenario Outline: 2
          * different steps
        Examples:
          | param |
          | value |

        Scenario Outline: 3
          * different steps
        Examples:
          | param |
          | value |
      """
    And a file "test_directory/test_file_4.feature":
      """
      Feature: A feature with a mix of tests

        Scenario: 4
          * different steps

        Scenario Outline: 4
          * different steps
        Examples:
          | param |
          | value |
      """
    And a repository is made from "test_directory"


  Scenario: Using 'with' to limit the objects from which to return attributes
    When the following query is executed:
      """
      select name
      from scenarios
      with lambda { |scenario| scenario.source_line == 8 }
      """
    Then the following values are returned:
      | name   |
      | Test 2 |

  Scenario: Single filter shorthand
    When the following query is executed:
      """
      select name
      from scenarios
      with { |scenario| scenario.tags.include?('@special_tag') }
      """
    Then the result is the same as the result of the following query:
      """
      select name
      from scenarios
      with lambda { |scenario| scenario.tags.include?('@special_tag') }
      """

  Scenario: Using multiple filters
    When the following query is executed:
      """
      select name
      from scenarios, outlines, examples
      with lambda { |element| element.is_a?(CukeModeler::Example) },
           lambda { |element| element.name =~ /Second/ }
      """
    Then the following values are returned:
      | name            |
      | Second examples |

  Scenario: Selectively filtering models
    When the following query is executed:
      """
      select name
      from features, scenarios
      with scenarios => tags('@tag_1','@tag_2')
      """
    Then the following values are returned:
      | name                             |
      | A test feature                   |
      | A feature with lots of scenarios |
      | A feature with lots of outlines  |
      | A feature with a mix of tests    |
      | Test 1                           |

  Scenario: Using the 'with' clause multiple times
    When the following query is executed:
      """
      select name
      from scenarios, outlines, examples
      with { |element| element.is_a?(CukeModeler::Example) }
      with { |element| element.name =~ /Second/ }
      """
    Then the result is the same as the result of the following query:
      """
      select name
      from scenarios, outlines, examples
      with lambda { |element| element.is_a?(CukeModeler::Example) },
           lambda { |element| element.name =~ /Second/ }
      """
    When the following query is executed:
      """
      select name
      from features, scenarios
      with scenarios => lambda { |scenario| scenario.tags.include?('@tag_1') }
      with scenarios => lambda { |scenario| scenario.tags.include?('@tag_2') }
      """
    Then the result is the same as the result of the following query:
      """
      select name
      from features, scenarios
      with({ scenarios => lambda { |scenario| scenario.tags.include?('@tag_1') }},
           { scenarios => lambda { |scenario| scenario.tags.include?('@tag_2') }})
      """

  Scenario: Mixing targeted and blanket filters
    When the following query is executed:
      """
      select name
      from examples, features
      with { |element| element.name != '' }
      with features => lambda { |feature| feature.name =~ /lots/ }
      """
    Then the following values are returned:
      | name                             |
      | First examples                   |
      | Second examples                  |
      | A feature with lots of scenarios |
      | A feature with lots of outlines  |

# todo - break out the predefined filters into another feature file?
  Scenario: Filtering by tags
    When the following query is executed:
      """
      select name
      from scenarios
      with tags '@tag_1', '@tag_2'
      """
    Then the following values are returned:
      | name   |
      | Test 1 |

  Scenario: Filtering by tag count
    When the following query is executed:
      """
      select name
      from scenarios, outlines
      with tc gt 2
      """
    Then the following values are returned:
      | name   |
      | Test 3 |

  Scenario: Filtering by name
    When the following query is executed:
      """
      select name
      from scenarios, outlines
      with name 'Test 3'
      """
    Then the following values are returned:
      | name   |
      | Test 3 |
    When the following query is executed:
      """
      select name
      from scenarios, outlines
      with name /Test [12]/
      """
    Then the following values are returned:
      | name   |
      | Test 1 |
      | Test 2 |

  Scenario: Filtering by line
    When the following query is executed:
      """
      select name
      from scenarios, outlines
      with line 'some steps'
      """
    Then the following values are returned:
      | name   |
      | Test 1 |
      | Test 3 |
    When the following query is executed:
      """
      select name
      from scenarios, outlines
      with line /other/
      """
    Then the following values are returned:
      | name   |
      | Test 2 |

  Scenario: Filtering by line count
    When the following query is executed:
      """
      select name
      from scenarios, outlines
      with lc gt 1
      """
    Then the following values are returned:
      | name   |
      | Test 3 |

  Scenario: Filtering by scenario count
    When the following query is executed:
      """
      select name
      from features
      with sc gt 2
      """
    Then the following values are returned:
      | name                             |
      | A feature with lots of scenarios |

  Scenario: Filtering by outline count
    When the following query is executed:
      """
      select name
      from features
      with soc gt 2
      """
    Then the following values are returned:
      | name                            |
      | A feature with lots of outlines |

  Scenario: Filtering by combined test count
    When the following query is executed:
      """
      select name
      from features
      with ssoc lt 3
      """
    Then the following values are returned:
      | name                          |
      | A feature with a mix of tests |

  @wip
  Scenario: Using the 'lt' count filter

  @wip
  Scenario: Using the 'lte' count filter

  @wip
  Scenario: Using the 'gt' count filter

  @wip
  Scenario: Using the 'gte' count filter

  Scenario: Using 'without' for negation
    When the following query is executed:
      """
      select name
      from scenarios
      without { |scenario| scenario.source_line == 8 }
      """
    Then the result is the same as the result of the following query:
      """
      select name
      from scenarios
      with { |scenario| !(scenario.source_line == 8) }
      """
    When the following query is executed:
      """
      select name
      from features
      without ssoc lt 3
      """
    Then the result is the same as the result of the following query:
      """
      select name
      from features
      with ssoc gt 2
      """