taza 0.5.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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