├── .gitignore ├── Commands └── Switch to New Canonoical Fork.tmCommand ├── MIT-LICENSE.txt ├── Preferences ├── Comments.tmPreferences ├── Cucumber Plain Text Feature Completions.tmPreferences ├── Symbol list: Scenario.tmPreferences ├── Symbol list: Steps with String.tmPreferences └── Symbol list: Steps.tmPreferences ├── README.textile ├── Support ├── Rakefile ├── bundle_tasks │ └── syntax.rake ├── fixtures │ ├── example_failing_spec.rb │ ├── example_passing_spec.rb │ └── features │ │ ├── additional_basic.feature │ │ ├── basic.feature │ │ ├── feature1 │ │ ├── foo.feature │ │ └── step_definitions │ │ │ └── foo_steps.rb │ │ ├── non_standard.feature │ │ ├── non_standard_dir │ │ ├── runners │ │ │ └── non_standard.rb │ │ └── step_definitions │ │ │ └── non_standard_steps.rb │ │ └── step_definitions │ │ ├── additional_basic_steps.rb │ │ ├── basic_steps.rb │ │ ├── global_steps.rb │ │ └── unconventional_steps.rb ├── lib │ └── cucumber │ │ ├── mate.rb │ │ └── mate │ │ ├── feature_helper.rb │ │ ├── files.rb │ │ ├── files │ │ ├── base.rb │ │ ├── feature_file.rb │ │ ├── step_detector.rb │ │ └── steps_file.rb │ │ ├── path_helper.rb │ │ ├── runner.rb │ │ ├── table_aligner.rb │ │ └── text_mate_helper.rb └── spec │ ├── cucumber │ └── mate │ │ ├── feature_helper_spec.rb │ │ ├── files │ │ ├── base_spec.rb │ │ ├── feature_file_spec.rb │ │ └── steps_file_spec.rb │ │ ├── runner_spec.rb │ │ ├── table_aligner_spec.rb │ │ └── text_mate_helper_spec.rb │ ├── spec.opts │ └── spec_helper.rb ├── Syntaxes ├── Cucumber Plain Text Feature.tmLanguage ├── Cucumber Steps.tmLanguage └── plaintext_template.erb └── info.plist /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | coverage 4 | -------------------------------------------------------------------------------- /Commands/Switch to New Canonoical Fork.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | beforeRunningCommand 6 | nop 7 | command 8 | cd "$TM_BUNDLE_PATH" 9 | 10 | echo "<pre>" 11 | [ -d ".git" ] && git remote rename origin old-origin && git remote add origin git://github.com/aslakhellesoy/cucumber-tmbundle.git && git fetch origin && git checkout -b old-master && git branch -d master && git checkout origin/master && git checkout -b master && git config branch.master.remote origin && git config branch.master.merge refs/heads/master && git checkout master && git pull && echo -e "\n\nHi, I (Ben Mabey) am no longer maintaining the Cucumber Bundle (vim convert).\nYour git origin is now set to track Aslak Hellesøy's fork as he \nand Dr. Nic are now in charge of maintaining/improving the project. Please \nuse Aslak's GitHub issue tracker if you have any problems.\n\n" && echo So long, and thanks for all the fish\! 12 | [ ! -d .git ] && echo "Hmm.. we didn't find any git files in your bundle. Please re-install manually for an update." 13 | 14 | osascript -e 'tell app "TextMate" to reload bundles' 15 | 16 | echo "</pre>" 17 | input 18 | selection 19 | name 20 | Switch to New Canonical Fork and Update 21 | output 22 | showAsHTML 23 | uuid 24 | E7E79650-2A87-44A0-8226-3113A94FCE53 25 | 26 | 27 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2009 Ben Mabey 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Preferences/Comments.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Comments 7 | scope 8 | text.gherkin.feature 9 | settings 10 | 11 | shellVariables 12 | 13 | 14 | name 15 | TM_COMMENT_START 16 | value 17 | # 18 | 19 | 20 | name 21 | TM_COMMENT_START_2 22 | value 23 | =begin 24 | 25 | 26 | 27 | name 28 | TM_COMMENT_END_2 29 | value 30 | =end 31 | 32 | 33 | 34 | 35 | uuid 36 | 75E08730-A1D7-4D0F-B594-EAFD9DD49B4D 37 | 38 | 39 | -------------------------------------------------------------------------------- /Preferences/Cucumber Plain Text Feature Completions.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Gherkin Completions 7 | scope 8 | text.gherkin.feature 9 | settings 10 | 11 | completions 12 | 13 | As a 14 | As an 15 | I want to 16 | So that 17 | Story: 18 | Scenario: 19 | Scenario Outline: 20 | Given 21 | Then 22 | When 23 | And 24 | But 25 | 26 | 27 | uuid 28 | 8B547854-6A6A-4E45-B51E-9324F0E9D364 29 | 30 | 31 | -------------------------------------------------------------------------------- /Preferences/Symbol list: Scenario.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Symbol list: Scenario 7 | scope 8 | text.gherkin.feature string.language.gherkin.scenario.title 9 | settings 10 | 11 | showInSymbolList 12 | 1 13 | symbolTransformation 14 | s/^\s*:\s*/ / 15 | 16 | uuid 17 | 90F0D309-13C4-4286-88DE-60F61DDEF094 18 | 19 | 20 | -------------------------------------------------------------------------------- /Preferences/Symbol list: Steps with String.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Symbol list: Steps with String 7 | scope 8 | source.ruby.rspec.cucumber.steps string.quoted.step.cucumber.classic.ruby 9 | settings 10 | 11 | showInSymbolList 12 | 1 13 | 14 | uuid 15 | 41A25845-3142-47FE-A225-FDF453C1D59D 16 | 17 | 18 | -------------------------------------------------------------------------------- /Preferences/Symbol list: Steps.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Symbol list: Steps with Regexp 7 | scope 8 | source.ruby.rspec.cucumber.steps string.regexp.step.cucumber 9 | settings 10 | 11 | showInSymbolList 12 | 1 13 | 14 | uuid 15 | 0843B907-3EC2-4F51-A1F6-85EFDB88464B 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. Textmate Bundle for Cucumber 2 | 3 | Hi, 4 | Aslak Hellesøy and Dr. Nic Williams are now maintaining this project. 5 | 6 | Please install and refer to "the canonical fork":http://github.com/cucumber/cucumber-tmbundle for updates/tickets. 7 | If you already have the bundle installed with my fork you can use the "Update Cucumber Bundle" menu item followed 8 | by the "Switch to New Canonical Fork" item once it is updated. 9 | 10 | I have been working on this project since May 2008 (originally for the RSpec Story Runner) and it has been 11 | a lot of fun and rewarding. I no longer use TextMate though so I decided to hand it over to people who 12 | are still avid TextMate users and will give it the proper attention and love it deserves. :) 13 | 14 | So long, and thanks for all the fish! 15 | 16 | -Ben 17 | 18 | 19 | P.S. 20 | Some people with older git versions installed have had trouble with the "Switch to New Canonical Fork" command. 21 | If the command doesn't work for you the bundle will need to be reinstalled manually. Assuming you used the 22 | original install directions this should work for you: 23 | 24 |
25 |     cd ~/Library/Application\ Support/TextMate/Bundles
26 |     rm -rf Cucumber.tmbundle 
27 |     git clone git://github.com/cucumber/cucumber-tmbundle.git Cucumber.tmbundle
28 |     osascript -e 'tell app "TextMate" to reload bundles'
29 | 
30 | 31 | -------------------------------------------------------------------------------- /Support/Rakefile: -------------------------------------------------------------------------------- 1 | $:.unshift(File.dirname(__FILE__) + '/../../rspec/lib') 2 | require 'rubygems' 3 | require 'spec/rake/spectask' 4 | 5 | desc "Run all specs" 6 | Spec::Rake::SpecTask.new do |t| 7 | t.rcov = true 8 | t.spec_opts = ['--colour', '--diff'] 9 | t.rcov_opts = ['--exclude', 'rspec\/plugins,rspec\/lib\/spec,spec\/spec,fixtures,bin\/spec'] 10 | end 11 | 12 | 13 | Dir['bundle_tasks/**/*.rake'].each { |rake| load rake } -------------------------------------------------------------------------------- /Support/bundle_tasks/syntax.rake: -------------------------------------------------------------------------------- 1 | class SyntaxGenerator 2 | def generate 3 | require 'erb' 4 | require 'gherkin/i18n' 5 | 6 | template = ERB.new(IO.read(File.dirname(__FILE__) + '/../../Syntaxes/plaintext_template.erb')) 7 | syntax = template.result(binding) 8 | 9 | syntax_file = File.dirname(__FILE__) + '/../../Syntaxes/Cucumber Plain Text Feature.tmLanguage' 10 | File.open(syntax_file, "w") do |io| 11 | io.write(syntax) 12 | end 13 | end 14 | 15 | def escape(s) 16 | s.gsub(/'/, "\\\\'").gsub(/\*/, "\\\\*") 17 | end 18 | end 19 | 20 | desc 'Generates the plain text syntax file for all languages supported by Cucumber' 21 | task :generate do 22 | SyntaxGenerator.new.generate 23 | end 24 | -------------------------------------------------------------------------------- /Support/fixtures/example_failing_spec.rb: -------------------------------------------------------------------------------- 1 | describe "An example failing spec" do 2 | it "should fail" do 3 | true.should be_false 4 | end 5 | 6 | it "should also fail" do 7 | false.should be_true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Support/fixtures/example_passing_spec.rb: -------------------------------------------------------------------------------- 1 | describe "An example failing spec" do 2 | it "should pass" do 3 | true.should be_true 4 | end 5 | 6 | it "should pass too" do 7 | false.should be_false 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Support/fixtures/features/additional_basic.feature: -------------------------------------------------------------------------------- 1 | Feature: Additional Basic 2 | 3 | As a person interested in something 4 | I want to look for something 5 | So that I can do something 6 | 7 | Scenario: do some cool thingy yeah man 8 | Given Basic step (given) 9 | And another basic step 10 | And global step 11 | And unimplemented step 12 | 13 | When Basic when 14 | 15 | Then Basic then 16 | 17 | Scenario: do something else 18 | Given Basic regexp (given) 19 | 20 | When Basic when 21 | 22 | Then Basic then 23 | -------------------------------------------------------------------------------- /Support/fixtures/features/basic.feature: -------------------------------------------------------------------------------- 1 | Feature: Basic 2 | 3 | As a person interested in something 4 | I want to look for something 5 | So that I can do something 6 | 7 | Scenario: do some cool thingy yeah man 8 | Given Basic step (given) 9 | And another basic step 10 | And global step 11 | And unimplemented step 12 | 13 | When Basic when 14 | 15 | Then Basic then 16 | 17 | Scenario: do something else 18 | Given Basic regexp (given) 19 | 20 | When Basic when 21 | 22 | Then Basic then 23 | -------------------------------------------------------------------------------- /Support/fixtures/features/feature1/foo.feature: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmabey/cucumber-tmbundle/bb89925f54372282e6f7500cc53b746e44dbc31a/Support/fixtures/features/feature1/foo.feature -------------------------------------------------------------------------------- /Support/fixtures/features/feature1/step_definitions/foo_steps.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmabey/cucumber-tmbundle/bb89925f54372282e6f7500cc53b746e44dbc31a/Support/fixtures/features/feature1/step_definitions/foo_steps.rb -------------------------------------------------------------------------------- /Support/fixtures/features/non_standard.feature: -------------------------------------------------------------------------------- 1 | 2 | # rake some_defined_task_in_feature:file 3 | # profile some_defined_profile_in_feature 4 | 5 | Feature: some feature 6 | 7 | As a role 8 | I want feature 9 | So that value 10 | -------------------------------------------------------------------------------- /Support/fixtures/features/non_standard_dir/runners/non_standard.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmabey/cucumber-tmbundle/bb89925f54372282e6f7500cc53b746e44dbc31a/Support/fixtures/features/non_standard_dir/runners/non_standard.rb -------------------------------------------------------------------------------- /Support/fixtures/features/non_standard_dir/step_definitions/non_standard_steps.rb: -------------------------------------------------------------------------------- 1 | Given "non standard step" do 2 | 3 | end -------------------------------------------------------------------------------- /Support/fixtures/features/step_definitions/additional_basic_steps.rb: -------------------------------------------------------------------------------- 1 | Given "additional basic step" do 2 | 3 | end 4 | -------------------------------------------------------------------------------- /Support/fixtures/features/step_definitions/basic_steps.rb: -------------------------------------------------------------------------------- 1 | Given "Basic step (given)" do 2 | Foo.should_not_error 3 | end 4 | 5 | Given "another basic step" do # a comment 6 | 7 | end 8 | 9 | Given %r{Basic regexp (.*) with multiple (.*) groups} do |first, second| 10 | 11 | end 12 | 13 | Given %r{Some quoted regexp "(.*)" and '(.*)'} do |first, second| # a comment 14 | 15 | end 16 | 17 | Given /classic regexp/ do # a comment 18 | 19 | end 20 | 21 | When "Basic when" do 22 | 23 | end 24 | 25 | Then 'Basic then' do 26 | 27 | end 28 | -------------------------------------------------------------------------------- /Support/fixtures/features/step_definitions/global_steps.rb: -------------------------------------------------------------------------------- 1 | Given "global step" do 2 | Foo.should_not_error 3 | end 4 | 5 | Given "another global step" do 6 | 7 | end 8 | -------------------------------------------------------------------------------- /Support/fixtures/features/step_definitions/unconventional_steps.rb: -------------------------------------------------------------------------------- 1 | Given("one liner with comment") { one; liner } # boo 2 | 3 | Given ("one liner with trailing space") { one; line; } 4 | 5 | Given("one liner with no trailing space") { one; liner} 6 | 7 | Given ("braces with a comment") { |boo| # yah! 8 | } 9 | 10 | Given("braces with a trailing space") { |boo| 11 | } 12 | 13 | Given("braces no trailing space") { |boo| 14 | yah! 15 | } 16 | -------------------------------------------------------------------------------- /Support/lib/cucumber/mate.rb: -------------------------------------------------------------------------------- 1 | # This is based on the official RSpec tm-bundle 2 | require 'rubygems' 3 | 4 | if ENV['TM_PROJECT_DIRECTORY'] 5 | rspec_rails_plugin = File.join(ENV['TM_PROJECT_DIRECTORY'],'vendor','plugins','rspec','lib') 6 | rspec_merb_gem = (merb_dir = (Dir["#{ENV['TM_PROJECT_DIRECTORY']}/gems/gems/rspec*"].first || '')) && File.join(merb_dir, "lib") 7 | 8 | if File.directory?(rspec_rails_plugin) 9 | $LOAD_PATH.unshift(rspec_rails_plugin) 10 | elsif File.directory?(rspec_merb_gem) 11 | $LOAD_PATH.unshift(rspec_merb_gem) 12 | elsif ENV['TM_RSPEC_HOME'] 13 | rspec_lib = File.join(ENV['TM_RSPEC_HOME'], 'lib') 14 | unless File.directory?(rspec_lib) 15 | raise "TM_RSPEC_HOME points to a bad location: #{ENV['TM_RSPEC_HOME']}" 16 | end 17 | $LOAD_PATH.unshift(rspec_lib) 18 | end 19 | end 20 | require 'spec' 21 | 22 | $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..'))) 23 | require "cucumber/mate/feature_helper" 24 | require "cucumber/mate/runner" -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/feature_helper.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), %w[.. mate]) 2 | require File.join(File.dirname(__FILE__), %w[path_helper]) 3 | require File.join(File.dirname(__FILE__), %w[text_mate_helper]) 4 | require File.join(File.dirname(__FILE__), 'files') 5 | 6 | module Cucumber 7 | module Mate 8 | 9 | class FeatureHelper 10 | include PathHelper 11 | 12 | def initialize(full_file_path) 13 | @full_file_path = full_file_path 14 | @file = Files::Base.create_from_file_path(full_file_path) 15 | end 16 | 17 | def run_feature 18 | argv = [] 19 | argv << "FEATURE=#{@file.feature_file_path}" 20 | unless (cucumber_opts = ENV['TM_CUCUMBER_OPTS']) 21 | cucumber_opts = "" 22 | cucumber_opts << '--format' 23 | cucumber_opts << '=html' 24 | end 25 | argv << "CUCUMBER_OPTS=#{cucumber_opts}" 26 | 27 | in_project_directory do 28 | puts `rake features:standard #{argv.join(' ')}` 29 | end 30 | end 31 | 32 | def goto_alternate_file 33 | goto_or_create_file(@file.alternate_file_path) 34 | end 35 | 36 | def choose_alternate_file 37 | alternate_files_and_names = @file.alternate_files_and_names 38 | if (choice = TextMateHelper.display_select_list(alternate_files_and_names.collect{|h| h[:name] || h[:file_path]})) 39 | goto_or_create_file(alternate_files_and_names[choice][:file_path]) 40 | end 41 | end 42 | 43 | def goto_current_step(line_number) 44 | return unless @file.feature_file? && step_info = @file.step_information_for_line(line_number) 45 | if (step_location = @file.location_of_step(step_info)) 46 | TextMateHelper.goto_file(step_location.delete(:file_path), step_location) 47 | else 48 | goto_steps_file_with_new_steps([step_info]) 49 | end 50 | end 51 | 52 | def goto_step_usage(line_number) 53 | 54 | end 55 | 56 | def create_all_undefined_steps 57 | return unless @file.feature_file? && undefined_steps = @file.undefined_steps 58 | goto_steps_file_with_new_steps(undefined_steps) 59 | end 60 | 61 | def autocomplete_step(stdout, current_line) 62 | unless matches = current_line.match(/([\s\t]*(?:given|when|then|and|but)\s+)(.*)/i) 63 | stdout.print current_line 64 | return 65 | end 66 | line_start, step_prefix = matches[1..2] 67 | matching_step_definitions = @file.steps_starting_with(step_prefix) 68 | unless matching_step_definitions && matching_step_definitions.size > 0 69 | stdout.print current_line 70 | return 71 | end 72 | if matching_step_definitions.size > 1 73 | patterns = matching_step_definitions.map { |step| step[:pattern_text] } 74 | if choice = TextMateHelper.display_select_list(patterns) 75 | result = convert_step_definition_regexp_groups_to_snippet_tab_stops(matching_step_definitions[choice]) 76 | stdout.print "#{line_start}#{result}" 77 | return 78 | end 79 | else 80 | result = convert_step_definition_regexp_groups_to_snippet_tab_stops(matching_step_definitions.first) 81 | stdout.print "#{line_start}#{result}" 82 | return 83 | end 84 | stdout.print current_line 85 | end 86 | 87 | protected 88 | def goto_steps_file_with_new_steps(new_steps) 89 | steps_file = Files::StepsFile.new(@file.steps_file_path) 90 | goto_or_create_file(steps_file.full_file_path, 91 | :line => 1, 92 | :column => 1, 93 | :additional_content => Files::StepsFile.create_steps(new_steps, !File.file?(steps_file.full_file_path))) 94 | end 95 | 96 | def request_confirmation_to_create_file(file_path) 97 | TextMateHelper.request_confirmation(:title => "Create new file?", :prompt => "Do you want to create\n#{file_path.gsub(/^(.*?)features/, 'features')}?") 98 | end 99 | 100 | def goto_or_create_file(file_path, options = {}) 101 | options = {:line => 1, :column => 1}.merge(options) 102 | additional_content = options.delete(:additional_content) 103 | 104 | if File.file?(file_path) 105 | TextMateHelper.goto_file(file_path, options) 106 | TextMateHelper.insert_text(additional_content) if additional_content 107 | elsif request_confirmation_to_create_file(file_path) 108 | TextMateHelper.create_and_open_file(file_path) 109 | TextMateHelper.insert_text(default_content(file_path, additional_content)) 110 | end 111 | end 112 | 113 | def silently_create_file(file_path) 114 | TextMateHelper.create_file(file_path) 115 | `echo "#{Files::Base.create_from_file_path(file_path).class.default_content(file_path).gsub('"','\\"')}" > "#{file_path}"` 116 | end 117 | 118 | def default_content(file_path, additional_content) 119 | Files::Base.default_content_for(file_path, additional_content) 120 | end 121 | 122 | def step_regexs 123 | [/^I am on (.+)$/, /I go to (.+)$/, /^I press "(.*)"$/] 124 | end 125 | 126 | def convert_step_definition_regexp_groups_to_snippet_tab_stops(step_def) 127 | tab_stop_count = 1 128 | snippet_text = step_def[:pattern_text] 129 | while snippet_text.match(%r{\(}) 130 | snippet_text.sub!(%r{\(([^)]+)\)}, "${#{tab_stop_count}:\\1}") 131 | tab_stop_count += 1 132 | end 133 | snippet_text 134 | end 135 | end 136 | 137 | end 138 | end 139 | -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/files.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(File.join(File.dirname(__FILE__), 'files')) 2 | require 'base' 3 | require 'feature_file' 4 | require 'steps_file' 5 | require 'step_detector' -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/files/base.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | module Cucumber 4 | module Mate 5 | 6 | module Files 7 | 8 | class InvalidFilePathError < StandardError; end 9 | class Base 10 | attr_accessor :full_file_path 11 | 12 | class << self 13 | def create_from_file_path(file_path) 14 | if klass_from_file_path(file_path) 15 | klass_from_file_path(file_path).new(file_path) 16 | else 17 | raise InvalidFilePathError, "Feature files should have suffix .feature; Step definitions should be _steps.rb" 18 | end 19 | end 20 | 21 | def default_content_for(file_path, additional_content = nil) 22 | klass_from_file_path(file_path).default_content(file_path, additional_content) 23 | end 24 | 25 | def klass_from_file_path(file_path) 26 | case file_path 27 | when /feature$/ then FeatureFile 28 | when /_steps\.rb$/ then StepsFile 29 | end 30 | end 31 | end 32 | 33 | def initialize(full_file_path) 34 | @full_file_path = full_file_path 35 | @options = parse_options 36 | end 37 | 38 | def project_root 39 | @project_root ||= find_project_dir(File.dirname(full_file_path)) 40 | end 41 | 42 | def relative_path 43 | @relative_path ||= full_file_path[project_root.length+1..-1] 44 | end 45 | 46 | def in_project_directory(&block) 47 | result = nil 48 | Dir.chdir(project_root) { result = yield } 49 | result 50 | end 51 | 52 | def default_file_path(kind, name = self.name) 53 | name = @options[kind] if @options[kind] 54 | replacements = { 55 | :steps => "/step_definitions/#{name}_steps.rb", 56 | :feature => "/#{name}.feature" 57 | } 58 | 59 | raise ArgumentError, "passed argument must be one of #{replacements.keys.join(', ')}" unless replacements.has_key?(kind) 60 | full_file_path.gsub(%r<(/#{name}\.(feature|txt))|((/[^/])?(/steps)?/#{name}(_\w*)?\.rb)>, replacements[kind]) 61 | end 62 | 63 | def file_path(kind, name = self.name) 64 | name = @options[kind] if @options[kind] 65 | search_paths = { 66 | :steps => "#{project_root}/features/**/#{name}_steps.rb", 67 | :feature => "#{project_root}/features/**/#{name}.{feature,txt}" 68 | } 69 | 70 | raise ArgumentError, "passed argument must be one of #{search_paths.keys.join(', ')}" unless search_paths.has_key?(kind) 71 | 72 | Dir[search_paths[kind]].first || default_file_path(kind) 73 | end 74 | 75 | def all(kind) 76 | in_project_directory do 77 | case kind.to_sym 78 | when :feature 79 | Dir['features/**/*.feature'].map { |f| FeatureFile.new(File.expand_path(f)) } 80 | when :steps 81 | Dir['features/**/*_steps.rb'].map { |f| StepsFile.new(File.expand_path(f)) } 82 | end 83 | end 84 | end 85 | 86 | def all_path_and_names(kind) 87 | all(kind).map {|file| {:file_path => file.full_file_path, :name => file.name} } 88 | end 89 | 90 | def feature_file_path 91 | file_path(:feature) 92 | end 93 | 94 | def steps_file_path 95 | file_path(:steps) 96 | end 97 | 98 | def name 99 | @name ||= full_file_path.match(%r{/([^/]*)\.\w*$}).captures.first 100 | end 101 | 102 | def feature_file?; false; end 103 | def steps_file?; false; end 104 | 105 | def ==(step_or_feature_file) 106 | step_or_feature_file.is_a?(self.class) && self.full_file_path == step_or_feature_file.full_file_path 107 | end 108 | 109 | private 110 | def parse_options 111 | return {} unless File.file?(full_file_path) 112 | first_line = File.open(full_file_path) {|f| f.readline unless f.eof} || '' 113 | return {} unless first_line.match(/\s*#\s*(.+:.+)/) 114 | $1.split(',').inject({}) do |hash, pair| 115 | k,v = pair.split(':') 116 | hash[k.strip.to_sym] = v.strip if k && v 117 | hash 118 | end 119 | end 120 | 121 | def find_project_dir(current_dir) 122 | return nil unless File.exists?(current_dir) 123 | current_dir = File.expand_path(current_dir) 124 | FileUtils.chdir(current_dir) do 125 | parent_dir = File.expand_path("..") 126 | return nil if parent_dir == current_dir 127 | boot_file = File.join(current_dir, "features") 128 | return File.exists?(boot_file) ? current_dir : find_project_dir(parent_dir) 129 | end 130 | end 131 | 132 | end 133 | 134 | end 135 | 136 | end 137 | end 138 | -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/files/feature_file.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), %w[.. path_helper]) 2 | module Cucumber 3 | module Mate 4 | 5 | module Files 6 | 7 | class FeatureFile < Base 8 | class << self 9 | include PathHelper 10 | 11 | def default_content(file_path, additional_content = nil) 12 | TextMateHelper.snippet_text_for('Feature') 13 | end 14 | 15 | end 16 | 17 | def feature_file?; true; end 18 | 19 | def alternate_file_path 20 | steps_file_path 21 | end 22 | 23 | def rake_task 24 | content_lines.detect {|line| line =~ /^\s*#\s*rake\s+([\w:]+)/} ? $1 : nil 25 | end 26 | 27 | def profile 28 | content_lines.detect {|line| line =~ /^\s*#\s*profile\s+([\w]+)/} ? $1 : nil 29 | end 30 | 31 | def step_files_and_names 32 | all_path_and_names(:steps) 33 | end 34 | 35 | alias :alternate_files_and_names :step_files_and_names 36 | 37 | # Returns the name of a step at a specific line number 38 | # e.g. if the step on the target line is: Given a logged in user 39 | # then the result would be: { :step_name => "a logged in user" } 40 | def step_information_for_line(line_number) 41 | line_index = line_number.to_i-1 42 | 43 | line_text = content_lines[line_index] 44 | return unless line_text && line_text.strip!.match(/^(given|when|then|and)(.*)/i) 45 | step_type = $1.capitalize 46 | source_step_name = $2.strip 47 | if step_type == "And" 48 | content_lines[0..(line_index - 1)].reverse.detect do |line| 49 | if line.match(/^\s*(given|when|then)(.*)/i) 50 | step_type = $1.capitalize 51 | end 52 | end 53 | end 54 | 55 | return {:step_name => source_step_name, :step_type => step_type} 56 | end 57 | 58 | # Right now will return first matching step 59 | # Ultimately used by TextMateHelper.goto_file 60 | # Returns a hash with keys: :file_path, :line, :pattern, :pattern_text 61 | def location_of_step(step_info) 62 | all_defined_steps.each do |step_def| 63 | if step_def[:pattern].is_a?(Regexp) 64 | pattern = step_def[:pattern] 65 | else 66 | pattern = %r(^#{Regexp.escape(step_def[:pattern].gsub(/\$\w+/, "STRING_MATCHER_TOKEN")).gsub("STRING_MATCHER_TOKEN", "(.+)")}$) 67 | end 68 | return step_def if pattern =~ step_info[:step_name] 69 | end 70 | nil 71 | end 72 | 73 | def steps_starting_with(step_prefix) 74 | step_prefix_regex = /^#{step_prefix}/ 75 | all_defined_steps.select do |step_def| 76 | step_def[:pattern_text] =~ step_prefix_regex 77 | end 78 | end 79 | 80 | def undefined_steps 81 | all_steps_in_file.inject([]) do |undefined_steps, step_info| 82 | unless location_of_step(step_info) || undefined_steps.any?{|s| s[:step_name] == step_info[:step_name] } 83 | undefined_steps << step_info 84 | end 85 | undefined_steps 86 | end 87 | end 88 | 89 | protected 90 | def all_steps_in_file 91 | file_lines = File.read(full_file_path).split("\n").collect{|l| l.strip} 92 | 93 | file_lines.inject([]) do |text_steps, line| 94 | step_type = $1 if line.match(/^(Given|When|Then)\s+/) 95 | text_steps << {:step_type => step_type, :step_name => $2} if line.match(/^(Given|When|Then|And)\s+(.*)$/) 96 | text_steps 97 | end 98 | end 99 | 100 | def content_lines 101 | @content_lines ||= File.read(full_file_path).split("\n") 102 | end 103 | 104 | # Returns an array of hashes, each describing a Given/When/Then step defined in this step file 105 | # Each hash-per-step has the keys: :file_path, :line, :pattern, :pattern_text 106 | def all_defined_steps 107 | @defined_steps ||= step_files_and_names.inject([]) do |mem, step_file_info| 108 | StepsFile.new(step_file_info[:file_path]).step_definitions.each do |step_def| 109 | mem << step_def 110 | end 111 | mem 112 | end 113 | end 114 | end 115 | 116 | end 117 | 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/files/step_detector.rb: -------------------------------------------------------------------------------- 1 | module Cucumber 2 | module Mate 3 | 4 | module Files 5 | 6 | class StepDetector 7 | def initialize(path_to_a_feature_file) 8 | @step_files = ( 9 | Dir[File.dirname(path_to_a_feature_file) + "/step_definitions/**/*.rb"] + 10 | Dir[File.dirname(path_to_a_feature_file) + "/**/*_steps.rb"] 11 | ).uniq 12 | end 13 | 14 | # returns [ { :file_path => path, :name => StepFile#name } ] 15 | def step_files_and_names 16 | @step_files.map do |step_file| 17 | { :file_path => File.expand_path(step_file), :name => StepsFile.new(step_file).name } 18 | end 19 | end 20 | end 21 | 22 | end 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/files/steps_file.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | module Cucumber 4 | module Mate 5 | 6 | module Files 7 | 8 | class StepsFile < Base 9 | class << self 10 | def default_content(file_path, additional_content = create_steps([:step_type => 'Given', :step_name => 'condition'])) 11 | additional_content || "" 12 | end 13 | 14 | def create_steps(steps_to_create, already_included_snippet_selection = true) 15 | sorted_steps = steps_to_create.inject({'Given' => [], 'When' => [], 'Then' => []}) do |steps_so_far, current_step_info| 16 | steps_so_far[current_step_info[:step_type] || 'Then'] << current_step_info[:step_name] 17 | steps_so_far 18 | end 19 | 20 | content = "" 21 | %w(Given When Then).each do |step_type| 22 | sorted_steps[step_type].each do |step_name| 23 | step_name_text = already_included_snippet_selection ? step_name : "${1:#{step_name}}" 24 | content += %Q{ #{step_type} "#{step_name_text}" do\n pending\n end\n \n} 25 | already_included_snippet_selection = true 26 | end 27 | end 28 | content 29 | end 30 | end 31 | 32 | def steps_file?; true; end 33 | 34 | def name 35 | @name ||= super.gsub("_steps", "") 36 | end 37 | 38 | def rake_task 39 | feature_file.rake_task 40 | end 41 | 42 | def profile 43 | feature_file.profile 44 | end 45 | 46 | def alternate_file_path 47 | feature_file_path 48 | end 49 | 50 | def feature_files_and_names 51 | all_path_and_names(:feature) 52 | end 53 | 54 | alias :alternate_files_and_names :feature_files_and_names 55 | 56 | # Returns an array of hashes, each describing a Given/When/Then step defined in this step file 57 | # Each hash-per-step has the keys: :file_path, :line, :pattern, :pattern_text 58 | def step_definitions 59 | if File.file?(full_file_path) 60 | @steps = [] 61 | @file_contents = File.read(full_file_path) 62 | lines = @file_contents.split("\n") 63 | lines.each do |line| 64 | case line 65 | when /\s*(When|Given|Then).+do\s*(\|[^\|]+\|){0,1}\s*(#.+|$)/ 66 | line.gsub!($3, "") if $3 67 | line << "; end" 68 | when /\s*(When|Given|Then)\s*\(.+\)\s*\{\s*.+\s*\}\s*(#.+|$)/ 69 | when /\s*(When|Given|Then)\s*\(.+\)\s*\{\s*(\|[^\|]+\|){0,1}\s*(#.+|$)/ 70 | line.gsub!(/#[^#]+$/, '') if $2 71 | line << "}" 72 | else 73 | line.insert(0, "# ") 74 | end 75 | end 76 | @file_contents = lines * "\n" 77 | instance_eval(@file_contents, full_file_path, 1) 78 | @steps 79 | else 80 | [] 81 | end 82 | end 83 | 84 | protected 85 | # While evaluating step definitions code - This called when a new step has been parse 86 | # We need to save these to be able to match plain text 87 | def add_step(type, pattern) 88 | line_number = caller[1].match(/:(\d+)/).captures.first.to_i 89 | 90 | @steps << {:pattern => pattern, :line => line_number, 91 | :pattern_text => (pattern.is_a?(Regexp) ? pattern.source.gsub('^', '') : pattern), 92 | :file_path => full_file_path} 93 | end 94 | 95 | def steps_for(*args) 96 | yield if block_given? 97 | end 98 | 99 | def feature_file 100 | @feature_file ||= FeatureFile.new(feature_file_path) 101 | end 102 | 103 | def Given(pattern) 104 | add_step('Given', pattern) 105 | end 106 | 107 | def When(pattern) 108 | add_step('When', pattern) 109 | end 110 | 111 | def Then(pattern) 112 | add_step('Then', pattern) 113 | end 114 | 115 | def World(helpers) 116 | end 117 | 118 | def After 119 | end 120 | 121 | def Before 122 | end 123 | 124 | def at_exit 125 | end 126 | end 127 | 128 | end 129 | 130 | end 131 | end 132 | -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/path_helper.rb: -------------------------------------------------------------------------------- 1 | module PathHelper 2 | 3 | def full_project_directory 4 | features_directory = find_project_dir(File.dirname(@full_file_path)) 5 | end 6 | 7 | # Evaluates the block within the full_project_directory 8 | # and returns the result 9 | def in_project_directory(&block) 10 | result = nil 11 | Dir.chdir(full_project_directory) { result = yield } 12 | result 13 | end 14 | 15 | def find_project_dir(current_dir) 16 | return nil unless File.exists?(current_dir) 17 | current_dir = File.expand_path(current_dir) 18 | FileUtils.chdir(current_dir) do 19 | parent_dir = File.expand_path("..") 20 | return nil if parent_dir == current_dir 21 | boot_file = File.join(current_dir, "config", "boot.rb") 22 | return File.exists?(boot_file) ? current_dir : find_project_dir(parent_dir) 23 | end 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/runner.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), %w[.. mate]) 2 | require File.join(File.dirname(__FILE__), 'files') 3 | 4 | module Cucumber 5 | module Mate 6 | 7 | class Runner 8 | CUCUMBER_BIN = %x{which cucumber}.chomp 9 | RUBY_BIN = ENV['TM_RUBY'] || %x{which ruby}.chomp 10 | RAKE_BIN = %x{which rake}.chomp 11 | 12 | def initialize(output, project_directory, full_file_path, cucumber_bin = nil, cucumber_opts=nil) 13 | @file = Files::Base.create_from_file_path(full_file_path) 14 | @output = output 15 | @project_directory = project_directory 16 | @filename_opts = "" 17 | @cucumber_bin = cucumber_bin || CUCUMBER_BIN 18 | @cucumber_opts = cucumber_opts || "--format=html" 19 | @cucumber_opts << " --profile=#{@file.profile}" if @file.profile 20 | end 21 | 22 | def run_scenario(line_number) 23 | @filename_opts << ":#{line_number}" 24 | run 25 | end 26 | 27 | def run_feature 28 | run 29 | end 30 | 31 | def autoformat_feature 32 | in_project_dir do 33 | Kernel.system("#{cucumber_cmd} --autoformat . #{@file.relative_path}") 34 | end 35 | end 36 | 37 | 38 | protected 39 | 40 | def run 41 | argv = [] 42 | if @file.rake_task 43 | command = RAKE_BIN 44 | argv << "FEATURE=#{@file.full_file_path}" 45 | argv << %Q{CUCUMBER_OPTS="#{@cucumber_opts}"} 46 | else 47 | command = cucumber_cmd 48 | argv << "#{@file.full_file_path}#{@filename_opts}" 49 | argv << @cucumber_opts 50 | end 51 | in_project_dir do 52 | @output << %Q{Running: #{full_command = "#{RUBY_BIN} #{command} #{@file.rake_task} #{argv.join(' ')}"} \n} 53 | @output << Kernel.system(full_command) 54 | end 55 | end 56 | 57 | def cucumber_cmd 58 | File.exists?(script = "#{@project_directory}/script/cucumber") ? script : @cucumber_bin 59 | end 60 | 61 | def in_project_dir(&block) 62 | Dir.chdir(@project_directory, &block) 63 | end 64 | 65 | end 66 | 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/table_aligner.rb: -------------------------------------------------------------------------------- 1 | module Cucumber 2 | module Mate 3 | class TableAligner 4 | def align(lines) 5 | group_by_tables(lines).map do |group| 6 | if(group.is_a? Array) 7 | align_table(group) 8 | else 9 | group 10 | end 11 | end.flatten 12 | end 13 | 14 | private 15 | 16 | def group_by_tables(lines) 17 | current_table = [] 18 | groups = [] 19 | 20 | lines.each do |line| 21 | if line.match(/\s*\|/) 22 | current_table << line 23 | else 24 | groups << current_table unless current_table.empty? 25 | groups << line 26 | current_table = [] 27 | end 28 | end 29 | 30 | groups << current_table unless current_table.empty? 31 | groups 32 | end 33 | 34 | def align_table(table) 35 | table_data = table.map{|line| split_line(line).map{|cell| cell.strip}} 36 | max_columns = table_data.inject(0) {|memo, row| memo > row.size ? memo : row.size} 37 | table_data = table_data.map{|row| row.concat([""] * (max_columns - row.size))} 38 | max_lengths = table_data.transpose.map { |col| col.map { |cell| cell.unpack("U*").length }.max }.flatten 39 | initial_space = table.first.match(/(\s*)|/)[1] 40 | 41 | table_data.map do |line| 42 | initial_space[0..-2].to_s + \ 43 | line.zip(max_lengths).map { |cell, max_length| 44 | cell + " " * (max_length - cell.unpack("U*").length) 45 | }.join(' | ') + ' |' 46 | end 47 | end 48 | 49 | def split_line(line) 50 | cells = line.strip.split("|", -1) 51 | 52 | if(cells.last.strip == "") 53 | cells.delete_at(cells.size - 1) if line =~ /\|\s*$/ 54 | end 55 | 56 | cells 57 | end 58 | end 59 | end 60 | end 61 | 62 | -------------------------------------------------------------------------------- /Support/lib/cucumber/mate/text_mate_helper.rb: -------------------------------------------------------------------------------- 1 | require "#{ENV['TM_SUPPORT_PATH']}/lib/textmate" 2 | require "#{ENV['TM_SUPPORT_PATH']}/lib/ui" 3 | require "#{ENV['TM_SUPPORT_PATH']}/lib/exit_codes" 4 | require "tempfile" 5 | 6 | module Cucumber 7 | module Mate 8 | 9 | class TextMateHelper 10 | class << self 11 | # Opens target file_path and sets cursor position 12 | # options: 13 | # :line - line number (default: ENV['TM_LINE_NUMBER']) 14 | # :column - column number (default: 1) 15 | def goto_file(file_path, options = {}) 16 | TextMate.go_to(options.merge(:file => file_path)) 17 | end 18 | 19 | def display_select_list(options) 20 | ninja_search = "/Applications/NinjaSearch.app/Contents/MacOS/NinjaSearch" 21 | list = options 22 | if list.size > too_many_to_select && File.exists?(ninja_search) 23 | data = list.join("\n") # TODO escape single quotes OR store in file 24 | res = nil 25 | Tempfile.open("ninjasearch-cucumber") do |f| 26 | f << data 27 | f.flush 28 | res = %x{NINJA_DATA='#{f.path}' #{e_sh ninja_search} 2>/dev/console} 29 | end 30 | list.index(res.strip) 31 | else 32 | TextMate::UI.menu(list) 33 | end 34 | end 35 | 36 | def alert(options = {}) 37 | options = {:message => options} if options.kind_of?(String) 38 | options = {:style => :informational, :title => 'Alert!', :message => '', :buttons => 'OK'}.merge(options) 39 | TextMate::UI.alert(options[:style], options[:title], options[:message], options[:buttons]) 40 | end 41 | 42 | def request_confirmation(options) 43 | TextMate::UI.request_confirmation(options) 44 | end 45 | 46 | def create_file(file_path) 47 | `mkdir -p "#{File.dirname(file_path)}"` 48 | `touch "#{file_path}"` 49 | end 50 | 51 | def create_and_open_file(file_path) 52 | create_file(file_path) 53 | `osascript &>/dev/null -e 'tell app "SystemUIServer" to activate' -e 'tell app "TextMate" to activate'` 54 | `"$TM_SUPPORT_PATH/bin/mate" "#{file_path}"` 55 | end 56 | 57 | def insert_text(text) 58 | `osascript &>/dev/null -e 'tell app "SystemUIServer" to activate' -e 'tell app "TextMate" to activate'` 59 | escaped_content = text.gsub("\n","\\n").gsub('$','\\$').gsub('"','\\\\\\\\\\\\"') 60 | `osascript &>/dev/null -e "tell app \\"TextMate\\" to insert \\"#{escaped_content}\\" as snippet true"` 61 | end 62 | 63 | def too_many_to_select 64 | 9 65 | end 66 | end 67 | end 68 | 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /Support/spec/cucumber/mate/feature_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../spec_helper' 2 | require File.dirname(__FILE__) + '/../../../lib/cucumber/mate/feature_helper' 3 | 4 | module Cucumber 5 | module Mate 6 | 7 | describe FeatureHelper do 8 | before(:each) do 9 | @fixtures_path = File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. .. fixtures])) 10 | # Default - don't let TextMateHelper actually perform any actions 11 | TextMateHelper.stub!(:goto_file) 12 | TextMateHelper.stub!(:display_select_list) 13 | TextMateHelper.stub!(:request_confirmation) 14 | TextMateHelper.stub!(:create_and_open_file) 15 | TextMateHelper.stub!(:insert_text) 16 | TextMateHelper.stub!(:create_file) 17 | 18 | File.stub!(:file?).and_return(true) 19 | 20 | @helper_file = mock('helper file', 21 | :feature_file? => true, 22 | :feature_file_path => '/path/to/feature/file', 23 | :steps_file_path => '/path/to/step_definitions/file', 24 | :runner_file_path => '/path/to/runner/file', 25 | :alternate_file_path => "/alternate/file/path", 26 | :alternate_files_and_names => [ 27 | {:name => 'one', :file_path => "/path/to/one"}, 28 | {:name => 'two', :file_path => "/path/to/two"} 29 | ]) 30 | 31 | Files::Base.stub!(:create_from_file_path).and_return(@helper_file) 32 | Files::Base.stub!(:default_content_for).and_return('') 33 | @feature_helper = FeatureHelper.new("#{@fixtures_path}/features/basic.feature") 34 | end 35 | 36 | describe "#goto_alternate_file" do 37 | it "should tell textmate to go to the correct file" do 38 | # expects 39 | TextMateHelper.should_receive('goto_file').with('/alternate/file/path', :line => 1, :column => 1) 40 | # when 41 | @feature_helper.goto_alternate_file 42 | end 43 | 44 | describe "when a file doesn't exist" do 45 | before(:each) do 46 | File.stub!(:file?).and_return(false) 47 | end 48 | 49 | it "should ask if the file should be created" do 50 | # expects 51 | TextMateHelper.should_receive('request_confirmation') 52 | # when 53 | @feature_helper.goto_alternate_file 54 | end 55 | 56 | describe "and the user chooses to create the file" do 57 | before(:each) do 58 | TextMateHelper.stub!(:request_confirmation).and_return(true) 59 | end 60 | 61 | it "should create the file and add the default contents" do 62 | # expects 63 | TextMateHelper.should_receive('create_and_open_file').with('/alternate/file/path') 64 | TextMateHelper.should_receive('insert_text') 65 | # when 66 | @feature_helper.goto_alternate_file 67 | end 68 | end 69 | 70 | describe "and the user chooses NOT to create the file" do 71 | before(:each) do 72 | TextMateHelper.stub!(:request_confirmation).and_return(false) 73 | end 74 | 75 | it "should not create the file" do 76 | # expects 77 | TextMateHelper.should_not_receive('create_and_open_file') 78 | # when 79 | @feature_helper.goto_alternate_file 80 | end 81 | end 82 | end 83 | end 84 | 85 | describe "#autocomplete_step" do 86 | describe "with no matches" do 87 | before(:each) do 88 | @helper_file.should_receive(:steps_starting_with).with("xxx").and_return([]) 89 | stdout = StringIO.new 90 | @feature_helper.autocomplete_step(stdout, " Given xxx") 91 | stdout.rewind 92 | @stdout = stdout.read 93 | end 94 | it "should print original current_line" do 95 | @stdout.should == " Given xxx" 96 | end 97 | end 98 | 99 | describe "with 1 match" do 100 | before(:each) do 101 | @helper_file.should_receive(:steps_starting_with).with("weird step"). 102 | and_return([{:pattern_text => 'weird step with (\d+) match in step files'}]) 103 | stdout = StringIO.new 104 | @feature_helper.autocomplete_step(stdout, " Given weird step") 105 | stdout.rewind 106 | @stdout = stdout.read 107 | end 108 | it "should print original current_line" do 109 | @stdout.should == ' Given weird step with ${1:\d+} match in step files' 110 | end 111 | end 112 | 113 | describe "with multiple matches and choose 1" do 114 | before(:each) do 115 | @helper_file.should_receive(:steps_starting_with).with("weird step"). 116 | and_return([ 117 | {:pattern_text => 'weird step (with|without) object in step (.*)'}, 118 | {:pattern_text => "weird step with second match in step files"} 119 | ]) 120 | TextMateHelper.should_receive(:display_select_list).with([ 121 | 'weird step (with|without) object in step (.*)', 'weird step with second match in step files' ]). 122 | and_return(0) 123 | stdout = StringIO.new 124 | @feature_helper.autocomplete_step(stdout, " Given weird step") 125 | stdout.rewind 126 | @stdout = stdout.read 127 | end 128 | it "should print chosen pattern" do 129 | @stdout.should == ' Given weird step ${1:with|without} object in step ${2:.*}' 130 | end 131 | end 132 | end 133 | 134 | describe "#choose_alternate_file" do 135 | it "should prompt the user to choose a step file from those included in the runner" do 136 | # expects 137 | TextMateHelper.should_receive('display_select_list').with(['one', 'two']) 138 | # when 139 | @feature_helper.choose_alternate_file 140 | end 141 | 142 | it "should tell textmate to open the chosen file (after a user has selected)" do 143 | TextMateHelper.stub!(:display_select_list).and_return(0) 144 | 145 | # expects 146 | TextMateHelper.should_receive('goto_file').with("/path/to/one", :line => 1, :column => 1) 147 | # when 148 | @feature_helper.choose_alternate_file 149 | end 150 | end 151 | 152 | describe "#goto_current_step" do 153 | describe "when not on a feature file" do 154 | before(:each) do 155 | @helper_file.stub!(:feature_file?).and_return(false) 156 | end 157 | 158 | it "should not tell textmate to do anything" do 159 | # expects 160 | TextMateHelper.should_not_receive('display_select_list') 161 | TextMateHelper.should_not_receive('goto_file') 162 | # when 163 | @feature_helper.goto_current_step(1) 164 | end 165 | end 166 | 167 | describe "when on a feature file" do 168 | describe "and the current line doesn't contain a step" do 169 | before(:each) do 170 | @helper_file.stub!(:step_information_for_line).and_return(nil) 171 | end 172 | 173 | it "should not tell textmate to do anything" do 174 | # expect 175 | TextMateHelper.should_not_receive('goto_file') 176 | # when 177 | @feature_helper.goto_current_step(1) 178 | end 179 | end 180 | 181 | describe "and the current line contains a step" do 182 | before(:each) do 183 | @helper_file.stub!(:step_information_for_line).and_return({:step_type => 'Given', :step_name => 'blah'}) 184 | end 185 | 186 | describe "when the runner file doesn't exist" do 187 | before(:each) do 188 | File.stub!(:file?).and_return(false) 189 | @helper_file.stub!(:location_of_step) 190 | end 191 | 192 | it "should prompt to create the runner file" do 193 | # expects 194 | TextMateHelper.should_receive('request_confirmation').once # once for the steps file 195 | # when 196 | @feature_helper.goto_current_step(1) 197 | end 198 | end 199 | 200 | describe "and the step exists" do 201 | before(:each) do 202 | @helper_file.stub!(:location_of_step).and_return({:file_path => '/foo/bar', :line => 10, :column => 3}) 203 | end 204 | 205 | it "should tell textmate to goto the file where the step is defined" do 206 | # expects 207 | TextMateHelper.should_receive('goto_file').with('/foo/bar', {:line => 10, :column => 3}) 208 | # when 209 | @feature_helper.goto_current_step(1) 210 | end 211 | end 212 | 213 | describe "and the step doesn't exist" do 214 | before(:each) do 215 | @helper_file.stub!(:location_of_step).and_return(nil) 216 | @helper_file.stub!(:step_information_for_line).and_return(nil) 217 | end 218 | 219 | it "should tell textmate to goto the feature's step file and to insert the step" do 220 | pending "JohnnyT..." 221 | # expects 222 | TextMateHelper.should_receive('goto_file').with('/path/to/step_definitions/file', {:line => 2, :column => 1}) 223 | TextMateHelper.should_receive('insert_text') 224 | 225 | # when 226 | @feature_helper.goto_current_step(1) 227 | end 228 | end 229 | end 230 | end # when on a feature file 231 | end 232 | end 233 | 234 | end 235 | end 236 | -------------------------------------------------------------------------------- /Support/spec/cucumber/mate/files/base_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | require File.dirname(__FILE__) + '/../../../../lib/cucumber/mate/files' 3 | 4 | module Cucumber 5 | module Mate 6 | 7 | module Files 8 | 9 | describe Base do 10 | before(:each) do 11 | @fixtures_path = File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. .. .. fixtures])) 12 | @file = Base.new(File.expand_path(File.join(@fixtures_path, %w[features basic.feature]))) 13 | end 14 | 15 | it "should determine the base project path" do 16 | @file.project_root.should == @fixtures_path 17 | end 18 | 19 | it "should determine the relative path (relative to the project_root)" do 20 | @file.relative_path.should == 'features/basic.feature' 21 | end 22 | 23 | it "should determine the name of the file" do 24 | @file.name.should == 'basic' 25 | end 26 | 27 | describe "#create_from_file_path" do 28 | describe "when file name is not valid step nor feature file name" do 29 | it "should throw a descriptive exception" do 30 | lambda { Base.create_from_file_path("/path/to/some_feature.features") }.should raise_error(InvalidFilePathError) 31 | end 32 | end 33 | describe "when file name is .feature" do 34 | before(:each) do 35 | @file = Base.create_from_file_path("/path/to/some_feature.feature") 36 | end 37 | it do 38 | @file.class.should == FeatureFile 39 | end 40 | end 41 | describe "when file name is _steps.rb" do 42 | before(:each) do 43 | @file = Base.create_from_file_path("/path/to/some_steps.rb") 44 | end 45 | it do 46 | @file.class.should == StepsFile 47 | end 48 | end 49 | end 50 | 51 | describe "#default_file_path" do 52 | describe "when the file type is invalid" do 53 | it "should throw a descriptive exception" do 54 | lambda { @file.default_file_path(:blah) }.should raise_error(ArgumentError) 55 | end 56 | end 57 | 58 | it "should determine the default steps file path" do 59 | @file.default_file_path(:steps).should == "#{@fixtures_path}/features/step_definitions/basic_steps.rb" 60 | end 61 | 62 | it "should determine the default feature file path" do 63 | @file.default_file_path(:feature).should == "#{@fixtures_path}/features/basic.feature" 64 | end 65 | end 66 | 67 | describe "#file_path" do 68 | describe "when the file type is invalid" do 69 | it "should throw a descriptive exception" do 70 | lambda { @file.file_path(:blah) }.should raise_error(ArgumentError) 71 | end 72 | end 73 | 74 | describe "when looking for an existing steps file" do 75 | describe "when the file is the standard location" do 76 | it "should return the path to the existing file" do 77 | @file.file_path(:steps).should == "#{@fixtures_path}/features/step_definitions/basic_steps.rb" 78 | end 79 | end 80 | 81 | describe "when the file is in a non-standard location" do 82 | before(:each) do 83 | @file = Base.new(File.expand_path(File.join(@fixtures_path, %w[features non_standard.feature]))) 84 | end 85 | 86 | it "should return the path to the existing file" do 87 | @file.file_path(:steps).should == "#{@fixtures_path}/features/non_standard_dir/step_definitions/non_standard_steps.rb" 88 | end 89 | end 90 | end 91 | 92 | 93 | describe "when looking for an existing feature file" do 94 | describe "when the file is the standard location" do 95 | it "should return the path to the existing file" do 96 | @file.file_path(:feature).should == "#{@fixtures_path}/features/basic.feature" 97 | end 98 | end 99 | 100 | describe "when the file is in a non-standard location" do 101 | before(:each) do 102 | @file = Base.new(File.expand_path(File.join(@fixtures_path, %w[features non_standard_dir runners non_standard.rb]))) 103 | end 104 | 105 | it "should return the path to the existing file" do 106 | @file.file_path(:feature).should == "#{@fixtures_path}/features/non_standard.feature" 107 | end 108 | end 109 | end 110 | 111 | describe "when looking for all existing files" do 112 | it "should find all feature files" do 113 | expected = %w[additional_basic.feature basic.feature feature1/foo.feature non_standard.feature] 114 | expected.map! { |path| FeatureFile.new(File.join(project_root, "features", path)) } 115 | @file.all(:feature).should == expected 116 | end 117 | 118 | it "should find all steps files" do 119 | expected = %w[feature1/step_definitions/foo_steps.rb 120 | non_standard_dir/step_definitions/non_standard_steps.rb 121 | step_definitions/additional_basic_steps.rb 122 | step_definitions/basic_steps.rb 123 | step_definitions/global_steps.rb 124 | step_definitions/unconventional_steps.rb 125 | ] 126 | expected.map! { |path| StepsFile.new(File.join(project_root, "features", path)) } 127 | @file.all(:steps).should == expected 128 | end 129 | end 130 | 131 | 132 | 133 | end # file_path 134 | 135 | end 136 | 137 | end 138 | end 139 | end -------------------------------------------------------------------------------- /Support/spec/cucumber/mate/files/feature_file_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | require File.dirname(__FILE__) + '/../../../../lib/cucumber/mate/files' 3 | 4 | module Cucumber 5 | module Mate 6 | module Files 7 | 8 | describe FeatureFile do 9 | 10 | #TODO Get rid of fixtures and create the example files in specs inline (and stub the IO.read call) 11 | 12 | def feature_file_from_fixtures(feature_name) 13 | FeatureFile.new(File.expand_path(File.join(@fixtures_path, "features", "#{feature_name}.feature"))) 14 | end 15 | 16 | before(:each) do 17 | @fixtures_path = File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. .. .. fixtures])) 18 | @feature_file = feature_file_from_fixtures('basic') 19 | end 20 | 21 | it "should be a feature file" do 22 | @feature_file.should be_feature_file 23 | end 24 | 25 | # describe "when a steps file exists on the filesystem (even if not using the assumed directory structure)" do 26 | # before(:each) do 27 | # @feature_file = FeatureFile.new(File.expand_path(File.join(@fixtures_path, %w[features non_standard.feature]))) 28 | # end 29 | # 30 | # it "should return the path to the existing steps file" do 31 | # @feature_file.steps_file_path.should == "#{@fixtures_path}/features/non_standard_dir/step_definitions/non_standard_steps.rb" 32 | # end 33 | # end 34 | # 35 | # describe "when a steps file doesn't exist on the filesystem" do 36 | # it "should determine the path to the new steps file (and assume the proposed directory structure)" do 37 | # @feature_file.steps_file_path.should == "#{@fixtures_path}/features/step_definitions/basic_steps.rb" 38 | # end 39 | # end 40 | 41 | it "should not be a steps file" do 42 | @feature_file.should_not be_steps_file 43 | end 44 | 45 | it "should return the correct step file path" do 46 | @feature_file.alternate_file_path.should == "#{@fixtures_path}/features/step_definitions/basic_steps.rb" 47 | end 48 | 49 | it "should determine the correct alternate file as the step file" do 50 | @feature_file.alternate_file_path.should == @feature_file.steps_file_path 51 | end 52 | 53 | describe "#name" do 54 | it "should return the simple name (based off the file name)" do 55 | @feature_file.name.should == 'basic' 56 | end 57 | end 58 | 59 | describe "#rake_task" do 60 | it "should return nil when none is defined in the file" do 61 | @feature_file.rake_task.should be_nil 62 | end 63 | 64 | it "should return the rake task defined in the features file" do 65 | feature_file = feature_file_from_fixtures('non_standard') 66 | feature_file.rake_task.should == 'some_defined_task_in_feature:file' 67 | end 68 | end 69 | 70 | describe "#profile" do 71 | it "should return nil when none is defined in the file" do 72 | @feature_file.profile.should be_nil 73 | end 74 | 75 | it "should return the rake task defined in the features file" do 76 | feature_file = feature_file_from_fixtures('non_standard') 77 | feature_file.profile.should == 'some_defined_profile_in_feature' 78 | end 79 | end 80 | 81 | describe "#alternate_files_and_names" do 82 | it "should return the list of step files being used in the feature" do 83 | @feature_file.alternate_files_and_names.should == 84 | [ 85 | {:file_path=>"#{@fixtures_path}/features/feature1/step_definitions/foo_steps.rb", :name => 'foo'}, 86 | {:file_path=>"#{@fixtures_path}/features/non_standard_dir/step_definitions/non_standard_steps.rb", :name => 'non_standard'}, 87 | {:file_path=>"#{@fixtures_path}/features/step_definitions/additional_basic_steps.rb", :name => 'additional_basic'}, 88 | {:file_path=>"#{@fixtures_path}/features/step_definitions/basic_steps.rb", :name => 'basic'}, 89 | {:file_path=>"#{@fixtures_path}/features/step_definitions/global_steps.rb", :name => 'global'}, 90 | {:file_path=>"#{@fixtures_path}/features/step_definitions/unconventional_steps.rb", :name => 'unconventional'} 91 | ] 92 | end 93 | end 94 | 95 | describe "#step_information_for_line" do 96 | it "should not return anything if the line doesn't contain a valid step" do 97 | @feature_file.step_information_for_line(5).should == nil 98 | end 99 | 100 | it "should return the step information if the line contains a valid step" do 101 | @feature_file.step_information_for_line(8).should == {:step_name => 'Basic step (given)', :step_type => "Given"} 102 | end 103 | 104 | end 105 | 106 | describe "#location_of_step" do 107 | describe "when the step definition exists" do 108 | it "should return the correct file, line and column for string-based step" do 109 | StepsFile.stub!(:new).and_return(@steps = mock('steps file', :step_definitions => [{:pattern => "string pattern", :pattern_text => "string pattern", :line => 3, :file_path => '/path/to/basic_steps.rb'}], :full_file_path => '/path/to/basic_steps.rb', :name => 'basic')) 110 | @feature_file.location_of_step({:step_name => 'string pattern'}).should == 111 | {:pattern => "string pattern", :pattern_text => "string pattern", :line => 3, :file_path => '/path/to/basic_steps.rb'} 112 | end 113 | 114 | it "should return the correct file, line and column for regexp-based step" do 115 | StepsFile.stub!(:new).and_return(@steps = mock('steps file', :step_definitions => [{:pattern => /string pattern/, :pattern_text => "string pattern", :line => 3, :file_path => '/path/to/basic_steps.rb'}], :full_file_path => '/path/to/basic_steps.rb', :name => 'basic')) 116 | @feature_file.location_of_step({:step_name => 'string pattern'}).should == 117 | {:pattern => /string pattern/, :pattern_text => "string pattern", :line => 3, :file_path => '/path/to/basic_steps.rb'} 118 | end 119 | 120 | it "matches $ tokens in strings" do 121 | step = {:pattern => "it should have $count items", :pattern_text => "it should have $count items", :line => 3, :file_path => '/path/to/basic_steps.rb'} 122 | StepsFile.stub!(:new).and_return(@steps = mock('steps file', :step_definitions => [step], :full_file_path => '/path/to/basic_steps.rb', :name => 'basic')) 123 | @feature_file.location_of_step({:step_name => 'it should have 5 items'}).should == 124 | step 125 | end 126 | 127 | it "enforces beginning and end matching with string steps" do 128 | step = {:pattern => "it should have $count items", :pattern_text => "it should have $count items", :line => 3, :file_path => '/path/to/basic_steps.rb'} 129 | StepsFile.stub!(:new).and_return(@steps = mock('steps file', :step_definitions => [step], :full_file_path => '/path/to/basic_steps.rb', :name => 'basic')) 130 | @feature_file.location_of_step({:step_name => 'it should have 5 items and some cheese'}).should == nil 131 | end 132 | end 133 | end 134 | 135 | describe "#steps_starting_with" do 136 | before(:each) do 137 | StepsFile.stub!(:new).and_return(@steps = mock('steps file', :step_definitions => [ 138 | {:pattern => "matching string", :pattern_text => "matching string", :line => 3, :file_path => '/path/to/steps'}, 139 | {:pattern => /^matching pattern/, :pattern_text => "matching pattern", :line => 3, :file_path => '/path/to/steps'}, 140 | {:pattern => "not matching string", :pattern_text => "not matching string", :line => 3, :file_path => '/path/to/steps'}, 141 | ], :full_file_path => '/path/to/basic_steps.rb', :name => 'basic')) 142 | @feature_file.should_receive(:step_files_and_names).at_least(:once).and_return([{:file_path => '/path/to/steps', :name => 'steps'}]) 143 | end 144 | 145 | describe "when 1 matching string step definition exists" do 146 | before(:each) do 147 | @matching_steps = @feature_file.steps_starting_with('matching s') 148 | end 149 | 150 | it "should return the step definition" do 151 | @matching_steps.size.should == 1 152 | end 153 | end 154 | 155 | describe "when 1 matching regex step definition exists" do 156 | before(:each) do 157 | @matching_steps = @feature_file.steps_starting_with('matching p') 158 | end 159 | 160 | it "should return the step definition" do 161 | @matching_steps.size.should == 1 162 | end 163 | end 164 | 165 | describe "when multiple matching step definitions exists" do 166 | before(:each) do 167 | @matching_steps = @feature_file.steps_starting_with('match') 168 | end 169 | 170 | it "should return the step definition" do 171 | @matching_steps.size.should == 2 172 | end 173 | end 174 | 175 | describe "when no matching step definitions exists" do 176 | before(:each) do 177 | @matching_steps = @feature_file.steps_starting_with('xxx') 178 | end 179 | 180 | it "should return the step definition" do 181 | @matching_steps.size.should == 0 182 | end 183 | end 184 | end 185 | 186 | describe "#undefined_steps" do 187 | it "should return a unique list of steps not defined in the feature" do 188 | @feature_file.stub!(:all_steps_in_file).and_return([ 189 | {:step_name => 'a member named Foo'}, 190 | {:step_name => 'Foo walks into a bar'}, 191 | {:step_name => 'a member named Foo'} 192 | ]) 193 | 194 | @feature_file.stub!(:location_of_step).and_return(nil) 195 | @feature_file.undefined_steps.should == ([ 196 | {:step_name => 'a member named Foo'}, 197 | {:step_name => 'Foo walks into a bar'} 198 | ]) 199 | end 200 | end 201 | end 202 | 203 | end 204 | 205 | end 206 | end -------------------------------------------------------------------------------- /Support/spec/cucumber/mate/files/steps_file_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | require File.dirname(__FILE__) + '/../../../../lib/cucumber/mate/files' 3 | 4 | module Cucumber 5 | module Mate 6 | module Files 7 | 8 | describe StepsFile do 9 | before(:each) do 10 | @fixtures_path = File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. .. .. fixtures])) 11 | @steps_file = StepsFile.new(File.expand_path(File.join(@fixtures_path, %w[features step_definitions basic_steps.rb]))) 12 | end 13 | 14 | it "should determine the feature file" do 15 | @steps_file.feature_file_path.should == "#{@fixtures_path}/features/basic.feature" 16 | end 17 | 18 | it "should determine the correct alternate file" do 19 | @steps_file.alternate_file_path.should == @steps_file.feature_file_path 20 | end 21 | 22 | describe "#name" do 23 | it "should return the simple name (based off the file name)" do 24 | @steps_file.name.should == 'basic' 25 | end 26 | end 27 | 28 | describe "#rake_task" do 29 | it "should delegate to the file's feature file" do 30 | FeatureFile.should_receive(:new).with(@steps_file.feature_file_path).and_return(feature_file = mock('feature file')) 31 | feature_file.stub!(:rake_task).and_return("some_rake_task") 32 | 33 | @steps_file.rake_task.should == "some_rake_task" 34 | end 35 | end 36 | 37 | describe "#profile" do 38 | it "should delegate to the file's feature file" do 39 | FeatureFile.should_receive(:new).with(@steps_file.feature_file_path).and_return(feature_file = mock('feature file')) 40 | feature_file.stub!(:profile).and_return("watir") 41 | 42 | @steps_file.profile.should == "watir" 43 | end 44 | end 45 | 46 | describe "#alternate_files_and_names" do 47 | it "should generate a list of feature files (and names) which use this steps file" do 48 | @steps_file.alternate_files_and_names.should == 49 | [ 50 | {:file_path=>"#{@fixtures_path}/features/additional_basic.feature", :name => 'additional_basic'}, 51 | {:file_path=>"#{@fixtures_path}/features/basic.feature", :name => 'basic'}, 52 | {:file_path=>"#{@fixtures_path}/features/feature1/foo.feature", :name => 'foo'}, 53 | {:file_path=>"#{@fixtures_path}/features/non_standard.feature", :name => 'non_standard'} 54 | ] 55 | end 56 | end 57 | 58 | describe "#step_definitions" do 59 | it "should return a list of step definitions included in this file" do 60 | @steps_file.step_definitions.should == 61 | [ 62 | {:pattern => "Basic step (given)", :pattern_text => "Basic step (given)", :line => 1, :file_path => @steps_file.full_file_path}, 63 | {:pattern => "another basic step", :pattern_text => "another basic step", :line => 5, :file_path => @steps_file.full_file_path}, 64 | {:pattern => %r{Basic regexp (.*) with multiple (.*) groups}, :pattern_text => "Basic regexp (.*) with multiple (.*) groups", :line => 9, :file_path => @steps_file.full_file_path}, 65 | {:pattern => /Some quoted regexp "(.*)" and '(.*)'/, :pattern_text => "Some quoted regexp \"(.*)\" and '(.*)'", :line => 13, :file_path => @steps_file.full_file_path}, 66 | {:pattern => /classic regexp/, :pattern_text => "classic regexp", :line => 17, :file_path => @steps_file.full_file_path}, 67 | {:pattern => "Basic when", :pattern_text => "Basic when", :line => 21, :file_path => @steps_file.full_file_path}, 68 | {:pattern => "Basic then", :pattern_text => "Basic then", :line => 25, :file_path => @steps_file.full_file_path}, 69 | ] 70 | end 71 | 72 | it "should parse unconventional step definitions" do 73 | StepsFile.new(File.expand_path(File.join(@fixtures_path, %w[features step_definitions unconventional_steps.rb]))).step_definitions.should == [ 74 | {:pattern_text => "one liner with comment", :pattern => "one liner with comment", :file_path => "#{@fixtures_path}/features/step_definitions/unconventional_steps.rb", :line => 1}, 75 | {:pattern_text => "one liner with trailing space", :pattern => "one liner with trailing space", :file_path => "#{@fixtures_path}/features/step_definitions/unconventional_steps.rb", :line => 3}, 76 | {:pattern_text => "one liner with no trailing space", :pattern => "one liner with no trailing space", :file_path => "#{@fixtures_path}/features/step_definitions/unconventional_steps.rb", :line => 5}, 77 | {:pattern_text => "braces with a comment", :pattern => "braces with a comment", :file_path => "#{@fixtures_path}/features/step_definitions/unconventional_steps.rb", :line => 7}, 78 | {:pattern_text => "braces with a trailing space", :pattern => "braces with a trailing space", :file_path => "#{@fixtures_path}/features/step_definitions/unconventional_steps.rb", :line => 10}, 79 | {:pattern_text => "braces no trailing space", :pattern => "braces no trailing space", :file_path => "#{@fixtures_path}/features/step_definitions/unconventional_steps.rb", :line => 13} 80 | ] 81 | end 82 | end 83 | end 84 | 85 | end 86 | 87 | end 88 | end -------------------------------------------------------------------------------- /Support/spec/cucumber/mate/runner_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../spec_helper' 2 | require File.dirname(__FILE__) + '/../../../lib/cucumber/mate/runner' 3 | 4 | module Cucumber 5 | module Mate 6 | describe "a run command", :shared => true do 7 | it "should run with the cucumber command by default" do 8 | expect_system_call_to_be_made_with(%r[#{Cucumber::Mate::Runner::CUCUMBER_BIN}]) 9 | when_run_is_called 10 | end 11 | 12 | it "should run with the cucumber command passed in" do 13 | expect_system_call_to_be_made_with(%r[/some/path/cucumber ]) 14 | when_run_is_called(nil, "/some/path/cucumber") 15 | end 16 | 17 | it "should run with /project/path/script/cucumber if present" do 18 | File.should_receive(:exists?).with("/project/path/script/cucumber").and_return(true) 19 | expect_system_call_to_be_made_with(%r[/project/path/script/cucumber ]) 20 | when_run_is_called 21 | end 22 | 23 | it "should run the single feature" do 24 | expect_system_call_to_be_made_with(/ #{@file.full_file_path}/) 25 | when_run_is_called 26 | end 27 | 28 | it "should run with the deafult cucumber options when none are passed in" do 29 | expect_system_call_to_be_made_with(/--format=html/) 30 | when_run_is_called 31 | end 32 | 33 | it "should run with the profile defined in the feature file" do 34 | # given 35 | @file.stub!(:profile).and_return('watir') 36 | expect_system_call_to_be_made_with(/--profile=watir/) 37 | when_run_is_called 38 | end 39 | 40 | it "should run with the cucumber options passed in" do 41 | expect_system_call_to_be_made_with(/--format=custom/) 42 | when_run_is_called("--format=custom") 43 | end 44 | 45 | it "should direct the call's output to the passed in output" do 46 | Kernel.stub!(:system).and_return("features html") 47 | output = when_run_is_called 48 | output.string.should =~ /features html/ 49 | end 50 | 51 | it "should output the exact command it is running" do 52 | Kernel.stub!(:system).and_return("features html") 53 | output = when_run_is_called 54 | output.string.should =~ /^Running: .+cucumber / 55 | end 56 | 57 | describe "when the feature file defines a rake task" do 58 | before(:each) do 59 | @file.stub!(:rake_task).and_return('some_task') 60 | end 61 | 62 | it "should run the feature with the defined rake task" do 63 | expect_system_call_to_be_made_with(/#{Cucumber::Mate::Runner::RAKE_BIN} #{@file.rake_task} /) 64 | when_run_is_called 65 | end 66 | 67 | it "should run the single feature with the rake syntax" do 68 | expect_system_call_to_be_made_with(/FEATURE=#{@file.full_file_path}/) 69 | when_run_is_called 70 | end 71 | 72 | it "should run with the deafult cucumber options when none are passed in with the rake syntax" do 73 | expect_system_call_to_be_made_with(/CUCUMBER_OPTS="--format=html/) 74 | when_run_is_called 75 | end 76 | 77 | it "should run with the cucumber options passed in with the rake syntax" do 78 | expect_system_call_to_be_made_with(/CUCUMBER_OPTS="--format=custom/) 79 | when_run_is_called("--format=custom") 80 | end 81 | end 82 | end 83 | 84 | describe Runner do 85 | before(:each) do 86 | Files::Base.stub!(:create_from_file_path).and_return( 87 | @file = mock("feature file", 88 | :rake_task => nil, 89 | :profile => nil, 90 | :feature_file_path => 'path_to_feature.feature', 91 | :relative_path => 'relative_path.feature', 92 | :full_file_path => '/foo/bar/relative_path.feature' 93 | ) 94 | ) 95 | Dir.stub!(:chdir).and_yield 96 | Kernel.stub!(:system) 97 | File.stub!(:exists?).and_return(false) 98 | end 99 | 100 | def expect_system_call_to_be_made_with(regex) 101 | Kernel.should_receive(:system).with(regex) 102 | end 103 | 104 | it "should create a new Files::Base from the passed in file path" do 105 | # expect 106 | Files::Base.should_receive(:create_from_file_path).with("/path/to/file").and_return(stub('file').as_null_object) 107 | # when 108 | Runner.new(nil, "/path","/path/to/file") 109 | end 110 | 111 | describe "#run_feature" do 112 | 113 | def when_run_feature_is_called(cucumber_options=nil, cucumber_bin = nil) 114 | Runner.new(output=StringIO.new, "/project/path", "/project/path/feature_file", cucumber_bin, cucumber_options).run_feature 115 | output 116 | end 117 | alias :when_run_is_called :when_run_feature_is_called 118 | 119 | it_should_behave_like "a run command" 120 | end 121 | 122 | describe "#run_scenario" do 123 | def when_run_scenario_is_called(cucumber_options=nil, cucumber_bin = nil) 124 | Runner.new(output=StringIO.new, "/project/path", "/project/path/feature_file", cucumber_bin, cucumber_options).run_scenario(12) 125 | output 126 | end 127 | alias :when_run_is_called :when_run_scenario_is_called 128 | 129 | it_should_behave_like "a run command" 130 | 131 | it "should pass the line number in the cucumber options" do 132 | # given 133 | runner = Runner.new(output=StringIO.new, "/project/path", @file.full_file_path) 134 | 135 | expect_system_call_to_be_made_with(%r{#{@file.full_file_path}:42 --format=html}) 136 | 137 | # when 138 | runner.run_scenario(42) 139 | end 140 | end 141 | 142 | describe "#format_feature" do 143 | it "should use cucumber's --autoformat on the specified feature file" do 144 | # given 145 | runner = Runner.new(output=StringIO.new, "/project/path", "/project/path/feature_file") 146 | 147 | expect_system_call_to_be_made_with(%r{--autoformat \. relative_path.feature$}) 148 | 149 | # when 150 | runner.autoformat_feature 151 | end 152 | end 153 | end 154 | end 155 | end 156 | -------------------------------------------------------------------------------- /Support/spec/cucumber/mate/table_aligner_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../spec_helper' 2 | require File.dirname(__FILE__) + '/../../../lib/cucumber/mate/table_aligner' 3 | 4 | module Cucumber 5 | module Mate 6 | describe TableAligner do 7 | it "should align a simple table" do 8 | unaligned = [ 9 | " | a |b|", 10 | " |c| d |" 11 | ] 12 | 13 | expected = [ 14 | " | a | b |", 15 | " | c | d |" 16 | ] 17 | 18 | TableAligner.new.align(unaligned).should == expected 19 | end 20 | 21 | it "should align multiple tables" do 22 | unaligned = [ 23 | " | a |b|", 24 | " |c| d |", 25 | "", 26 | " |x | y|zz|", 27 | " |1|2|3|" 28 | ] 29 | 30 | expected = [ 31 | " | a | b |", 32 | " | c | d |", 33 | "", 34 | " | x | y | zz |", 35 | " | 1 | 2 | 3 |" 36 | ] 37 | 38 | TableAligner.new.align(unaligned).should == expected 39 | end 40 | 41 | it "should pad short rows out to the longest row" do 42 | unaligned = [ 43 | "", 44 | " |a|b|", 45 | " |x|y|z", 46 | " |", 47 | "" 48 | ] 49 | 50 | expected = [ 51 | "", 52 | " | a | b | |", 53 | " | x | y | z |", 54 | " | | | |", 55 | "" 56 | ] 57 | 58 | TableAligner.new.align(unaligned).should == expected 59 | end 60 | 61 | it "should align a table with multi-byte UTF8 values" do 62 | unaligned = [ 63 | " | aa |b|", 64 | " |÷| d |" 65 | ] 66 | 67 | expected = [ 68 | " | aa | b |", 69 | " | ÷ | d |" 70 | ] 71 | 72 | TableAligner.new.align(unaligned).should == expected 73 | end 74 | 75 | it "should align a table that has cells with no content" do 76 | unaligned = [ 77 | " |a|b|", 78 | " |||" 79 | ] 80 | 81 | expected = [ 82 | " | a | b |", 83 | " | | |" 84 | ] 85 | 86 | TableAligner.new.align(unaligned).should == expected 87 | end 88 | end 89 | end 90 | end -------------------------------------------------------------------------------- /Support/spec/cucumber/mate/text_mate_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../spec_helper' 2 | require File.dirname(__FILE__) + '/../../../lib/cucumber/mate/text_mate_helper' 3 | 4 | module Cucumber 5 | module Mate 6 | 7 | describe TextMateHelper do 8 | end 9 | 10 | end 11 | end -------------------------------------------------------------------------------- /Support/spec/spec.opts: -------------------------------------------------------------------------------- 1 | --colour 2 | --loadby 3 | mtime 4 | --reverse -------------------------------------------------------------------------------- /Support/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'spec' 3 | 4 | ENV['TM_SUPPORT_PATH'] = '/Applications/TextMate.app/Contents/SharedSupport/Support' 5 | 6 | module Spec::Example::ExampleMethods 7 | def project_root 8 | @project_root ||= File.expand_path(File.join(File.dirname(__FILE__), '../fixtures')) 9 | end 10 | end -------------------------------------------------------------------------------- /Syntaxes/Cucumber Plain Text Feature.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | feature 8 | 9 | firstLineMatch 10 | 기능|機能|功能|フィーチャ|خاصية|תכונה|Функционалност|Функционал|Особина|Могућност|Özellik|Właściwość|Tính năng|Savybė|Požiadavka|Požadavek|Osobina|Ominaisuus|Omadus|OH HAI|Mogućnost|Mogucnost|Jellemző|Fīča|Funzionalità|Funktionalität|Funkcionalnost|Funkcionalitāte|Funcționalitate|Functionaliteit|Functionalitate|Funcionalitat|Funcionalidade|Fonctionnalité|Fitur|Feature|Egenskap|Egenskab|Crikey|Característica|Arwedd(.*) 11 | foldingStartMarker 12 | ^\s*\b(예|시나리오 개요|시나리오|배경|背景|場景大綱|場景|场景大纲|场景|劇本大綱|劇本|例子|例|テンプレ|シナリオテンプレート|シナリオテンプレ|シナリオアウトライン|シナリオ|サンプル|سيناريو مخطط|سيناريو|امثلة|الخلفية|תרחיש|תבנית תרחיש|רקע|דוגמאות|Тарих|Сценарији|Сценарио|Сценарий структураси|Сценарий|Структура сценарија|Структура сценария|Скица|Рамка на сценарий|Примери|Пример|Предыстория|Предистория|Позадина|Основа|Мисоллар|Концепт|Контекст|Значения|Örnekler|Założenia|Voorbeelden|Variantai|Tình huống|Tausta|Taust|Tapausaihio|Tapaus|Tapaukset|Szenariogrundriss|Szenario|Szablon scenariusza|Stsenaarium|Struktura scenarija|Skica|Skenario konsep|Skenario|Situācija|Senaryo taslağı|Senaryo|Scénář|Scénario|Schema dello scenario|Scenārijs pēc parauga|Scenārijs|Scenár|Scenariusz|Scenariul de şablon|Scenariul de sablon|Scenariu|Scenarios|Scenario Outline|Scenario Amlinellol|Scenario|Scenarijus|Scenariji|Scenarijaus šablonas|Scenarijai|Scenarij|Scenarie|Rerefons|Raamstsenaarium|Příklady|Példák|Príklady|Przykłady|Primjeri|Primeri|Primer|Pozadí|Pozadina|Pozadie|Plan du scénario|Plan du Scénario|Piemēri|Pavyzdžiai|Paraugs|Osnova scénáře|Osnova|Náčrt Scénáře|Náčrt Scenáru|Mate|MISHUN SRSLY|MISHUN|Kịch bản|Kontext|Konteksts|Kontekstas|Kontekst|Koncept|Khung tình huống|Khung kịch bản|Juhtumid|Háttér|Grundlage|Geçmiş|Forgatókönyv vázlat|Forgatókönyv|Exemplos|Exemples|Exemplele|Exempel|Examples|Esquema do Cenário|Esquema do Cenario|Esquema del escenario|Esquema de l\'escenari|Esempi|Escenario|Escenari|Enghreifftiau|Eksempler|Ejemplos|EXAMPLZ|Dữ liệu|Dasar|Contoh|Contexto|Contexte|Contesto|Condiţii|Conditii|Cobber|Cenário|Cenario|Cefndir|Bối cảnh|Blokes|Beispiele|Bakgrunn|Bakgrund|Baggrund|Background|B4|Antecedents|Antecedentes|All y\'all|Achtergrond|Abstrakt Scenario|Abstract Scenario) 13 | foldingStopMarker 14 | ^\s*$ 15 | keyEquivalent 16 | ^~C 17 | name 18 | Cucumber Plain Text Feature 19 | patterns 20 | 21 | 22 | include 23 | #feature_element_keyword 24 | 25 | 26 | include 27 | #description 28 | 29 | 30 | include 31 | #feature_keyword 32 | 33 | 34 | include 35 | #step_keyword 36 | 37 | 38 | include 39 | #strings_triple_quote 40 | 41 | 42 | include 43 | #strings_single_quote 44 | 45 | 46 | include 47 | #strings_double_quote 48 | 49 | 50 | include 51 | #comments 52 | 53 | 54 | include 55 | #tags 56 | 57 | 58 | include 59 | #scenario_outline_variable 60 | 61 | 62 | include 63 | #table 64 | 65 | 66 | repository 67 | 68 | comments 69 | 70 | captures 71 | 72 | 0 73 | 74 | name 75 | comment.line.number-sign 76 | 77 | 78 | match 79 | \s*(#.*) 80 | 81 | table 82 | 83 | begin 84 | ^\s*\| 85 | end 86 | \|\s*$ 87 | name 88 | keyword.control.cucumber.table 89 | patterns 90 | 91 | 92 | match 93 | \w 94 | name 95 | source 96 | 97 | 98 | 99 | feature_keyword 100 | 101 | captures 102 | 103 | 1 104 | 105 | name 106 | keyword.language.gherkin.feature 107 | 108 | 2 109 | 110 | name 111 | string.language.gherkin.feature.title 112 | 113 | 114 | match 115 | ^\s*(기능|機能|功能|フィーチャ|خاصية|תכונה|Функционалност|Функционал|Особина|Могућност|Özellik|Właściwość|Tính năng|Savybė|Požiadavka|Požadavek|Osobina|Ominaisuus|Omadus|OH HAI|Mogućnost|Mogucnost|Jellemző|Fīča|Funzionalità|Funktionalität|Funkcionalnost|Funkcionalitāte|Funcționalitate|Functionaliteit|Functionalitate|Funcionalitat|Funcionalidade|Fonctionnalité|Fitur|Feature|Egenskap|Egenskab|Crikey|Característica|Arwedd):(.*)\b 116 | 117 | step_keyword 118 | 119 | captures 120 | 121 | 1 122 | 123 | name 124 | keyword.language.gherkin.feature.step 125 | 126 | 127 | match 128 | ^\s*(하지만|조건|먼저|만일|만약|단|그리고|그러면|那麼|那么|而且|當|当|前提|假設|假如|但是|但し|並且|もし|ならば|ただし|しかし|かつ|و |متى |لكن |عندما |ثم |بفرض |اذاً |כאשר |וגם |בהינתן |אזי |אז |אבל |Унда |То |Онда |Но |Лекин |Когато |Када |Кад |К тому же |И |Задато |Задати |Задате |Если |Допустим |Дадено |Ва |Бирок |Аммо |Али |Агар |А |Și |És |anrhegedig a |Zatati |Zakładając |Zadato |Zadate |Zadano |Zadani |Zadan |Yna |Ya know how |Ya gotta |Y |Wtedy |When y\'all |When |Wenn |WEN |Và |Ve |Und |Un |Thì |Then y\'all |Then |Tapi |Tak |Tada |Tad |Så |Stel |Soit |Siis |Si |Quando |Quand |Quan |Pryd |Pokud |Pokiaľ |Però |Pero |Pak |Oraz |Onda |Ond |Oletetaan |Og |Och |O zaman |Når |När |Niin |Nhưng |N |Mutta |Men |Mas |Maka |Majd |Mais |Maar |Ma |Lorsque |Lorsqu\'|Kun |Kuid |Kui |Khi |Keď |Ketika |Když |Kai |Kada |Kad |Jeżeli |Ja |Ir |I CAN HAZ |I |Ha |Givet |Given y\'all |Given |Gitt |Gegeven |Gegeben sei |Fakat |Eğer ki |Etant donné |Et |Então |Entonces |Entao |En |Eeldades |E |Duota |Donat |Donada |Diyelim ki |Dengan |De |Dato |Dar |Dann |Dan |Dado |Dacă |Daca |DEN |Când |Cuando |Cho |Cept |Cand |Cal |But y\'all |But |Biết |Bet |BUT |Atès |Atunci |Atesa |Angenommen |And y\'all |And |Ama |Als |Alors |Allora |Ali |Aleshores |Ale |Akkor |Aber |AN |A také |A |\* ) 129 | 130 | feature_element_keyword 131 | 132 | captures 133 | 134 | 1 135 | 136 | name 137 | keyword.language.gherkin.feature.scenario 138 | 139 | 2 140 | 141 | name 142 | string.language.gherkin.scenario.title.title 143 | 144 | 145 | match 146 | ^\s*(예|시나리오 개요|시나리오|배경|背景|場景大綱|場景|场景大纲|场景|劇本大綱|劇本|例子|例|テンプレ|シナリオテンプレート|シナリオテンプレ|シナリオアウトライン|シナリオ|サンプル|سيناريو مخطط|سيناريو|امثلة|الخلفية|תרחיש|תבנית תרחיש|רקע|דוגמאות|Тарих|Сценарији|Сценарио|Сценарий структураси|Сценарий|Структура сценарија|Структура сценария|Скица|Рамка на сценарий|Примери|Пример|Предыстория|Предистория|Позадина|Основа|Мисоллар|Концепт|Контекст|Значения|Örnekler|Założenia|Voorbeelden|Variantai|Tình huống|Tausta|Taust|Tapausaihio|Tapaus|Tapaukset|Szenariogrundriss|Szenario|Szablon scenariusza|Stsenaarium|Struktura scenarija|Skica|Skenario konsep|Skenario|Situācija|Senaryo taslağı|Senaryo|Scénář|Scénario|Schema dello scenario|Scenārijs pēc parauga|Scenārijs|Scenár|Scenariusz|Scenariul de şablon|Scenariul de sablon|Scenariu|Scenarios|Scenario Outline|Scenario Amlinellol|Scenario|Scenarijus|Scenariji|Scenarijaus šablonas|Scenarijai|Scenarij|Scenarie|Rerefons|Raamstsenaarium|Příklady|Példák|Príklady|Przykłady|Primjeri|Primeri|Primer|Pozadí|Pozadina|Pozadie|Plan du scénario|Plan du Scénario|Piemēri|Pavyzdžiai|Paraugs|Osnova scénáře|Osnova|Náčrt Scénáře|Náčrt Scenáru|Mate|MISHUN SRSLY|MISHUN|Kịch bản|Kontext|Konteksts|Kontekstas|Kontekst|Koncept|Khung tình huống|Khung kịch bản|Juhtumid|Háttér|Grundlage|Geçmiş|Forgatókönyv vázlat|Forgatókönyv|Exemplos|Exemples|Exemplele|Exempel|Examples|Esquema do Cenário|Esquema do Cenario|Esquema del escenario|Esquema de l\'escenari|Esempi|Escenario|Escenari|Enghreifftiau|Eksempler|Ejemplos|EXAMPLZ|Dữ liệu|Dasar|Contoh|Contexto|Contexte|Contesto|Condiţii|Conditii|Cobber|Cenário|Cenario|Cefndir|Bối cảnh|Blokes|Beispiele|Bakgrunn|Bakgrund|Baggrund|Background|B4|Antecedents|Antecedentes|All y\'all|Achtergrond|Abstrakt Scenario|Abstract Scenario):(.*) 147 | 148 | scenario_outline_variable 149 | 150 | begin 151 | < 152 | end 153 | > 154 | name 155 | variable.other 156 | 157 | strings_double_quote 158 | 159 | begin 160 | " 161 | end 162 | " 163 | name 164 | string.quoted.double 165 | patterns 166 | 167 | 168 | match 169 | \\. 170 | name 171 | constant.character.escape.untitled 172 | 173 | 174 | 175 | strings_single_quote 176 | 177 | begin 178 | (?<![a-zA-Z"])' 179 | end 180 | '(?![a-zA-Z]) 181 | name 182 | string.quoted.single 183 | patterns 184 | 185 | 186 | match 187 | \\. 188 | name 189 | constant.character.escape 190 | 191 | 192 | 193 | strings_triple_quote 194 | 195 | begin 196 | """ 197 | end 198 | """ 199 | name 200 | string.quoted.single 201 | 202 | tags 203 | 204 | captures 205 | 206 | 0 207 | 208 | name 209 | storage.type.tag.cucumber 210 | 211 | 212 | match 213 | (@[^@\r\n\t ]+) 214 | 215 | 216 | scopeName 217 | text.gherkin.feature 218 | uuid 219 | 85E2C52C-9B16-4A54-81E7-6D8D3ADAEFA8 220 | 221 | 222 | -------------------------------------------------------------------------------- /Syntaxes/Cucumber Steps.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | steps.rb 8 | 9 | keyEquivalent 10 | ^~C 11 | name 12 | Cucumber Steps 13 | patterns 14 | 15 | 16 | match 17 | \b(GivenScenario|Given|When|Then)\b 18 | name 19 | keyword.other.step.cucumber 20 | 21 | 22 | begin 23 | \b(?<=GivenScenario|Given|When|Then) (") 24 | captures 25 | 26 | 1 27 | 28 | name 29 | string.quoted.double.ruby 30 | 31 | 2 32 | 33 | name 34 | punctuation.definition.string.ruby 35 | 36 | 37 | comment 38 | string after a Cucumber keyword 39 | contentName 40 | string.quoted.step.cucumber.classic.ruby 41 | end 42 | ((\1)) 43 | patterns 44 | 45 | 46 | include 47 | #interpolated_ruby 48 | 49 | 50 | include 51 | #regex_sub 52 | 53 | 54 | 55 | 56 | begin 57 | \b(?<=GivenScenario|Given|When|Then) (') 58 | captures 59 | 60 | 1 61 | 62 | name 63 | string.quoted.single.ruby 64 | 65 | 2 66 | 67 | name 68 | punctuation.definition.string.ruby 69 | 70 | 71 | comment 72 | string after a Cucumber keyword 73 | contentName 74 | string.quoted.step.cucumber.classic.ruby 75 | end 76 | ((\1)) 77 | patterns 78 | 79 | 80 | include 81 | #regex_sub 82 | 83 | 84 | include 85 | #interpolated_ruby 86 | 87 | 88 | 89 | 90 | begin 91 | \b(?<=GivenScenario|Given|When|Then) (/) 92 | captures 93 | 94 | 1 95 | 96 | name 97 | string.regexp.classic.ruby 98 | 99 | 2 100 | 101 | name 102 | punctuation.definition.string.ruby 103 | 104 | 105 | comment 106 | regular expression after a Cucumber keyword 107 | contentName 108 | string.regexp.step.cucumber.classic.ruby 109 | end 110 | ((/[eimnosux]*)) 111 | patterns 112 | 113 | 114 | include 115 | #regex_sub 116 | 117 | 118 | 119 | 120 | begin 121 | \b(?<=GivenScenario|Given|When|Then) (%r{) 122 | captures 123 | 124 | 1 125 | 126 | name 127 | string.regexp.mod-r.ruby 128 | 129 | 2 130 | 131 | name 132 | punctuation.definition.string.ruby 133 | 134 | 135 | comment 136 | regular expression after a Cucumber keyword 137 | contentName 138 | string.regexp.step.cucumber.mod-r.ruby 139 | end 140 | ((}[eimnosux]*)) 141 | patterns 142 | 143 | 144 | include 145 | #regex_sub 146 | 147 | 148 | 149 | 150 | begin 151 | (?><<-CUCUMBER\b) 152 | beginCaptures 153 | 154 | 0 155 | 156 | name 157 | punctuation.definition.string.begin.ruby 158 | 159 | 160 | comment 161 | embedded Cucumber feature 162 | contentName 163 | text.cucumber.embedded.ruby 164 | end 165 | \s*CUCUMBER$ 166 | endCaptures 167 | 168 | 0 169 | 170 | name 171 | punctuation.definition.string.end.ruby 172 | 173 | 174 | name 175 | string.unquoted.embedded.cucumber.feature 176 | patterns 177 | 178 | 179 | include 180 | text.gherkin.feature 181 | 182 | 183 | 184 | 185 | include 186 | source.ruby 187 | 188 | 189 | repository 190 | 191 | escaped_char 192 | 193 | match 194 | \\(?:[0-7]{1,3}|x[\da-fA-F]{1,2}|.) 195 | name 196 | constant.character.escape.ruby 197 | 198 | interpolated_ruby 199 | 200 | patterns 201 | 202 | 203 | captures 204 | 205 | 0 206 | 207 | name 208 | punctuation.section.embedded.ruby 209 | 210 | 1 211 | 212 | name 213 | source.ruby.embedded.source.empty 214 | 215 | 216 | match 217 | #\{(\}) 218 | name 219 | source.ruby.embedded.source 220 | 221 | 222 | begin 223 | #\{ 224 | captures 225 | 226 | 0 227 | 228 | name 229 | punctuation.section.embedded.ruby 230 | 231 | 232 | end 233 | \} 234 | name 235 | source.ruby.embedded.source 236 | patterns 237 | 238 | 239 | include 240 | #nest_curly_and_self 241 | 242 | 243 | include 244 | source.ruby 245 | 246 | 247 | 248 | 249 | captures 250 | 251 | 1 252 | 253 | name 254 | punctuation.definition.variable.ruby 255 | 256 | 257 | match 258 | (#@)[a-zA-Z_]\w* 259 | name 260 | variable.other.readwrite.instance.ruby 261 | 262 | 263 | captures 264 | 265 | 1 266 | 267 | name 268 | punctuation.definition.variable.ruby 269 | 270 | 271 | match 272 | (#@@)[a-zA-Z_]\w* 273 | name 274 | variable.other.readwrite.class.ruby 275 | 276 | 277 | captures 278 | 279 | 1 280 | 281 | name 282 | punctuation.definition.variable.ruby 283 | 284 | 285 | match 286 | (#\$)[a-zA-Z_]\w* 287 | name 288 | variable.other.readwrite.global.ruby 289 | 290 | 291 | 292 | nest_curly_and_self 293 | 294 | patterns 295 | 296 | 297 | begin 298 | \{ 299 | captures 300 | 301 | 0 302 | 303 | name 304 | punctuation.section.scope.ruby 305 | 306 | 307 | end 308 | \} 309 | patterns 310 | 311 | 312 | include 313 | #nest_curly_and_self 314 | 315 | 316 | 317 | 318 | include 319 | source.ruby 320 | 321 | 322 | 323 | regex_sub 324 | 325 | patterns 326 | 327 | 328 | include 329 | #interpolated_ruby 330 | 331 | 332 | include 333 | #escaped_char 334 | 335 | 336 | captures 337 | 338 | 1 339 | 340 | name 341 | punctuation.definition.arbitrary-repitition.ruby 342 | 343 | 3 344 | 345 | name 346 | punctuation.definition.arbitrary-repitition.ruby 347 | 348 | 349 | match 350 | (\{)\d+(,\d+)?(\}) 351 | name 352 | string.regexp.arbitrary-repitition.ruby 353 | 354 | 355 | begin 356 | \[(?:\^?\])? 357 | captures 358 | 359 | 0 360 | 361 | name 362 | punctuation.definition.character-class.ruby 363 | 364 | 365 | end 366 | \] 367 | name 368 | string.regexp.character-class.ruby 369 | patterns 370 | 371 | 372 | include 373 | #escaped_char 374 | 375 | 376 | 377 | 378 | begin 379 | \( 380 | captures 381 | 382 | 0 383 | 384 | name 385 | punctuation.definition.group.ruby 386 | 387 | 388 | end 389 | \) 390 | name 391 | string.regexp.group.ruby 392 | patterns 393 | 394 | 395 | include 396 | #regex_sub 397 | 398 | 399 | 400 | 401 | captures 402 | 403 | 1 404 | 405 | name 406 | punctuation.definition.comment.ruby 407 | 408 | 409 | comment 410 | We are restrictive in what we allow to go after the comment character to avoid false positives, since the availability of comments depend on regexp flags. 411 | match 412 | (?<=^|\s)(#)\s[[a-zA-Z0-9,. \t?!-][^\x{00}-\x{7F}]]*$ 413 | name 414 | comment.line.number-sign.ruby 415 | 416 | 417 | 418 | 419 | scopeName 420 | source.ruby.rspec.cucumber.steps 421 | uuid 422 | B269B8F3-3A6D-4169-9E70-DD89A679416A 423 | 424 | 425 | -------------------------------------------------------------------------------- /Syntaxes/plaintext_template.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | feature 8 | 9 | firstLineMatch 10 | <%= Gherkin::I18n.keyword_regexp(:feature) %>(.*) 11 | foldingStartMarker 12 | ^\s*\b(<%= Gherkin::I18n.keyword_regexp(:background, :scenario, :scenario_outline, :examples) %>) 13 | foldingStopMarker 14 | ^\s*$ 15 | keyEquivalent 16 | ^~C 17 | name 18 | Gherkin 19 | patterns 20 | 21 | 22 | include 23 | #feature_element_keyword 24 | 25 | 26 | include 27 | #description 28 | 29 | 30 | include 31 | #feature_keyword 32 | 33 | 34 | include 35 | #step_keyword 36 | 37 | 38 | include 39 | #strings_triple_quote 40 | 41 | 42 | include 43 | #strings_single_quote 44 | 45 | 46 | include 47 | #strings_double_quote 48 | 49 | 50 | include 51 | #comments 52 | 53 | 54 | include 55 | #tags 56 | 57 | 58 | include 59 | #scenario_outline_variable 60 | 61 | 62 | include 63 | #table 64 | 65 | 66 | repository 67 | 68 | comments 69 | 70 | captures 71 | 72 | 0 73 | 74 | name 75 | comment.line.number-sign 76 | 77 | 78 | match 79 | \s*(#.*) 80 | 81 | table 82 | 83 | begin 84 | ^\s*\| 85 | end 86 | \|\s*$ 87 | name 88 | keyword.control.cucumber.table 89 | patterns 90 | 91 | 92 | match 93 | \w 94 | name 95 | source 96 | 97 | 98 | 99 | feature_keyword 100 | 101 | captures 102 | 103 | 1 104 | 105 | name 106 | keyword.language.gherkin.feature 107 | 108 | 2 109 | 110 | name 111 | string.language.gherkin.feature.title 112 | 113 | 114 | match 115 | ^\s*(<%= Gherkin::I18n.keyword_regexp(:feature) %>):(.*)\b 116 | 117 | step_keyword 118 | 119 | captures 120 | 121 | 1 122 | 123 | name 124 | keyword.language.gherkin.feature.step 125 | 126 | 127 | match 128 | ^\s*(<%= Gherkin::I18n.keyword_regexp(:step) %>) 129 | 130 | feature_element_keyword 131 | 132 | captures 133 | 134 | 1 135 | 136 | name 137 | keyword.language.gherkin.feature.scenario 138 | 139 | 2 140 | 141 | name 142 | string.language.gherkin.scenario.title.title 143 | 144 | 145 | match 146 | ^\s*(<%= Gherkin::I18n.keyword_regexp(:background, :scenario, :scenario_outline, :examples) %>):(.*) 147 | 148 | scenario_outline_variable 149 | 150 | begin 151 | < 152 | end 153 | > 154 | name 155 | variable.other 156 | 157 | strings_double_quote 158 | 159 | begin 160 | " 161 | end 162 | " 163 | name 164 | string.quoted.double 165 | patterns 166 | 167 | 168 | match 169 | \\. 170 | name 171 | constant.character.escape.untitled 172 | 173 | 174 | 175 | strings_single_quote 176 | 177 | begin 178 | (?<![a-zA-Z"])' 179 | end 180 | '(?![a-zA-Z]) 181 | name 182 | string.quoted.single 183 | patterns 184 | 185 | 186 | match 187 | \\. 188 | name 189 | constant.character.escape 190 | 191 | 192 | 193 | strings_triple_quote 194 | 195 | begin 196 | """ 197 | end 198 | """ 199 | name 200 | string.quoted.single 201 | 202 | tags 203 | 204 | captures 205 | 206 | 0 207 | 208 | name 209 | storage.type.tag.cucumber 210 | 211 | 212 | match 213 | (@[^@\r\n\t ]+) 214 | 215 | 216 | scopeName 217 | text.gherkin.feature 218 | uuid 219 | 85E2C52C-9B16-4A54-81E7-6D8D3ADAEFA8 220 | 221 | 222 | -------------------------------------------------------------------------------- /info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mainMenu 6 | 7 | items 8 | 9 | 1530D367-DCDF-4F89-8BDB-896FAA16830E 10 | E6FBAE52-8B70-47EE-8F22-3A58D4A088F9 11 | FEB590E8-C0CB-4DF2-95C9-3E7B56C7758E 12 | 6AD6154B-3EE8-43D0-B87F-6261C859AD07 13 | 2BCE4864-D70A-4C52-B49D-FB3220130B74 14 | 15 | submenus 16 | 17 | 18 | name 19 | Cucumber 20 | ordering 21 | 22 | 75E08730-A1D7-4D0F-B594-EAFD9DD49B4D 23 | 8B547854-6A6A-4E45-B51E-9324F0E9D364 24 | 90F0D309-13C4-4286-88DE-60F61DDEF094 25 | 0843B907-3EC2-4F51-A1F6-85EFDB88464B 26 | 41A25845-3142-47FE-A225-FDF453C1D59D 27 | E7E79650-2A87-44A0-8226-3113A94FCE53 28 | 85E2C52C-9B16-4A54-81E7-6D8D3ADAEFA8 29 | B269B8F3-3A6D-4169-9E70-DD89A679416A 30 | 31 | uuid 32 | 10DCDBF3-71B9-424F-AE10-17BFB8C4CD69 33 | 34 | 35 | --------------------------------------------------------------------------------