cucumber-tag_expressions 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 19bf8f6a97a5f34c6040463bbae25c1a0bfb6666
4
- data.tar.gz: 954221878d37d62109793038d3d0aa41681a7522
3
+ metadata.gz: 2e5a0bcac8bfcc6f2dc1106c1137dc0680230ffb
4
+ data.tar.gz: ca8e096362f13513df653e454ad22978249acece
5
5
  SHA512:
6
- metadata.gz: 4f1c563583f5a5cd596143818b39c598ef42881905563fe7a2cbb8d6cf14287980d7485a920935d50cf9878d602507100f7d03f52e05861a00c8cd9df9240e6f
7
- data.tar.gz: 702f403529dd3b7b014e29750df7a3cde3242f591cf222dfdda677914b979f4be87251538b27337eafc6ffc2c009e0e765465308ce783b209180c5efffa95bbc
6
+ metadata.gz: 62fed4362ef18098c1640e13c73f1f7a241b29abe6e5aa07156871c1ccf596859158684874cb4e8528937fef53a8b54111a288317da32df57591a0fcc5eae1af
7
+ data.tar.gz: cc6d23a76a4d0ea419c01c451509acbfe798648287e49da4e65b9cff2dfa8618a8a900bfecc73c34795e7d1fb2f411488140ccd35ddda9c0112c4dc46fbbb901
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This script reads NDJSON-encoded Cucumber events from STDIN and prints
4
+ # the location of each pickle (scenario) matching a tag expression. If
5
+ # no tag expression is specified, all pickle locations are printed.
6
+ #
7
+ # Example:
8
+ #
9
+ # gherkin features/*.feature | cucumber-tag-expressions "@foo and not @bar"
10
+ #
11
+ require 'json'
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"../lib"))
13
+ require 'cucumber/tag_expressions'
14
+
15
+ tag_expression_parser = Cucumber::TagExpressions::Parser.new
16
+ tag_expression = ARGV[0] ? tag_expression_parser.parse(ARGV[0]) : nil
17
+
18
+ STDIN.each do |json|
19
+ event = JSON.parse(json)
20
+ if event['type'] == 'pickle'
21
+ tag_names = event['pickle']['tags'].map {|tag| tag['name']}
22
+ if tag_expression.nil? || tag_expression.evaluate(tag_names)
23
+ location = "#{event['uri']}:#{event['pickle']['locations'][0]['line']}"
24
+ puts location
25
+ end
26
+ end
27
+ end
@@ -3,7 +3,7 @@ module Cucumber
3
3
  # Literal expression node
4
4
  class Literal
5
5
  def initialize(value)
6
- @value = value
6
+ @value = value.gsub(/\\\(/, '(').gsub(/\\\)/, ')')
7
7
  end
8
8
 
9
9
  def evaluate(variables)
@@ -11,7 +11,7 @@ module Cucumber
11
11
  end
12
12
 
13
13
  def to_s
14
- @value
14
+ @value.gsub(/\(/, '\\(').gsub(/\)/, '\\)')
15
15
  end
16
16
  end
17
17
 
@@ -6,22 +6,22 @@ module Cucumber
6
6
  class Parser
7
7
  def initialize
8
8
  @expressions = []
9
- @operations = []
10
-
11
- @components = {
12
- 'or' => { type: :operation, precedence: 0, assoc: :left },
13
- 'and' => { type: :operation, precedence: 1, assoc: :left },
14
- 'not' => { type: :operation, precedence: 2, assoc: :right },
15
- ')' => { type: :close_paren, precedence: -1 },
16
- '(' => { type: :open_paren, precedence: 1 }
9
+ @operators = []
10
+
11
+ @operator_types = {
12
+ 'or' => { type: :binary_operator, precedence: 0, assoc: :left },
13
+ 'and' => { type: :binary_operator, precedence: 1, assoc: :left },
14
+ 'not' => { type: :unary_operator, precedence: 2, assoc: :right },
15
+ ')' => { type: :close_paren, precedence: -1 },
16
+ '(' => { type: :open_paren, precedence: 1 }
17
17
  }
18
18
  end
19
19
 
20
20
  def parse(infix_expression)
21
21
  process_tokens!(infix_expression)
22
- while @operations.any?
23
- raise 'Unclosed (' if @operations.last == '('
24
- push_expression(pop(@operations))
22
+ while @operators.any?
23
+ raise 'Syntax error. Unmatched (' if @operators.last == '('
24
+ push_expression(pop(@operators))
25
25
  end
26
26
  expression = pop(@expressions)
27
27
  @expressions.empty? ? expression : raise('Not empty')
@@ -33,34 +33,36 @@ module Cucumber
33
33
  # Helpers
34
34
  #
35
35
  def assoc_of(token, value)
36
- @components[token][:assoc] == value
36
+ @operator_types[token][:assoc] == value
37
37
  end
38
38
 
39
39
  def lower_precedence?(operation)
40
40
  (assoc_of(operation, :left) &&
41
- prec(operation) <= prec(@operations.last)) ||
41
+ precedence(operation) <= precedence(@operators.last)) ||
42
42
  (assoc_of(operation, :right) &&
43
- prec(operation) < prec(@operations.last))
43
+ precedence(operation) < precedence(@operators.last))
44
44
  end
45
45
 
46
- def operation?(token)
47
- @components[token][:type] == :operation
46
+ def operator?(token)
47
+ @operator_types[token][:type] == :unary_operator ||
48
+ @operator_types[token][:type] == :binary_operator
48
49
  end
49
50
 
50
- def prec(token)
51
- @components[token][:precedence]
51
+ def precedence(token)
52
+ @operator_types[token][:precedence]
52
53
  end
53
54
 
54
55
  def tokens(infix_expression)
55
- infix_expression.gsub(/\(/, ' ( ').gsub(/\)/, ' ) ').strip.split(/\s+/)
56
+ infix_expression.gsub(/(?<!\\)\(/, ' ( ').gsub(/(?<!\\)\)/, ' ) ').strip.split(/\s+/)
56
57
  end
57
58
 
58
59
  def process_tokens!(infix_expression)
60
+ expected_token_type = :operand
59
61
  tokens(infix_expression).each do |token|
60
- if @components[token]
61
- send("handle_#{@components[token][:type]}", token)
62
+ if @operator_types[token]
63
+ expected_token_type = send("handle_#{@operator_types[token][:type]}", token, expected_token_type)
62
64
  else
63
- handle_literal(token)
65
+ expected_token_type = handle_literal(token, expected_token_type)
64
66
  end
65
67
  end
66
68
  end
@@ -81,28 +83,48 @@ module Cucumber
81
83
  ############################################################################
82
84
  # Handlers
83
85
  #
84
- def handle_literal(token)
85
- push_expression(token)
86
+ def handle_unary_operator(token, expected_token_type)
87
+ check(expected_token_type, :operand)
88
+ @operators.push(token)
89
+ :operand
86
90
  end
87
91
 
88
- def handle_operation(token)
89
- while @operations.any? && operation?(@operations.last) &&
92
+ def handle_binary_operator(token, expected_token_type)
93
+ check(expected_token_type, :operator)
94
+ while @operators.any? && operator?(@operators.last) &&
90
95
  lower_precedence?(token)
91
- push_expression(pop(@operations))
96
+ push_expression(pop(@operators))
92
97
  end
93
- @operations.push(token)
98
+ @operators.push(token)
99
+ :operand
100
+ end
101
+
102
+ def handle_open_paren(token, expected_token_type)
103
+ check(expected_token_type, :operand)
104
+ @operators.push(token)
105
+ :operand
94
106
  end
95
107
 
96
- def handle_close_paren(_token)
97
- while @operations.any? && @operations.last != '('
98
- push_expression(pop(@operations))
108
+ def handle_close_paren(_token, expected_token_type)
109
+ check(expected_token_type, :operator)
110
+ while @operators.any? && @operators.last != '('
111
+ push_expression(pop(@operators))
99
112
  end
100
- raise 'Unclosed (' if @operations.empty?
101
- pop(@operations) if @operations.last == '('
113
+ raise 'Syntax error. Unmatched )' if @operators.empty?
114
+ pop(@operators) if @operators.last == '('
115
+ :operator
102
116
  end
103
117
 
104
- def handle_open_paren(token)
105
- @operations.push(token)
118
+ def handle_literal(token, expected_token_type)
119
+ check(expected_token_type, :operand)
120
+ push_expression(token)
121
+ :operator
122
+ end
123
+
124
+ def check(expected_token_type, token_type)
125
+ if expected_token_type != token_type
126
+ raise "Syntax error. Expected #{expected_token_type}"
127
+ end
106
128
  end
107
129
 
108
130
  def pop(array, n = 1)
@@ -3,8 +3,8 @@ require 'cucumber/tag_expressions/parser'
3
3
  describe 'Expression node' do
4
4
  shared_examples 'expression node' do |infix_expression, data|
5
5
  data.each do |tags, result|
6
- expression = Cucumber::TagExpressions::Parser.new.parse(infix_expression)
7
6
  it "#{infix_expression.inspect} with variables: #{tags.inspect}'" do
7
+ expression = Cucumber::TagExpressions::Parser.new.parse(infix_expression)
8
8
  expect(expression.evaluate(tags)).to eq(result)
9
9
  end
10
10
  end
@@ -40,4 +40,16 @@ describe 'Expression node' do
40
40
  include_examples 'expression node', infix_expression, data
41
41
  end
42
42
  end
43
+
44
+ describe Cucumber::TagExpressions::Or do
45
+ context '#evaluate' do
46
+ infix_expression = 'x\\(1\\) or (y\\(2\\))'
47
+ data = [[%w(), false],
48
+ [%w(x), false],
49
+ [%w(y), false],
50
+ [%w(x(1)), true],
51
+ [%w(y(2)), true]]
52
+ include_examples 'expression node', infix_expression, data
53
+ end
54
+ end
43
55
  end
@@ -7,15 +7,20 @@ describe Cucumber::TagExpressions::Parser do
7
7
  ['not a', 'not ( a )'],
8
8
  ['( a and b ) or ( c and d )', '( ( a and b ) or ( c and d ) )'],
9
9
  ['not a or b and not c or not d or e and f',
10
- '( ( ( not ( a ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )']
10
+ '( ( ( not ( a ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )'],
11
+ ['not a\\(\\) or b and not c or not d or e and f',
12
+ '( ( ( not ( a\\(\\) ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )']
11
13
  ]
12
14
 
13
15
  error_test_data = [
14
- ['( a and b ))', RuntimeError, 'Unclosed ('],
15
- ['a not ( and )', RuntimeError, 'Empty stack'],
16
- ['( ( a and b )', RuntimeError, 'Unclosed ('],
17
- ['a b', RuntimeError, 'Not empty'],
18
- ['or or', RuntimeError, 'Empty stack']
16
+ ['@a @b or', 'Syntax error. Expected operator'],
17
+ ['@a and (@b not)', 'Syntax error. Expected operator'],
18
+ ['@a and (@b @c) or', 'Syntax error. Expected operator'],
19
+ ['@a and or', 'Syntax error. Expected operand'],
20
+ ['or or', 'Syntax error. Expected operand'],
21
+ ['a b', 'Syntax error. Expected operator'],
22
+ ['( a and b ) )', 'Syntax error. Unmatched )'],
23
+ ['( ( a and b )', 'Syntax error. Unmatched ('],
19
24
  ]
20
25
 
21
26
  context '#parse' do
@@ -29,11 +34,11 @@ describe Cucumber::TagExpressions::Parser do
29
34
  end
30
35
 
31
36
  context 'with error test data' do
32
- error_test_data.each do |infix_expression, error_type, message|
37
+ error_test_data.each do |infix_expression, message|
33
38
  parser = Cucumber::TagExpressions::Parser.new
34
39
  it "raises an error parsing #{infix_expression.inspect}" do
35
40
  expect { parser.parse(infix_expression) }
36
- .to raise_error(error_type, message)
41
+ .to raise_error(RuntimeError, message)
37
42
  end
38
43
  end
39
44
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cucumber-tag_expressions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrea Nodari
8
+ - Aslak Hellesøy
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2017-05-28 00:00:00.000000000 Z
12
+ date: 2017-11-28 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
@@ -44,14 +45,14 @@ dependencies:
44
45
  requirements:
45
46
  - - "~>"
46
47
  - !ruby/object:Gem::Version
47
- version: '3.5'
48
+ version: '3.7'
48
49
  type: :development
49
50
  prerelease: false
50
51
  version_requirements: !ruby/object:Gem::Requirement
51
52
  requirements:
52
53
  - - "~>"
53
54
  - !ruby/object:Gem::Version
54
- version: '3.5'
55
+ version: '3.7'
55
56
  - !ruby/object:Gem::Dependency
56
57
  name: coveralls
57
58
  requirement: !ruby/object:Gem::Requirement
@@ -68,18 +69,14 @@ dependencies:
68
69
  version: '0'
69
70
  description: Cucumber tag expressions for ruby
70
71
  email: cukes@googlegroups.com
71
- executables: []
72
+ executables:
73
+ - cucumber-tag-expressions
72
74
  extensions: []
73
75
  extra_rdoc_files: []
74
76
  files:
75
- - ".rspec"
76
- - ".travis.yml"
77
- - Gemfile
78
77
  - LICENSE
79
- - Makefile
80
78
  - README.md
81
- - Rakefile
82
- - cucumber-tag_expressions.gemspec
79
+ - bin/cucumber-tag-expressions
83
80
  - lib/cucumber/tag_expressions.rb
84
81
  - lib/cucumber/tag_expressions/expressions.rb
85
82
  - lib/cucumber/tag_expressions/parser.rb
@@ -110,8 +107,8 @@ rubyforge_project:
110
107
  rubygems_version: 2.6.8
111
108
  signing_key:
112
109
  specification_version: 4
113
- summary: cucumber-tag_expressions-1.0.1
110
+ summary: cucumber-tag_expressions-1.1.0
114
111
  test_files:
112
+ - spec/parser_spec.rb
115
113
  - spec/coverage.rb
116
114
  - spec/expressions_spec.rb
117
- - spec/parser_spec.rb
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --color
@@ -1,15 +0,0 @@
1
- language: ruby
2
-
3
- rvm:
4
- - 2.4.0
5
- - 2.3.3
6
- - 2.2.6
7
- - jruby-9.1.7.0
8
-
9
- notifications:
10
- webhooks:
11
- urls:
12
- - https://webhooks.gitter.im/e/5373e574ed4e88026838
13
- on_success: change
14
- on_failure: always
15
- on_start: never
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
- source 'https://rubygems.org'
3
- gemspec
data/Makefile DELETED
@@ -1,8 +0,0 @@
1
- default:
2
- bundle install
3
- bundle exec rake
4
- bundle exec rake install
5
- .PHONY: default
6
-
7
- clean:
8
- .PHONY: clean
data/Rakefile DELETED
@@ -1,13 +0,0 @@
1
- # encoding: utf-8
2
- require 'rubygems'
3
- require 'bundler'
4
- Bundler::GemHelper.install_tasks
5
-
6
- $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
7
-
8
- require 'rspec/core/rake_task'
9
- RSpec::Core::RakeTask.new(:spec) do |t|
10
- t.ruby_opts = %w(-r./spec/coverage -w)
11
- end
12
-
13
- task default: ['spec']
@@ -1,26 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- Gem::Specification.new do |s|
3
- s.name = 'cucumber-tag_expressions'
4
- s.version = '1.0.1'
5
- s.authors = ['Andrea Nodari']
6
- s.description = 'Cucumber tag expressions for ruby'
7
- s.summary = "#{s.name}-#{s.version}"
8
- s.email = 'cukes@googlegroups.com'
9
- s.homepage = 'https://docs.cucumber.io/tag-expressions/'
10
- s.platform = Gem::Platform::RUBY
11
- s.license = 'MIT'
12
- s.required_ruby_version = '>= 1.9.3'
13
-
14
- s.add_development_dependency 'bundler'
15
- s.add_development_dependency 'rake', '~> 10.5'
16
- s.add_development_dependency 'rspec', '~> 3.5'
17
-
18
- # For coverage reports
19
- s.add_development_dependency 'coveralls'
20
-
21
- s.rubygems_version = '>= 1.6.1'
22
- s.files = `git ls-files`.split("\n").reject { |path| path =~ /\.gitignore$/ }
23
- s.test_files = `git ls-files -- spec/*`.split("\n")
24
- s.rdoc_options = ['--charset=UTF-8']
25
- s.require_path = 'lib'
26
- end