makevoid-taza 0.8.6
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.
- data/History.txt +74 -0
- data/Manifest.txt +61 -0
- data/README +79 -0
- data/README.textile +1 -0
- data/VERSION.yml +4 -0
- data/bin/taza +17 -0
- data/generators/flow/flow_generator.rb +57 -0
- data/generators/flow/templates/flow.rb.erb +9 -0
- data/generators/page/page_generator.rb +58 -0
- data/generators/page/templates/functional_page_spec.rb.erb +8 -0
- data/generators/page/templates/page.rb.erb +8 -0
- data/generators/partial/partial_generator.rb +57 -0
- data/generators/partial/templates/partial.rb.erb +7 -0
- data/generators/site/site_generator.rb +56 -0
- data/generators/site/templates/site.rb.erb +10 -0
- data/generators/site/templates/site.yml.erb +3 -0
- data/lib/app_generators/taza/taza_generator.rb +74 -0
- data/lib/app_generators/taza/templates/config.yml.erb +3 -0
- data/lib/app_generators/taza/templates/rakefile.rb.erb +10 -0
- data/lib/app_generators/taza/templates/spec_helper.rb.erb +11 -0
- data/lib/extensions/array.rb +10 -0
- data/lib/extensions/hash.rb +15 -0
- data/lib/extensions/object.rb +33 -0
- data/lib/extensions/string.rb +11 -0
- data/lib/taza.rb +24 -0
- data/lib/taza/browser.rb +53 -0
- data/lib/taza/entity.rb +55 -0
- data/lib/taza/fixture.rb +68 -0
- data/lib/taza/fixtures.rb +24 -0
- data/lib/taza/flow.rb +40 -0
- data/lib/taza/page.rb +128 -0
- data/lib/taza/settings.rb +33 -0
- data/lib/taza/site.rb +150 -0
- data/lib/taza/tasks.rb +54 -0
- data/spec/array_spec.rb +17 -0
- data/spec/browser_spec.rb +86 -0
- data/spec/entity_spec.rb +35 -0
- data/spec/fixture_spec.rb +51 -0
- data/spec/fixtures_spec.rb +47 -0
- data/spec/flow_generator_spec.rb +50 -0
- data/spec/hash_spec.rb +15 -0
- data/spec/object_spec.rb +29 -0
- data/spec/page_generator_spec.rb +56 -0
- data/spec/page_module_spec.rb +165 -0
- data/spec/page_spec.rb +105 -0
- data/spec/partial_generator_spec.rb +38 -0
- data/spec/project_generator_spec.rb +56 -0
- data/spec/sandbox/config.yml +3 -0
- data/spec/sandbox/config/config.yml +1 -0
- data/spec/sandbox/config/site_name.yml +5 -0
- data/spec/sandbox/fixtures/examples.yml +15 -0
- data/spec/sandbox/fixtures/foo_site/bars.yml +2 -0
- data/spec/sandbox/fixtures/foos.yml +3 -0
- data/spec/sandbox/fixtures/users.yml +3 -0
- data/spec/sandbox/flows/batman.rb +5 -0
- data/spec/sandbox/flows/robin.rb +4 -0
- data/spec/sandbox/pages/foo/bar.rb +9 -0
- data/spec/sandbox/pages/foo/bay.rb +10 -0
- data/spec/sandbox/pages/foo/baz.rb +11 -0
- data/spec/sandbox/pages/foo/partials/partial_the_reckoning.rb +2 -0
- data/spec/settings_spec.rb +92 -0
- data/spec/site_fixtures_spec.rb +17 -0
- data/spec/site_generator_spec.rb +56 -0
- data/spec/site_spec.rb +268 -0
- data/spec/spec_helper.rb +57 -0
- data/spec/string_spec.rb +8 -0
- data/spec/taza_bin_spec.rb +13 -0
- data/spec/taza_tasks_spec.rb +56 -0
- data/taza.gemspec +46 -0
- metadata +196 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'taza/fixture'
|
2
|
+
|
3
|
+
module Taza
|
4
|
+
dirs = Dir.glob(File.join(Fixture.base_path,'*/'))
|
5
|
+
dirs.unshift Fixture.base_path
|
6
|
+
dirs.each do |dir|
|
7
|
+
mod = dir.sub(Fixture.base_path,File.join(File.basename(Fixture.base_path),'')).camelize.sub(/::$/,'')
|
8
|
+
self.class_eval <<-EOS
|
9
|
+
module #{mod}
|
10
|
+
def self.included(other_module)
|
11
|
+
fixture = Fixture.new
|
12
|
+
fixture.load_fixtures_from('#{dir}')
|
13
|
+
fixture.fixture_names.each do |fixture_name|
|
14
|
+
self.class_eval do
|
15
|
+
define_method(fixture_name) do |entity_key|
|
16
|
+
fixture.get_fixture_entity(fixture_name,entity_key.to_s)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
EOS
|
23
|
+
end
|
24
|
+
end
|
data/lib/taza/flow.rb
ADDED
@@ -0,0 +1,40 @@
|
|
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
|
+
# module WidgetsOfTheFuture
|
16
|
+
# class WidgetsOfTheFuture < Taza::Site
|
17
|
+
# def create_an_account_flow(params={})
|
18
|
+
# home_page.create_an_account_link.click
|
19
|
+
# create_an_account_page do |cap|
|
20
|
+
# cap.email.set params[:email]
|
21
|
+
# cap.password.set params[:password]
|
22
|
+
# cap.submit.click
|
23
|
+
# end
|
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.create_an_account_flow :email => "i.am@the.widget.store.com", :password => "secret"
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
class Flow
|
39
|
+
end
|
40
|
+
end
|
data/lib/taza/page.rb
ADDED
@@ -0,0 +1,128 @@
|
|
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
|
16
|
+
class Page
|
17
|
+
attr_accessor :browser
|
18
|
+
class << self
|
19
|
+
def elements # :nodoc:
|
20
|
+
@elements ||= {}
|
21
|
+
end
|
22
|
+
def filters # :nodoc:
|
23
|
+
@filters ||= Hash.new { [] }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
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
|
34
|
+
def self.element(name,&block)
|
35
|
+
if !@module.nil?
|
36
|
+
self.elements[@module] = Hash.new if self.elements[@module].nil?
|
37
|
+
self.elements[@module] = self.elements[@module].merge({ name => block })
|
38
|
+
elsif !self.elements[name].nil?
|
39
|
+
raise ElementError,"Duplicate definations for Element - #{name} on Page - #{self.to_s}"
|
40
|
+
else
|
41
|
+
self.elements[name] = block
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# A filter for elemenet(s) on a page
|
46
|
+
# Example:
|
47
|
+
# class HomePage < Taza::Page
|
48
|
+
# element(:foo) {browser.element_by_xpath('some xpath')}
|
49
|
+
# filter :title_given, :foo
|
50
|
+
# #a filter will apply to all elements if none are specified
|
51
|
+
# filter :some_filter
|
52
|
+
# #a filter will also apply to all elements if the symbol :all is given
|
53
|
+
# filter :another_filter, :all
|
54
|
+
#
|
55
|
+
# def some_filter
|
56
|
+
# true
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# def some_filter
|
60
|
+
# true
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# def title_given
|
64
|
+
# browser.title.nil?
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
def self.filter(method_name, *elements)
|
68
|
+
elements = [:all] if elements.empty?
|
69
|
+
elements.each do |element|
|
70
|
+
self.filters[element] = self.filters[element] << method_name
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.page_module(name,&block)
|
75
|
+
@module = name
|
76
|
+
yield(block)
|
77
|
+
@module = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.page_module_filter(method_name, page_module_name, *elements)
|
81
|
+
elements = [page_module_name] if elements.empty?
|
82
|
+
elements.each do |element|
|
83
|
+
self.filters[element] = self.filters[element] << method_name
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def initialize(page_module = nil)
|
88
|
+
add_element_methods(page_module)
|
89
|
+
@active_filters = []
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_element_methods(page_module = nil) # :nodoc:
|
93
|
+
self.class.elements.each do |element_name,element_block|
|
94
|
+
if (element_block.is_a?(Hash) && !page_module.nil? && page_module==element_name)
|
95
|
+
element_block.each do |key,value|
|
96
|
+
filters = self.class.filters[element_name] + self.class.filters[:all] + self.class.filters[key]
|
97
|
+
add_element_method(:filters => filters, :element_name => key, :element_block => value)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
filters = self.class.filters[element_name] + self.class.filters[:all]
|
101
|
+
add_element_method(:filters => filters, :element_name => element_name, :element_block => element_block)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_element_method(params) # :nodoc:
|
107
|
+
metaclass.class_eval do
|
108
|
+
define_method(params[:element_name]) do |*args|
|
109
|
+
check_filters(params)
|
110
|
+
self.instance_exec(*args,¶ms[:element_block])
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def check_filters(params) # :nodoc:
|
116
|
+
params[:filters].each do |filter_method|
|
117
|
+
unless @active_filters.include?(filter_method)
|
118
|
+
@active_filters << filter_method
|
119
|
+
raise FilterError, "#{filter_method} returned false for #{params[:element_name]}" unless send(filter_method)
|
120
|
+
@active_filters.delete(filter_method)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class FilterError < StandardError; end
|
127
|
+
class ElementError < StandardError; end
|
128
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'activesupport'
|
2
|
+
require 'taza/options'
|
3
|
+
|
4
|
+
module Taza
|
5
|
+
class Settings
|
6
|
+
# Taza::Settings.Config('google')
|
7
|
+
def self.config(site_name)
|
8
|
+
site_file(site_name).merge(Options.new.execute)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Loads the config file for the entire project and returns the hash.
|
12
|
+
# Does not override settings from the ENV variables.
|
13
|
+
def self.config_file
|
14
|
+
YAML.load_file(config_file_path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.config_file_path # :nodoc:
|
18
|
+
File.join(config_folder,'config.yml')
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.config_folder # :nodoc:
|
22
|
+
File.join(path,'config')
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.site_file(site_name) # :nodoc:
|
26
|
+
YAML.load_file(File.join(config_folder,"#{site_name.underscore}.yml"))[ENV['TAZA_ENV']]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.path # :nodoc:
|
30
|
+
'.'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/taza/site.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'activesupport'
|
3
|
+
|
4
|
+
module Taza
|
5
|
+
# An abstraction of a website, but more really a container for a sites pages.
|
6
|
+
#
|
7
|
+
# You can generate a site by performing the following command:
|
8
|
+
# $ ./script/generate site google
|
9
|
+
#
|
10
|
+
# This will generate a site file for google, a flows folder, and a pages folder in lib
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# require 'taza'
|
15
|
+
#
|
16
|
+
# class Google < Taza::Site
|
17
|
+
#
|
18
|
+
# end
|
19
|
+
class Site
|
20
|
+
@@before_browser_closes = Proc.new() {}
|
21
|
+
@@donot_close_browser = false
|
22
|
+
# Use this to do something with the browser before it closes, but note that it is a class method which
|
23
|
+
# means that this will get called for any instance of a site.
|
24
|
+
#
|
25
|
+
# Here's an example of how you might use it to print the DOM output of a browser before it closes:
|
26
|
+
#
|
27
|
+
# Taza::Site.before_browser_closes do |browser|
|
28
|
+
# puts browser.html
|
29
|
+
# end
|
30
|
+
def self.before_browser_closes(&block)
|
31
|
+
@@before_browser_closes = block
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.donot_close_browser
|
35
|
+
@@donot_close_browser = true
|
36
|
+
end
|
37
|
+
attr_accessor :browser
|
38
|
+
|
39
|
+
# A site can be called a few different ways
|
40
|
+
#
|
41
|
+
# The following example creates a new browser object and closes it:
|
42
|
+
# Google.new do
|
43
|
+
# google.search.set "taza"
|
44
|
+
# google.submit.click
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# This example will create a browser object but not close it:
|
48
|
+
# Google.new.search.set "taza"
|
49
|
+
#
|
50
|
+
# Sites can take a couple of parameters in the constructor:
|
51
|
+
# :browser => a browser object to act on instead of creating one automatically
|
52
|
+
# :url => the url of where to start the site
|
53
|
+
def initialize(params={},&block)
|
54
|
+
@module_name = self.class.parent.to_s
|
55
|
+
@class_name = self.class.to_s.split("::").last
|
56
|
+
define_site_pages
|
57
|
+
define_flows
|
58
|
+
config = Settings.config(@class_name)
|
59
|
+
if params[:browser]
|
60
|
+
@browser = params[:browser]
|
61
|
+
else
|
62
|
+
@browser = Browser.create(config)
|
63
|
+
@i_created_browser = true
|
64
|
+
end
|
65
|
+
@browser.goto(params[:url] || config[:url])
|
66
|
+
execute_block_and_close_browser(browser,&block) if block_given?
|
67
|
+
end
|
68
|
+
|
69
|
+
def execute_block_and_close_browser(browser)
|
70
|
+
begin
|
71
|
+
yield self
|
72
|
+
rescue => site_block_exception
|
73
|
+
ensure
|
74
|
+
begin
|
75
|
+
@@before_browser_closes.call(browser)
|
76
|
+
rescue => before_browser_closes_block_exception
|
77
|
+
"" # so basically rcov has a bug where it would insist this block is uncovered when empty
|
78
|
+
end
|
79
|
+
close_browser_and_raise_if site_block_exception || before_browser_closes_block_exception
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.settings # :nodoc:
|
84
|
+
Taza::Settings.site_file(self.name.to_s.split("::").last)
|
85
|
+
end
|
86
|
+
|
87
|
+
def close_browser_and_raise_if original_error # :nodoc:
|
88
|
+
begin
|
89
|
+
@browser.close if (@i_created_browser && !@@donot_close_browser)
|
90
|
+
ensure
|
91
|
+
raise original_error if original_error
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def define_site_pages # :nodoc:
|
96
|
+
Dir.glob(pages_path) do |file|
|
97
|
+
require file
|
98
|
+
page_name = File.basename(file,'.rb')
|
99
|
+
page_class = "#{@module_name}::#{page_name.camelize}"
|
100
|
+
self.class.class_eval <<-EOS
|
101
|
+
def #{page_name}(page_module = nil)
|
102
|
+
page = '#{page_class}'.constantize.new(page_module)
|
103
|
+
page.browser = @browser
|
104
|
+
yield page if block_given?
|
105
|
+
page
|
106
|
+
end
|
107
|
+
EOS
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def define_flows # :nodoc:
|
112
|
+
Dir.glob(flows_path) do |file|
|
113
|
+
require file
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# This is used to call a flow belonging to the site
|
118
|
+
#
|
119
|
+
# Example:
|
120
|
+
# Google.new do |google|
|
121
|
+
# google.flow(:perform_search, :query => "taza")
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
# Where the flow would be defined under lib/sites/google/flows/perform_search.rb and look like:
|
125
|
+
# class PerformSearch < Taza::Flow
|
126
|
+
# alias :google :site
|
127
|
+
#
|
128
|
+
# def run(params={})
|
129
|
+
# google.search.set params[:query]
|
130
|
+
# google.submit.click
|
131
|
+
# end
|
132
|
+
# end
|
133
|
+
|
134
|
+
def pages_path # :nodoc:
|
135
|
+
File.join(path,'pages','**','*.rb')
|
136
|
+
end
|
137
|
+
|
138
|
+
def flows_path # :nodoc:
|
139
|
+
File.join(path,'flows','*.rb')
|
140
|
+
end
|
141
|
+
|
142
|
+
def path # :nodoc:
|
143
|
+
File.join(base_path,'lib','sites',@class_name.underscore)
|
144
|
+
end
|
145
|
+
|
146
|
+
def base_path # :nodoc:
|
147
|
+
'.'
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/taza/tasks.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'taglob'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
|
6
|
+
def tags
|
7
|
+
ENV['TAGS']
|
8
|
+
end
|
9
|
+
|
10
|
+
module Taza
|
11
|
+
module Rake
|
12
|
+
class Tasks
|
13
|
+
attr_accessor :spec_opts
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
yield self if block_given?
|
17
|
+
define
|
18
|
+
end
|
19
|
+
|
20
|
+
def define_spec_task(name,glob_path)
|
21
|
+
Spec::Rake::SpecTask.new name do |t|
|
22
|
+
t.spec_files = Dir.taglob(glob_path,tags)
|
23
|
+
t.spec_opts << spec_opts
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def define
|
28
|
+
namespace :spec do
|
29
|
+
Dir.glob('./spec/*/').each do |dir|
|
30
|
+
recurse_to_create_rake_tasks(dir)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def recurse_to_create_rake_tasks(dir)
|
36
|
+
basename = File.basename(dir)
|
37
|
+
spec_pattern = File.join(dir,"**","*_spec.rb")
|
38
|
+
if (not Dir.glob(spec_pattern).empty?)
|
39
|
+
define_spec_task(basename,spec_pattern)
|
40
|
+
namespace basename do
|
41
|
+
Dir.glob(File.join(dir,"*_spec.rb")).each do |spec_file|
|
42
|
+
spec_name = File.basename(spec_file,'_spec.rb')
|
43
|
+
define_spec_task(spec_name,spec_file)
|
44
|
+
end
|
45
|
+
Dir.glob(File.join(dir,"*/")).each do |sub_dir|
|
46
|
+
recurse_to_create_rake_tasks(sub_dir)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/array_spec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
require 'extensions/array'
|
3
|
+
|
4
|
+
describe 'Array Extensions' do
|
5
|
+
it "should know if elements are not equivilent to a subset of those elements" do
|
6
|
+
[1,2,3].should_not be_equivalent([2,3])
|
7
|
+
end
|
8
|
+
it "should know if elements are not equivilent to a larger set including those elements" do
|
9
|
+
[1,2,3].should_not be_equivalent([1,2,3,4])
|
10
|
+
end
|
11
|
+
it "should know it is equivalent if the same order" do
|
12
|
+
[1,2,3].should be_equivalent([1,2,3])
|
13
|
+
end
|
14
|
+
it "should know it is equivalent if the different orders" do
|
15
|
+
[1,2,3].should be_equivalent([2,1,3])
|
16
|
+
end
|
17
|
+
end
|