butternut 0.2.2-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ module Butternut
2
+ module Helpers
3
+ def browser
4
+ @browser ||= Celerity::Browser.new
5
+ end
6
+
7
+ def visit(url)
8
+ browser.goto(url)
9
+ @page_changed = true
10
+ end
11
+
12
+ def current_url
13
+ browser.page.web_response.request_url.to_string
14
+ end
15
+
16
+ def current_page_source
17
+ return nil unless browser.page
18
+
19
+ string_writer = java.io.StringWriter.new
20
+ print_writer = java.io.PrintWriter.new(string_writer)
21
+
22
+ root = browser.page.document_element
23
+ node_to_xml(root, print_writer)
24
+ print_writer.close
25
+ string_writer.to_string
26
+ end
27
+
28
+ # Fill in a text field with a value
29
+ def fill_in(label_or_name, options = {})
30
+ elt = find_element_by_label_or_name(:text_field, label_or_name)
31
+ if elt.exist?
32
+ elt.value = options[:with]
33
+ @page_changed = true
34
+ end
35
+ end
36
+
37
+ def select(option_text, options = {})
38
+ elt = find_element_by_label_or_name(:select_list, options[:from])
39
+ if elt.exist?
40
+ elt.select(option_text)
41
+ @page_changed = true
42
+ end
43
+ end
44
+
45
+ def click_button(button_value)
46
+ browser.button(button_value).click
47
+ @page_changed = true
48
+ end
49
+
50
+ def page_changed?
51
+ @page_changed
52
+ end
53
+
54
+ def find_element_by_label_or_name(type, label_or_name)
55
+ elt = browser.send(type, :label, label_or_name)
56
+ elt.exist? ? elt : browser.send(type, :name, label_or_name)
57
+ end
58
+
59
+ private
60
+ # NOTE: I have to do this because HtmlUnit's asXml likes to put spaces
61
+ # and newlines in the middle of whitespace-sensitive tags (like
62
+ # <a> and <textarea>). Fail.
63
+ def node_to_xml(node, print_writer)
64
+ closing_tag = false
65
+
66
+ case node
67
+ when HtmlUnit::Html::DomText
68
+ # just print out the text
69
+ print_writer.write(HtmlUnit::Util::StringUtils.escape_xml_chars(node.data))
70
+ when HtmlUnit::Html::DomCDataSection, HtmlUnit::Html::DomProcessingInstruction
71
+ # use default printXml here
72
+ node.print_xml("", print_writer)
73
+ when HtmlUnit::Html::DomComment
74
+ print_writer.write("<!-- #{node.data} -->")
75
+ when HtmlUnit::Html::HtmlTextArea
76
+ node_print_opening_tag(node, print_writer)
77
+ print_writer.write(node.text)
78
+ closing_tag = true
79
+ else
80
+ node_print_opening_tag(node, print_writer)
81
+ closing_tag = true
82
+ end
83
+
84
+ child = node.first_child
85
+ while child
86
+ node_to_xml(child, print_writer)
87
+ child = child.next_sibling
88
+ end
89
+
90
+ node_print_closing_tag(node, print_writer) if closing_tag
91
+ end
92
+
93
+ def node_print_opening_tag(node, print_writer)
94
+ print_writer.write("<")
95
+ node.print_opening_tag_content_as_xml(print_writer)
96
+ print_writer.write(">")
97
+ end
98
+
99
+ def node_print_closing_tag(node, print_writer)
100
+ print_writer.write("</#{node.tag_name}>")
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,7 @@
1
+ module Butternut
2
+ module ScenarioExtensions
3
+ attr_accessor :last_page_source, :last_page_url
4
+ end
5
+ end
6
+
7
+ Cucumber::Ast::Scenario.send(:include, Butternut::ScenarioExtensions)
data/lib/butternut.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'cucumber'
3
+ require 'celerity'
4
+
5
+ module Butternut
6
+ def self.setup_hooks(obj)
7
+ obj.instance_exec do
8
+ AfterStep do |object|
9
+ if object.is_a?(Cucumber::Ast::Scenario)
10
+ if page_changed?
11
+ object.last_page_source = current_page_source
12
+ object.last_page_url = current_url
13
+ else
14
+ object.last_page_source = nil
15
+ object.last_page_url = nil
16
+ end
17
+ @page_changed = false
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ require File.dirname(__FILE__) + "/butternut/scenario_extensions"
25
+ require File.dirname(__FILE__) + "/butternut/helpers"
26
+ require File.dirname(__FILE__) + "/butternut/formatter"
27
+
@@ -0,0 +1,146 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Butternut
4
+ describe Formatter do
5
+ extend SpecHelperDsl
6
+ include SpecHelper
7
+
8
+ def setup_formatter(options = {})
9
+ @out = StringIO.new
10
+ @formatter = Butternut::Formatter.new(step_mother, @out, options)
11
+ end
12
+
13
+ def most_recent_html_file(dir)
14
+ path = Pathname.new(dir)
15
+ files = path.entries.collect { |file|
16
+ path+file
17
+ }.sort { |file1,file2|
18
+ file2.mtime <=> file1.mtime
19
+ }
20
+ files.detect { |f| f.to_s =~ /\.html$/ }
21
+ end
22
+
23
+ it "should be a subclass of the html formatter" do
24
+ Butternut::Formatter.superclass.should == Cucumber::Formatter::Html
25
+ end
26
+
27
+ describe "running without the --out option" do
28
+ define_steps do
29
+ Given(/foo/) do
30
+ visit("file://" + File.expand_path(File.dirname(__FILE__) + "/../fixtures/foo.html"))
31
+ end
32
+ end
33
+
34
+ define_feature(<<-FEATURE)
35
+ Scenario: Monkey goes to the zoo
36
+ Given foo
37
+ FEATURE
38
+
39
+ it "should raise an error" do
40
+ lambda {
41
+ setup_formatter
42
+ run_defined_feature
43
+ }.should raise_error
44
+ end
45
+ end
46
+
47
+ describe "running with the --out option" do
48
+ before(:each) do
49
+ dir = File.join(File.dirname(__FILE__), "..", "..", "tmp")
50
+ @tmp_dir = File.join(dir, "#{Time.now.to_i}-#{rand(1000)}")
51
+ FileUtils.mkdir(@tmp_dir)
52
+
53
+ #@tmp_dir = File.join(dir, "features", Date.today.to_s)
54
+ #file = most_recent_html_file(@tmp_dir)
55
+ #@page_doc = Nokogiri.HTML(open(file).read)
56
+ end
57
+
58
+ describe "with a filename specified" do
59
+ define_steps do
60
+ Given(/foo/) do
61
+ visit("file://" + File.join(FIXTURE_DIR, "foo.html"))
62
+ end
63
+ end
64
+
65
+ define_feature(<<-FEATURE)
66
+ Scenario: Monkey goes to the zoo
67
+ Given foo
68
+ Then bar
69
+ FEATURE
70
+
71
+ before(:each) do
72
+ setup_formatter({
73
+ :formats => [
74
+ ['Butternut::Formatter', File.join(@tmp_dir, "output.html")]
75
+ ]
76
+ })
77
+ run_defined_feature
78
+ @doc = Nokogiri.HTML(@out.string)
79
+
80
+ file = most_recent_html_file(File.join(@tmp_dir, "output"))
81
+ @page_doc = Nokogiri.HTML(open(file).read)
82
+ end
83
+
84
+ it "creates assets directory" do
85
+ File.join(@tmp_dir, "output").should be_an_existing_directory
86
+ end
87
+
88
+ it "links to the page source" do
89
+ step = @doc.at('.feature .scenario .step.passed')
90
+ link = step.at("a")
91
+ link.should_not be_nil
92
+ file = link['href']
93
+ file.should match(%r{^output/butternut.+\.html})
94
+ end
95
+
96
+ it "saves images and stylesheets and rewrites urls in page source" do
97
+ @page_doc.at('img:nth(1)')['src'].should == "picard.jpg"
98
+ File.join(@tmp_dir, "output", "picard.jpg").should be_an_existing_file
99
+
100
+ @page_doc.at('link:nth(1)[rel="stylesheet"]')['href'].should == "foo.css"
101
+ File.join(@tmp_dir, "output", "foo.css").should be_an_existing_file
102
+
103
+ @page_doc.at('link:nth(2)[rel="stylesheet"]')['href'].should == "bar.css"
104
+ File.join(@tmp_dir, "output", "bar.css").should be_an_existing_file
105
+ end
106
+
107
+ it "saves assets and rewrites urls referred to by stylesheets" do
108
+ foo = open(File.join(@tmp_dir, "output", "foo.css")).read
109
+ foo.should include("url(facepalm.jpg)")
110
+ File.join(@tmp_dir, "output", "facepalm.jpg").should be_an_existing_file
111
+ end
112
+
113
+ it "turns off links" do
114
+ link = @page_doc.at('a')
115
+ link['href'].should == "#"
116
+ link.inner_html.should == "Picard song"
117
+ end
118
+
119
+ it "turns off scripts" do
120
+ @page_doc.css('script').length.should == 0
121
+ end
122
+
123
+ it "disables form elements" do
124
+ @page_doc.css('input, select, textarea').each do |elt|
125
+ elt['disabled'].should == "disabled"
126
+ end
127
+ end
128
+
129
+ it "handles Errno::ENOENT" do
130
+ @page_doc.at('img:nth(2)')['src'].should == "/roflpwnage/missing_file_omg.gif"
131
+ end
132
+
133
+ it "handles OpenURI::HTTPError" do
134
+ @page_doc.at('img:nth(3)')['src'].should == "http://google.com/missing_file_omg.gif"
135
+ end
136
+
137
+ it "handles Net::FTPPermError" do
138
+ @page_doc.at('img:nth(4)')['src'].should == "ftp://mirror.anl.gov/missing_file_omg.gif"
139
+ end
140
+
141
+ it "handles badly formed URI's" do
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,132 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Butternut
4
+ describe Helpers do
5
+ include Helpers
6
+
7
+ def define_stub_chain
8
+ # This is what I hate about RSpec.
9
+ @stub_request_url = stub("fake request url", :to_string => "http://example.com")
10
+ @stub_response = stub("fake web response", :request_url => @stub_request_url)
11
+ @stub_page = stub("fake html page", {
12
+ :as_xml => "<cheese>pepperjack</cheese>",
13
+ :web_response => @stub_response
14
+ })
15
+ @stub_element = stub("fake element", :value= => nil, :select => nil, :click => nil, :exist? => true)
16
+ @stub_empty = stub("fake empty element", :exist? => false)
17
+
18
+ @stub_browser = stub("fake celerity browser", {
19
+ :goto => @stub_page, :page => @stub_page,
20
+ :text_field => @stub_element, :select_list => @stub_element,
21
+ :button => @stub_element
22
+ })
23
+ stub!(:browser).and_return(@stub_browser)
24
+ end
25
+
26
+ describe "#browser" do
27
+ it { browser.should be_a(Celerity::Browser) }
28
+ end
29
+
30
+ describe "#page_changed?" do
31
+ it { page_changed?.should_not be_true }
32
+ end
33
+
34
+ describe "#visit" do
35
+ before(:each) { define_stub_chain }
36
+
37
+ it "should go to the page" do
38
+ @stub_browser.should_receive(:goto).with("http://google.com")
39
+ visit("http://google.com")
40
+ end
41
+
42
+ it "should flag page as changed" do
43
+ visit("http://google.com")
44
+ page_changed?.should be_true
45
+ end
46
+ end
47
+
48
+ describe "#current_url" do
49
+ before(:each) { define_stub_chain }
50
+ it do
51
+ @stub_request_url.should_receive(:to_string).and_return("http://google.com")
52
+ current_url.should == "http://google.com"
53
+ end
54
+ end
55
+
56
+ describe "#current_page_source" do
57
+ before(:each) do
58
+ @browser = browser
59
+ visit("file://" + File.expand_path(File.dirname(__FILE__) + "/../fixtures/blargh.html"))
60
+ end
61
+
62
+ it "constructs the current page's source" do
63
+ # HtmlUnit's text node parsing it a little strange
64
+ expected = "<html><head>\n <title>Blargh</title>\n </head><body>\n <p>Foo</p>\n <p>Bar</p>\n \n</body></html>"
65
+ current_page_source.should == expected
66
+ end
67
+
68
+ it "returns nil if page is nil" do
69
+ @browser.stub!(:page).and_return(nil)
70
+ current_page_source.should be_nil
71
+ end
72
+ end
73
+
74
+ describe "#fill_in" do
75
+ before(:each) { define_stub_chain }
76
+
77
+ it "should find by label" do
78
+ @stub_browser.should_receive(:text_field).with(:label, "pants").and_return(@stub_element)
79
+ @stub_element.should_receive(:value=).with("khakis")
80
+ fill_in("pants", :with => "khakis")
81
+ end
82
+
83
+ it "should find by name" do
84
+ @stub_browser.should_receive(:text_field).with(:label, "pants").and_return(@stub_empty)
85
+ @stub_browser.should_receive(:text_field).with(:name, "pants").and_return(@stub_element)
86
+ @stub_element.should_receive(:value=).with("khakis")
87
+ fill_in("pants", :with => "khakis")
88
+ end
89
+
90
+ it "should flag page as changed" do
91
+ fill_in("pants", :with => "khakis")
92
+ page_changed?.should be_true
93
+ end
94
+ end
95
+
96
+ describe "#select" do
97
+ before(:each) { define_stub_chain }
98
+
99
+ it "should find by label" do
100
+ @stub_browser.should_receive(:select_list).with(:label, "pants").and_return(@stub_element)
101
+ @stub_element.should_receive(:select).with("khakis")
102
+ select("khakis", :from => "pants")
103
+ end
104
+
105
+ it "should find by name" do
106
+ @stub_browser.should_receive(:select_list).with(:label, "pants").and_return(@stub_empty)
107
+ @stub_browser.should_receive(:select_list).with(:name, "pants").and_return(@stub_element)
108
+ @stub_element.should_receive(:select).with("khakis")
109
+ select("khakis", :from => "pants")
110
+ end
111
+
112
+ it "should flag page as changed" do
113
+ select("khakis", :from => "pants")
114
+ page_changed?.should be_true
115
+ end
116
+ end
117
+
118
+ describe "#click_button" do
119
+ before(:each) { define_stub_chain }
120
+ it do
121
+ @stub_browser.should_receive(:button).with("pants").and_return(@stub_element)
122
+ @stub_element.should_receive(:click)
123
+ click_button("pants")
124
+ end
125
+
126
+ it "should flag page as changed" do
127
+ click_button("pants")
128
+ page_changed?.should be_true
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Butternut do
4
+ describe "running a scenario" do
5
+ extend SpecHelperDsl
6
+ include SpecHelper
7
+
8
+ describe "saving last page info" do
9
+ define_steps do
10
+ Given('waffles') do
11
+ visit("file://" + File.expand_path(File.dirname(__FILE__) + "/fixtures/foo.html"))
12
+ end
13
+ AfterStep do |scenario|
14
+ begin
15
+ scenario.last_page_source.should match(/Foo/)
16
+ scenario.last_page_url.should match(/foo\.html/)
17
+ rescue Exception => e
18
+ p e
19
+ end
20
+ end
21
+ end
22
+
23
+ define_feature <<-FEATURE
24
+ Scenario: Roffle waffles
25
+ Given waffles
26
+ FEATURE
27
+
28
+ it { run_defined_feature }
29
+ end
30
+
31
+ describe "resetting page_changed" do
32
+ define_steps do
33
+ Given('waffles') do
34
+ visit("file://" + File.expand_path(File.dirname(__FILE__) + "/fixtures/foo.txt"))
35
+ end
36
+ AfterStep do |scenario|
37
+ begin
38
+ page_changed?.should be_false
39
+ rescue Exception => e
40
+ p e
41
+ end
42
+ end
43
+ end
44
+
45
+ define_feature <<-FEATURE
46
+ Scenario: Roffle waffles
47
+ Given waffles
48
+ FEATURE
49
+
50
+ it { run_defined_feature }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <title>Blargh</title>
4
+ </head>
5
+ <body>
6
+ <p>Foo</p>
7
+ <p>Bar</p>
8
+ </body>
9
+ </html>
@@ -0,0 +1,3 @@
1
+ h1 {
2
+ font-size: 2em;
3
+ }
Binary file
@@ -0,0 +1,9 @@
1
+ body {
2
+ font-family: Verdana;
3
+ }
4
+
5
+ .facepalm {
6
+ width: 750px;
7
+ height: 600px;
8
+ background: url(../fixtures/facepalm.jpg);
9
+ }
@@ -0,0 +1,27 @@
1
+ <html>
2
+ <head>
3
+ <title>Foo</title>
4
+ <link rel="stylesheet" href="foo.css" type="text/css"/>
5
+ <link rel="stylesheet" href="css/bar.css" type="text/css"/>
6
+ <script type="text/javascript" src="foo.js"></script>
7
+ </head>
8
+ <body>
9
+ Foo
10
+ <img src="picard.jpg"/>
11
+ <a href="http://picard.ytmnd.com/">Picard song</a>
12
+ <form action="foo.html" method="post">
13
+ <input type="text" name="foo" />
14
+ <select name="bar">
15
+ <option>pants</option>
16
+ </select>
17
+ <textarea name="yar"></textarea>
18
+ <input type="submit" value="Submit" />
19
+ </form>
20
+ <img src="/roflpwnage/missing_file_omg.gif"/>
21
+ <img src="http://google.com/missing_file_omg.gif"/>
22
+ <img src="ftp://mirror.anl.gov/missing_file_omg.gif"/>
23
+ <img src="//baduri/pants.png"/>
24
+
25
+ <div class="facepalm">&nbsp;</div>
26
+ </body>
27
+ </html>
@@ -0,0 +1,3 @@
1
+ function picard() {
2
+ alert('Make it so!');
3
+ }
Binary file
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,97 @@
1
+ require 'rubygems'
2
+ gem 'rspec'
3
+ require 'spec'
4
+ require 'spec/autorun'
5
+ require 'fileutils'
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+ require 'butternut'
10
+
11
+ require 'cucumber/rb_support/rb_language'
12
+ require 'nokogiri'
13
+
14
+ module SpecHelperDsl
15
+ attr_reader :feature_content, :step_defs
16
+
17
+ def define_feature(string)
18
+ @feature_content = string
19
+ end
20
+
21
+ def define_steps(&block)
22
+ @step_defs = block
23
+ end
24
+ end
25
+
26
+ module SpecHelper
27
+ def run_defined_feature
28
+ setup_world
29
+ define_steps
30
+ features = load_features(self.class.feature_content || raise("No feature content defined!"))
31
+ run(features)
32
+ end
33
+
34
+ def step_mother
35
+ @step_mother ||= Cucumber::StepMother.new
36
+ end
37
+
38
+ def load_features(content)
39
+ feature_file = Cucumber::FeatureFile.new('spec.feature', content)
40
+ features = Cucumber::Ast::Features.new
41
+ features.add_feature feature_file.parse(step_mother, {})
42
+ features
43
+ end
44
+
45
+ def run(features)
46
+ # options = { :verbose => true }
47
+ options = {}
48
+ tree_walker = Cucumber::Ast::TreeWalker.new(step_mother, @formatter ? [@formatter] : [], options, STDOUT)
49
+ tree_walker.visit_features(features)
50
+ end
51
+
52
+ def dsl
53
+ unless @dsl
54
+ rb = step_mother.load_programming_language('rb')
55
+ @dsl = Object.new
56
+ @dsl.extend Cucumber::RbSupport::RbDsl
57
+ end
58
+ @dsl
59
+ end
60
+
61
+ def define_steps
62
+ return unless step_defs = self.class.step_defs
63
+ dsl.instance_exec &step_defs
64
+ end
65
+
66
+ def setup_world
67
+ dsl.instance_exec do
68
+ Butternut.setup_hooks(self)
69
+ World(Butternut::Helpers)
70
+ end
71
+ end
72
+ end
73
+
74
+ FIXTURE_DIR = File.expand_path(File.join(File.dirname(__FILE__), "fixtures"))
75
+
76
+ Spec::Matchers.define :be_an_existing_file do
77
+ match { |filename| File.exist?(filename) }
78
+ end
79
+
80
+ Spec::Matchers.define :be_an_existing_directory do
81
+ match { |filename| File.directory?(filename) }
82
+ end
83
+
84
+ Spec::Matchers.define :match_content_of do |expected|
85
+ match do |actual|
86
+ raise "expected file doesn't exist" unless File.exist?(expected)
87
+ raise "actual file doesn't exist" unless File.exist?(actual)
88
+ open(expected).read == open(actual).read
89
+ end
90
+ end
91
+
92
+
93
+ Spec::Runner.configure do |config|
94
+ config.before(:each) do
95
+ Cucumber::Parser::NaturalLanguage.instance_variable_set(:@languages, nil)
96
+ end
97
+ end
data/tmp/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *
2
+ !.gitignore