taza 0.5.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +33 -0
- data/Manifest.txt +39 -9
- data/README.txt +29 -7
- data/Rakefile +72 -13
- data/bin/taza +16 -3
- data/generators/flow/flow_generator.rb +57 -0
- data/generators/flow/templates/flow.rb.erb +12 -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/site/site_generator.rb +55 -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 +76 -0
- data/lib/app_generators/taza/templates/config.yml.erb +3 -0
- data/lib/{taza/generators → app_generators/taza}/templates/rakefile.rb.erb +0 -0
- data/lib/app_generators/taza/templates/spec_helper.rb.erb +11 -0
- data/lib/taza.rb +44 -5
- data/lib/taza/browser.rb +40 -0
- data/lib/taza/browsers/ie_watir.rb +8 -0
- data/lib/taza/browsers/safari_watir.rb +8 -0
- data/lib/taza/flow.rb +45 -0
- data/lib/taza/page.rb +66 -19
- data/lib/taza/settings.rb +36 -0
- data/lib/taza/site.rb +122 -11
- data/lib/taza/tasks.rb +26 -34
- data/spec/browser_spec.rb +72 -0
- data/spec/flow_generator_spec.rb +70 -0
- data/spec/page_generator_spec.rb +57 -0
- data/spec/page_spec.rb +82 -0
- data/spec/platform/osx/browser_spec.rb +14 -0
- data/spec/platform/windows/browser_spec.rb +14 -0
- data/spec/project_generator_spec.rb +42 -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/flows/batman.rb +2 -0
- data/spec/sandbox/flows/robin.rb +4 -0
- data/spec/sandbox/pages/foo/bar.rb +9 -0
- data/spec/settings_spec.rb +88 -0
- data/spec/site_generator_spec.rb +53 -0
- data/spec/site_spec.rb +239 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/taza_bin_spec.rb +14 -0
- data/spec/taza_spec.rb +12 -0
- data/spec/taza_tasks_spec.rb +27 -0
- data/spec/unit_helper_spec.rb +14 -0
- metadata +103 -13
- data/lib/taza/generators.rb +0 -4
- data/lib/taza/generators/base.rb +0 -33
- data/lib/taza/generators/page.rb +0 -22
- data/lib/taza/generators/project.rb +0 -18
- data/lib/taza/generators/site.rb +0 -24
- data/lib/taza/generators/templates/page.rb.erb +0 -6
- 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,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
|
File without changes
|
@@ -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/
|
3
|
+
require 'taza/browser'
|
4
|
+
require 'taza/settings'
|
5
|
+
require 'taza/flow'
|
5
6
|
|
6
7
|
module Taza
|
7
|
-
VERSION = '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
|
data/lib/taza/browser.rb
ADDED
@@ -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
|
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
|
-
|
19
|
+
def elements # :nodoc:
|
20
|
+
@elements ||= {}
|
21
|
+
end
|
22
|
+
def filters # :nodoc:
|
23
|
+
@filters ||= Hash.new { [] }
|
24
|
+
end
|
5
25
|
end
|
6
26
|
|
7
|
-
|
8
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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, :
|
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[:
|
36
|
-
params
|
37
|
-
|
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,¶ms[: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
|