taza 0.5.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/History.txt +33 -0
  2. data/Manifest.txt +39 -9
  3. data/README.txt +29 -7
  4. data/Rakefile +72 -13
  5. data/bin/taza +16 -3
  6. data/generators/flow/flow_generator.rb +57 -0
  7. data/generators/flow/templates/flow.rb.erb +12 -0
  8. data/generators/page/page_generator.rb +58 -0
  9. data/generators/page/templates/functional_page_spec.rb.erb +8 -0
  10. data/generators/page/templates/page.rb.erb +8 -0
  11. data/generators/site/site_generator.rb +55 -0
  12. data/generators/site/templates/site.rb.erb +10 -0
  13. data/generators/site/templates/site.yml.erb +3 -0
  14. data/lib/app_generators/taza/taza_generator.rb +76 -0
  15. data/lib/app_generators/taza/templates/config.yml.erb +3 -0
  16. data/lib/{taza/generators → app_generators/taza}/templates/rakefile.rb.erb +0 -0
  17. data/lib/app_generators/taza/templates/spec_helper.rb.erb +11 -0
  18. data/lib/taza.rb +44 -5
  19. data/lib/taza/browser.rb +40 -0
  20. data/lib/taza/browsers/ie_watir.rb +8 -0
  21. data/lib/taza/browsers/safari_watir.rb +8 -0
  22. data/lib/taza/flow.rb +45 -0
  23. data/lib/taza/page.rb +66 -19
  24. data/lib/taza/settings.rb +36 -0
  25. data/lib/taza/site.rb +122 -11
  26. data/lib/taza/tasks.rb +26 -34
  27. data/spec/browser_spec.rb +72 -0
  28. data/spec/flow_generator_spec.rb +70 -0
  29. data/spec/page_generator_spec.rb +57 -0
  30. data/spec/page_spec.rb +82 -0
  31. data/spec/platform/osx/browser_spec.rb +14 -0
  32. data/spec/platform/windows/browser_spec.rb +14 -0
  33. data/spec/project_generator_spec.rb +42 -0
  34. data/spec/sandbox/config.yml +3 -0
  35. data/spec/sandbox/config/config.yml +1 -0
  36. data/spec/sandbox/config/site_name.yml +5 -0
  37. data/spec/sandbox/flows/batman.rb +2 -0
  38. data/spec/sandbox/flows/robin.rb +4 -0
  39. data/spec/sandbox/pages/foo/bar.rb +9 -0
  40. data/spec/settings_spec.rb +88 -0
  41. data/spec/site_generator_spec.rb +53 -0
  42. data/spec/site_spec.rb +239 -0
  43. data/spec/spec_helper.rb +49 -0
  44. data/spec/taza_bin_spec.rb +14 -0
  45. data/spec/taza_spec.rb +12 -0
  46. data/spec/taza_tasks_spec.rb +27 -0
  47. data/spec/unit_helper_spec.rb +14 -0
  48. metadata +103 -13
  49. data/lib/taza/generators.rb +0 -4
  50. data/lib/taza/generators/base.rb +0 -33
  51. data/lib/taza/generators/page.rb +0 -22
  52. data/lib/taza/generators/project.rb +0 -18
  53. data/lib/taza/generators/site.rb +0 -24
  54. data/lib/taza/generators/templates/page.rb.erb +0 -6
  55. data/lib/taza/generators/templates/site.rb.erb +0 -6
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rubigen'
3
+ require 'activesupport'
4
+
5
+ class SiteGenerator < RubiGen::Base
6
+ default_options :author => nil
7
+ attr_reader :name
8
+
9
+ def initialize(runtime_args, runtime_options = {})
10
+ super
11
+ usage if args.empty?
12
+ @name = args.shift
13
+ extract_options
14
+ end
15
+
16
+ def manifest
17
+ record do |m|
18
+ site_path = File.join('lib','sites')
19
+ m.template "site.rb.erb", File.join(site_path,"#{name.underscore}.rb")
20
+ m.directory File.join(site_path,"#{name.underscore}")
21
+ m.directory File.join(site_path,("#{name.underscore}"),"flows")
22
+ m.directory File.join(site_path,("#{name.underscore}"),"pages")
23
+ m.directory File.join('spec','functional',name.underscore)
24
+ m.template "site.yml.erb", File.join('config',"#{name.underscore}.yml")
25
+ end
26
+ end
27
+
28
+ protected
29
+ def banner
30
+ <<-EOS
31
+ Creates a taza site.
32
+
33
+ USAGE: #{$0} #{spec.name} name
34
+ EOS
35
+ end
36
+
37
+ def add_options!(opts)
38
+ # opts.separator ''
39
+ # opts.separator 'Options:'
40
+ # For each option below, place the default
41
+ # at the top of the file next to "default_options"
42
+ # opts.on("-a", "--author=\"Your Name\"", String,
43
+ # "Some comment about this option",
44
+ # "Default: none") { |options[:author]| }
45
+ # opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
46
+ end
47
+
48
+ def extract_options
49
+ # for each option, extract it into a local variable (and create an "attr_reader :author" at the top)
50
+ # Templates can access these value via the attr_reader-generated methods, but not the
51
+ # raw instance variable value.
52
+ # @author = options[:author]
53
+ end
54
+
55
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'taza'
3
+
4
+ module <%= name.camelize %>
5
+ include ForwardInitialization
6
+
7
+ class <%= name.camelize %> < ::Taza::Site
8
+
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ ---
2
+ isolation:
3
+ :url: http://www.google.com
@@ -0,0 +1,76 @@
1
+ require 'rubygems'
2
+ require 'rubigen'
3
+ class TazaGenerator < RubiGen::Base
4
+ DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
5
+ Config::CONFIG['ruby_install_name'])
6
+
7
+ default_options :author => nil
8
+
9
+ attr_reader :name
10
+
11
+ def initialize(runtime_args, runtime_options = {})
12
+ super
13
+ usage if args.empty?
14
+ @destination_root = File.expand_path(args.shift)
15
+ @name = base_name
16
+ extract_options
17
+ end
18
+
19
+ def manifest
20
+ record do |m|
21
+ # Ensure appropriate folder(s) exists
22
+
23
+ BASEDIRS.each { |path| m.directory path }
24
+ m.directory File.join('lib','sites')
25
+ m.directory File.join('lib','flows')
26
+ m.directory File.join('spec','functional')
27
+ m.directory File.join('spec','integration')
28
+
29
+ m.template "rakefile.rb.erb", "rakefile"
30
+ m.template "config.yml.erb", File.join("config","config.yml")
31
+ m.template "spec_helper.rb.erb", File.join("spec","spec_helper.rb")
32
+
33
+ # Create stubs
34
+ # m.template_copy_each ["template.rb", "template2.rb"]
35
+ # m.file "file", "some_file_copied"
36
+ # m.file_copy_each ["path/to/file", "path/to/file2"]
37
+
38
+ m.dependency "install_rubigen_scripts", [destination_root, 'taza'],
39
+ :shebang => options[:shebang], :collision => :force
40
+ end
41
+ end
42
+
43
+ protected
44
+ def banner
45
+ <<-EOS
46
+ USAGE: #{spec.name} path/for/your/test/project [options]
47
+ EOS
48
+ end
49
+
50
+ def add_options!(opts)
51
+ opts.separator ''
52
+ opts.separator 'Options:'
53
+ # For each option below, place the default
54
+ # at the top of the file next to "default_options"
55
+ # opts.on("-a", "--author=\"Your Name\"", String,
56
+ # "Some comment about this option",
57
+ # "Default: none") { |options[:author]| }
58
+ opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
59
+ end
60
+
61
+ def extract_options
62
+ # for each option, extract it into a local variable (and create an "attr_reader :author" at the top)
63
+ # Templates can access these value via the attr_reader-generated methods, but not the
64
+ # raw instance variable value.
65
+ # @author = options[:author]
66
+ end
67
+
68
+ # Installation skeleton. Intermediate directories are automatically
69
+ # created so don't sweat their absence here.
70
+ BASEDIRS = %w(
71
+ lib
72
+ config
73
+ script
74
+ spec
75
+ )
76
+ end
@@ -0,0 +1,3 @@
1
+ ---
2
+ :browser: :firefox
3
+ :driver: :selenium
@@ -0,0 +1,11 @@
1
+ ENV['TAZA_ENV'] = "isolation" if ENV['TAZA_ENV'].nil?
2
+ require 'rubygems'
3
+ require 'spec'
4
+ require 'mocha'
5
+
6
+ lib_path = File.expand_path("#{File.dirname(__FILE__)}/../lib/sites")
7
+ $LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path)
8
+
9
+ Spec::Runner.configure do |config|
10
+ config.mock_with :mocha
11
+ end
data/lib/taza.rb CHANGED
@@ -1,13 +1,52 @@
1
- require 'taza/generators'
2
1
  require 'taza/page'
3
2
  require 'taza/site'
4
- require 'taza/tasks'
3
+ require 'taza/browser'
4
+ require 'taza/settings'
5
+ require 'taza/flow'
5
6
 
6
7
  module Taza
7
- VERSION = '0.5.0'
8
-
8
+ VERSION = '0.8.0'
9
+
9
10
  def self.windows?
10
11
  PLATFORM.include?("mswin")
11
12
  end
12
-
13
+ def self.osx?
14
+ PLATFORM.include?("darwin")
15
+ end
16
+ end
17
+
18
+ module ForwardInitialization
19
+ module ClassMethods
20
+ def new(*args,&block)
21
+ const_get("#{name.split("::").last}").new(*args,&block)
22
+ end
23
+ end
24
+
25
+ def self.included(klass)
26
+ klass.extend(ClassMethods)
27
+ end
28
+ end
29
+
30
+ # instance_exec comes with >1.8.7 thankfully
31
+ if VERSION <= '1.8.6'
32
+ class Object
33
+ module InstanceExecHelper; end
34
+ include InstanceExecHelper
35
+ def instance_exec(*args, &block)
36
+ begin
37
+ old_critical, Thread.critical = Thread.critical, true
38
+ n = 0
39
+ n += 1 while respond_to?(mname="__instance_exec#{n}")
40
+ InstanceExecHelper.module_eval{ define_method(mname, &block) }
41
+ ensure
42
+ Thread.critical = old_critical
43
+ end
44
+ begin
45
+ ret = send(mname, *args)
46
+ ensure
47
+ InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
48
+ end
49
+ ret
50
+ end
51
+ end
13
52
  end
@@ -0,0 +1,40 @@
1
+ require 'taza/browsers/ie_watir'
2
+ require 'taza/browsers/safari_watir'
3
+
4
+ module Taza
5
+ class Browser
6
+
7
+ # Create a browser instance depending on configuration. Configuration should be read in via Taza::Settings.config.
8
+ #
9
+ # Example:
10
+ # browser = Taza::Browser.create(Taza::Settings.config)
11
+ #
12
+ def self.create(params={})
13
+ self.send("create_#{params[:driver]}".to_sym,params)
14
+ end
15
+
16
+
17
+ private
18
+
19
+ def self.create_watir(params)
20
+ method = "create_watir_#{params[:browser]}"
21
+ raise BrowserUnsupportedError unless self.respond_to?(method)
22
+ watir = self.send(method)
23
+ watir
24
+ end
25
+
26
+ def self.create_selenium(params)
27
+ require 'selenium'
28
+ Selenium::SeleniumDriver.new(params[:server_ip],params[:server_port],'*' + params[:browser].to_s,params[:timeout])
29
+ end
30
+
31
+ def self.create_watir_firefox
32
+ require 'firewatir'
33
+ FireWatir::Firefox.new()
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+ class BrowserUnsupportedError < StandardError
40
+ end
@@ -0,0 +1,8 @@
1
+ module Taza
2
+ class Browser
3
+ def self.create_watir_ie
4
+ require 'watir'
5
+ Watir::IE.new
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Taza
2
+ class Browser
3
+ def self.create_watir_safari
4
+ require 'safariwatir'
5
+ Watir::Safari.new
6
+ end
7
+ end
8
+ end
data/lib/taza/flow.rb ADDED
@@ -0,0 +1,45 @@
1
+ module Taza
2
+ # Flows provide a way to write and manage common actions on a site.
3
+ # For instance, on an e-commerce site you may have multiple tests where a user is supposed to create a
4
+ # new account and add a product to the shopping bag. In this case you could have two flows. create_an_account
5
+ # and add_product_to_bag.
6
+ #
7
+ # Here's how you would get this started where your site is called Widgets of the Future:
8
+ # $ ./script/generate flow create_an_account widgets_of_the_future
9
+ # $ ./script/generate flow add_product_to_bag widgets_of_the_future
10
+ #
11
+ # This will generate flows for you in lib/sites/widgets_of_the_future/flows/
12
+ #
13
+ # From here you can create the logic needed to perform these flows without ever referencing a browser object:
14
+ #
15
+ # class CreateAnAccount < Taza::Flow
16
+ # alias :widgets_of_the_future :site
17
+ #
18
+ # def run(params={})
19
+ # widgets_of_the_future.home_page.create_an_account_link.click
20
+ # widgets_of_the_future.create_an_account_page do |cap|
21
+ # cap.email.set params[:email]
22
+ # cap.password.set params[:password]
23
+ # cap.submit.click
24
+ # end
25
+ # end
26
+ # end
27
+ #
28
+ #
29
+ # Then inside a spec or test you could run this flow like:
30
+ #
31
+ # describe "Widgets of the Future" do
32
+ # it "should do widgety things so that we can make more monies" do
33
+ # WidgetsOfTheFuture.new do |w|
34
+ # w.flow :create_an_account, :email => "i.am@the.widget.store.com", :password => "secret"
35
+ # end
36
+ # end
37
+ # end
38
+ class Flow
39
+ attr_reader :site
40
+
41
+ def initialize(site_instance)
42
+ @site = site_instance
43
+ end
44
+ end
45
+ end
data/lib/taza/page.rb CHANGED
@@ -1,21 +1,66 @@
1
1
  module Taza
2
+ # An abstraction of a web page, place the elements you care about accessing in here as well as specify the filters that apply when trying to access the element.
3
+ #
4
+ # Example:
5
+ # require 'taza'
6
+ # class HomePage < Taza::Page
7
+ # element(:foo) {browser.element_by_xpath('some xpath')}
8
+ # filter :title_given, :foo
9
+ #
10
+ # def title_given
11
+ # browser.title.nil?
12
+ # end
13
+ # end
14
+ #
15
+ # homepage.foo will return the element specified in the block if the filter returned true
2
16
  class Page
17
+ attr_accessor :browser
3
18
  class << self
4
- attr_accessor :filters, :elements
19
+ def elements # :nodoc:
20
+ @elements ||= {}
21
+ end
22
+ def filters # :nodoc:
23
+ @filters ||= Hash.new { [] }
24
+ end
5
25
  end
6
26
 
7
- self.filters = Hash.new { [] }
8
- self.elements = {}
9
-
27
+ # A element on a page
28
+ #
29
+ # Watir Example:
30
+ # class HomePage < Taza::Page
31
+ # element(:foo) {browser.element_by_xpath('some xpath')}
32
+ # end
33
+ # homepage.foo.click
10
34
  def self.element(name,&block)
11
35
  self.elements[name] = block
12
36
  end
13
37
 
14
- def self.filter(params,&block)
15
- params[:elements] = [:all] unless params.has_key? :elements
16
-
17
- params[:elements].each do |element|
18
- self.filters[element] = self.filters[element] << [params[:name],block]
38
+ # A filter for elemenet(s) on a page
39
+ # Example:
40
+ # class HomePage < Taza::Page
41
+ # element(:foo) {browser.element_by_xpath('some xpath')}
42
+ # filter :title_given, :foo
43
+ # #a filter will apply to all elements if none are specified
44
+ # filter :some_filter
45
+ # #a filter will also apply to all elements if the symbol :all is given
46
+ # filter :another_filter, :all
47
+ #
48
+ # def some_filter
49
+ # true
50
+ # end
51
+ #
52
+ # def some_filter
53
+ # true
54
+ # end
55
+ #
56
+ # def title_given
57
+ # browser.title.nil?
58
+ # end
59
+ # end
60
+ def self.filter(method_name, *elements)
61
+ elements = [:all] if elements.empty?
62
+ elements.each do |element|
63
+ self.filters[element] = self.filters[element] << method_name
19
64
  end
20
65
  end
21
66
 
@@ -23,25 +68,27 @@ module Taza
23
68
  add_element_methods
24
69
  end
25
70
 
26
- def add_element_methods
71
+ def add_element_methods # :nodoc:
27
72
  self.class.elements.each do |element_name,element_block|
28
73
  filters = self.class.filters[element_name] + self.class.filters[:all]
29
- add_element_method(:filters => filters, :method_name => element_name, :method_block => element_block)
74
+ add_element_method(:filters => filters, :element_name => element_name, :element_block => element_block)
30
75
  end
31
76
  end
32
77
 
33
- def add_element_method(params)
78
+ def add_element_method(params) # :nodoc:
34
79
  self.class.class_eval do
35
- define_method(params[:method_name]) do
36
- params[:filters].each do |(filter_name,filter_block)|
37
- unless filter_block.call
38
- raise FilterError, "#{filter_name} returned false for #{params[:method_name]}"
39
- end
40
- end
41
- params[:method_block].call
80
+ define_method(params[:element_name]) do |*args|
81
+ check_filters(params)
82
+ self.instance_exec(*args,&params[:element_block])
42
83
  end
43
84
  end
44
85
  end
86
+
87
+ def check_filters(params) # :nodoc:
88
+ params[:filters].each do |filter_method|
89
+ raise FilterError, "#{filter_method} returned false for #{params[:element_name]}" unless send(filter_method)
90
+ end
91
+ end
45
92
  end
46
93
 
47
94
  class FilterError < StandardError; end