calliper 0.0.1
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +1 -0
- data/.travis.yml +16 -0
- data/LICENSE +20 -0
- data/README.md +11 -0
- data/Rakefile +12 -0
- data/VERSION +1 -0
- data/calliper.gemspec +23 -0
- data/certs/ysbaddaden.pem +21 -0
- data/lib/calliper.rb +17 -0
- data/lib/calliper/config.rb +44 -0
- data/lib/calliper/minitest.rb +11 -0
- data/lib/calliper/page.rb +37 -0
- data/lib/calliper/rack.rb +19 -0
- data/lib/calliper/rails.rb +2 -0
- data/lib/calliper/server.rb +60 -0
- data/lib/calliper/version.rb +10 -0
- data/lib/calliper/webdriver.rb +76 -0
- data/test/sample/application.rb +22 -0
- data/test/sample/config.ru +2 -0
- data/test/sample/public/javascripts/angular.js +20757 -0
- data/test/sample/public/javascripts/application.js +26 -0
- data/test/sample/views/index.erb +27 -0
- data/test/sample_test.rb +48 -0
- data/test/test_helper.rb +24 -0
- metadata +139 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6744c434f323e9ef6685ecb0ac6eb918fa8c5711
|
4
|
+
data.tar.gz: 3384b9224c9767b45edc52233c707ecd060ca059
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 07b6681def6e25bfb470f39f4250b3a904fa0fd20067ca3c9da52e35dffc42b41719573f3668ce57706b11a2a533b914c76eb52cbade3f50786d94b4ccb31ae3
|
7
|
+
data.tar.gz: b62ef597f198e12f311487a2b6d5131ccdafbc771e9145913355b075ef27335cf0cd85213cc927993ae0d434bf54556ef3b00da5dbc31bd9451ac794cb0c39ee
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
Binary file
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.log
|
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
language: ruby
|
2
|
+
|
3
|
+
script: bundle exec rake test
|
4
|
+
|
5
|
+
rvm:
|
6
|
+
- 2.0.0
|
7
|
+
- 2.1.0
|
8
|
+
|
9
|
+
env:
|
10
|
+
global:
|
11
|
+
- secure: "naP+5ZG38Ie1/QGxMfrI9kLnGPnqRbIuNw0rFebCOcqeKj9MadBSSQjOi7c0gZBmNaV+js01R9U7RClNFWqHY+tGDWWlYL4gGDT0I2vIgCmnjGnIprD+iuS7ry1tISKjTOn7nfDvVxppK7R2xpuFFjrQgLmYyloPJMacT/n5E9Q="
|
12
|
+
- secure: "Q6wwGwwGfrKBEzoSwylLFjIKj8B5FNHoYEtEtivmHQYrqG+OyiL4YAOxFjpuaiWUJCYKeo6CWhTG9V0NVDgOZY54gxmJhqUpQCFZ53B1MylxANOykADOiXr5xHC9O2VBBF5OU1rkLL5fR8daA/pBOmoidXSD+Psn53ZWnReqZOc="
|
13
|
+
|
14
|
+
addons:
|
15
|
+
sauce_connect: true
|
16
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Julien Portalier
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Calliper
|
2
|
+
|
3
|
+
Protractor for Ruby, or testing your Angular application with elegance.
|
4
|
+
|
5
|
+
Thought this is only but a (working) hack for now.
|
6
|
+
|
7
|
+
## Support
|
8
|
+
|
9
|
+
It currently supports Rack and Rails applications with Minitest, but it should
|
10
|
+
be possible to hack in support for other frameworks (eg: Sinatra, RSpec).
|
11
|
+
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/calliper.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/calliper/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Julien Portalier"]
|
6
|
+
gem.email = ["julien@portalier.com"]
|
7
|
+
gem.description = gem.summary = "Protractor for Ruby, or testing your Angular application with elegance."
|
8
|
+
gem.homepage = "http://github.com/ysbaddaden/calliper"
|
9
|
+
gem.license = "MIT"
|
10
|
+
|
11
|
+
gem.files = `git ls-files | grep -Ev '^(Gemfile|test)'`.split("\n")
|
12
|
+
gem.test_files = `git ls-files -- test/*`.split("\n")
|
13
|
+
gem.name = "calliper"
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.version = Calliper::VERSION::STRING
|
16
|
+
|
17
|
+
gem.cert_chain = ['certs/ysbaddaden.pem']
|
18
|
+
gem.signing_key = File.expand_path('~/.ssh/gem-private_key.pem') if $0 =~ /gem\z/
|
19
|
+
|
20
|
+
gem.add_dependency 'selenium-webdriver'
|
21
|
+
gem.add_development_dependency 'rack'
|
22
|
+
gem.add_development_dependency 'minitest'
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMQ8wDQYDVQQDDAZqdWxp
|
3
|
+
ZW4xGTAXBgoJkiaJk/IsZAEZFglwb3J0YWxpZXIxEzARBgoJkiaJk/IsZAEZFgNj
|
4
|
+
b20wHhcNMTQwMTE0MjIzMTQ4WhcNMTUwMTE0MjIzMTQ4WjBBMQ8wDQYDVQQDDAZq
|
5
|
+
dWxpZW4xGTAXBgoJkiaJk/IsZAEZFglwb3J0YWxpZXIxEzARBgoJkiaJk/IsZAEZ
|
6
|
+
FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpxWuWRJXEz2+p
|
7
|
+
2EW4NOPzkKloRLWoj+WQnqhQKT46GbH3ToDId8AMELTDIKpTQFiG2ty6D7S4IBFv
|
8
|
+
7ceFKNk/EJc17mSYE1DzrtItor2/eeGC1zeNfvLjyDtyHKyKUZ891C1D0so5coUx
|
9
|
+
2YbDW5npFkJkPaA5GneH7DFaCoIFLrD7ekbzaZAjlH+EH2fhd1XLhSsPEIiE+OnD
|
10
|
+
ilWnsPoRJAZwQOiVAtvh7xuc+29uSNndIIm2rU00SxbJnzsAq9ZddwPpMU/UcQpD
|
11
|
+
4gCBCaNGzrLz4+upQdYEOuggM7rR3P934qfhIwb+aRGglqdNunmUrdCuhsGXrxq2
|
12
|
+
FvqwDvFZAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
|
13
|
+
BBQoESDCnNz3LmbpUzOrGeXOpk9sqjAfBgNVHREEGDAWgRRqdWxpZW5AcG9ydGFs
|
14
|
+
aWVyLmNvbTAfBgNVHRIEGDAWgRRqdWxpZW5AcG9ydGFsaWVyLmNvbTANBgkqhkiG
|
15
|
+
9w0BAQUFAAOCAQEAML4w0F/VF0gi5JqMqYSO05TakAauG8jQX0hov5H8M0Xhl79G
|
16
|
+
BdUllH0QEw0cP6J2g46zAk0FGHIGthx0OKKi5YMYTs/KPqOVIAcJslt2sGIC1Ukm
|
17
|
+
wpOWIg1XMe68+JVTktBKcBFAvc0pLtty1TgdSd2wr7KQgfmBU9I8G6AoPYhJOhkG
|
18
|
+
SHTTSX3ms2/XePuSnyOfir/AQC7U0NalnKLNdwY9gkEdNwiTf5Ga/lZVDQ607bow
|
19
|
+
KVqCN//9bevjMk5OiMi9X3Wu/GtVWDwC6OTWFWKd54KgbuWlakO8LC1SMmStnCIF
|
20
|
+
W4qpyMWMZMcB4ZN/0mUVzY5xwrislBtsmQVUSw==
|
21
|
+
-----END CERTIFICATE-----
|
data/lib/calliper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'selenium/webdriver'
|
2
|
+
require 'calliper/config'
|
3
|
+
require 'calliper/page'
|
4
|
+
require 'calliper/server'
|
5
|
+
require 'calliper/webdriver'
|
6
|
+
require 'calliper/rails' if defined?(Rails)
|
7
|
+
require 'calliper/minitest' if defined?(Minitest)
|
8
|
+
|
9
|
+
module Calliper
|
10
|
+
def self.server?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.enable!
|
15
|
+
server.start
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Calliper
|
2
|
+
module Config
|
3
|
+
extend self
|
4
|
+
|
5
|
+
attr_accessor :application
|
6
|
+
attr_accessor :base_host
|
7
|
+
attr_accessor :base_url
|
8
|
+
attr_accessor :browser_name
|
9
|
+
attr_accessor :capabilities
|
10
|
+
attr_accessor :driver
|
11
|
+
attr_accessor :port
|
12
|
+
attr_accessor :remote_url
|
13
|
+
|
14
|
+
def base_url
|
15
|
+
@base_url ||= if base_host
|
16
|
+
build_base_url_from_host
|
17
|
+
else
|
18
|
+
Calliper.server.url
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def browser_name
|
23
|
+
@browser_name ||= :firefox
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def build_base_url_from_host
|
29
|
+
url = "http://#{base_host}"
|
30
|
+
|
31
|
+
if Calliper.server?
|
32
|
+
url + ":#{Calliper.server.port}"
|
33
|
+
elsif port
|
34
|
+
url + ":#{port}"
|
35
|
+
else
|
36
|
+
url
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.setup
|
42
|
+
yield Config
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Calliper
|
2
|
+
class Page
|
3
|
+
attr_writer :base_url
|
4
|
+
|
5
|
+
def self.get(*args)
|
6
|
+
page = new
|
7
|
+
page.get(*args)
|
8
|
+
page
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(path, sync: true)
|
12
|
+
url = path =~ %r(^http://) ? path : "#{base_url}#{path}"
|
13
|
+
driver.get(url)
|
14
|
+
|
15
|
+
if sync
|
16
|
+
wait = Selenium::WebDriver::Wait.new(timeout: 10)
|
17
|
+
wait.until { driver.find_element(css: "[ng-app]") }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def base_url
|
22
|
+
@base_url ||= Config.base_url
|
23
|
+
end
|
24
|
+
|
25
|
+
def driver
|
26
|
+
Calliper.driver
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(method_name, *args)
|
30
|
+
if driver.respond_to?(method_name)
|
31
|
+
driver.__send__(method_name, *args)
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'calliper/server'
|
2
|
+
|
3
|
+
module Calliper
|
4
|
+
def self.application
|
5
|
+
@app
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.application=(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.server
|
13
|
+
@server ||= Server.new(application)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.server?
|
17
|
+
!!@server
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'calliper/config'
|
2
|
+
|
3
|
+
module Calliper
|
4
|
+
def self.server
|
5
|
+
@server ||= Server.new(Config.application)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.server?
|
9
|
+
!!@server
|
10
|
+
end
|
11
|
+
|
12
|
+
# NOTE: Borrows code from the fantastic Teaspoon gem:
|
13
|
+
# http://github.com/modeset/teaspoon
|
14
|
+
class Server
|
15
|
+
attr_reader :application, :port, :logger
|
16
|
+
|
17
|
+
def initialize(application, port = Config.port, logger = nil)
|
18
|
+
@application = application
|
19
|
+
@port = port
|
20
|
+
@logger = logger
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
@thread = Thread.new do
|
25
|
+
server = Rack::Server.new(rack_options)
|
26
|
+
server.start
|
27
|
+
end
|
28
|
+
|
29
|
+
Timeout.timeout(60) do
|
30
|
+
@thread.join(0.1) until responsive?
|
31
|
+
end
|
32
|
+
rescue Timeout::Error
|
33
|
+
raise "Server failed to start within 60 seconds."
|
34
|
+
rescue => e
|
35
|
+
raise "Cannot start server: #{e.message}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def responsive?
|
39
|
+
return false if @thread && @thread.join(0)
|
40
|
+
TCPSocket.new('127.0.0.1', port).close
|
41
|
+
return true
|
42
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
def rack_options
|
47
|
+
{
|
48
|
+
app: application,
|
49
|
+
environment: 'test',
|
50
|
+
Port: port,
|
51
|
+
AccessLog: [],
|
52
|
+
Logger: logger
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def url
|
57
|
+
"http://127.0.0.1:#{port}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Calliper
|
4
|
+
# TODO: connect to any remote server (eg: custom, saucelabs, browserstack)
|
5
|
+
# FIXME: only firefox quits correctly when used directly
|
6
|
+
def self.driver
|
7
|
+
@driver ||= if local_server_running?
|
8
|
+
Selenium::WebDriver.for(:remote,
|
9
|
+
url: "http://localhost:4444/wd/hub",
|
10
|
+
desired_capabilities: { browserName: Config.driver.to_s }
|
11
|
+
)
|
12
|
+
elsif Config.driver == :remote
|
13
|
+
Selenium::WebDriver.for(:remote,
|
14
|
+
url: Config.remote_url,
|
15
|
+
desired_capabilities: Config.capabilities
|
16
|
+
)
|
17
|
+
else
|
18
|
+
Selenium::WebDriver.for(Config.driver)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.local_server_running?
|
23
|
+
begin
|
24
|
+
TCPSocket.new('localhost', 4444).close
|
25
|
+
true
|
26
|
+
rescue
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.driver?
|
32
|
+
!!@driver
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# We're hacking our way into WebDriver locators in order to emulate some
|
37
|
+
# nice Protractor locators.
|
38
|
+
#
|
39
|
+
# FIXME: use real locators that use protractor's client-side finders:
|
40
|
+
# https://github.com/angular/protractor/blob/master/lib/clientsidescripts.js
|
41
|
+
module Selenium::WebDriver::SearchContext
|
42
|
+
%i(find_element find_elements).each do |name|
|
43
|
+
alias_method "#{name}_without_angular", name
|
44
|
+
|
45
|
+
define_method name do |*args|
|
46
|
+
__send__("#{name}_without_angular", *angular_custom_locator(args))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def angular_custom_locator(args)
|
53
|
+
case args.size
|
54
|
+
when 2
|
55
|
+
how, what = args
|
56
|
+
when 1
|
57
|
+
how = args.first.keys.first
|
58
|
+
what = args.first[how]
|
59
|
+
else
|
60
|
+
return args
|
61
|
+
end
|
62
|
+
|
63
|
+
case how
|
64
|
+
when :model
|
65
|
+
[:css, angular_prefixes.map { |prefix| "[#{prefix}model='#{what}']" }.join(", ")]
|
66
|
+
when :repeater
|
67
|
+
[:css, angular_prefixes.map { |prefix| "[#{prefix}repeat^='#{what}']" }.join(", ")]
|
68
|
+
else
|
69
|
+
args
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def angular_prefixes
|
74
|
+
@angular_prefixes ||= %w(ng- ng_ data-ng- x-ng- ng\\:)
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class SampleApplication < Sinatra::Base
|
5
|
+
configure do
|
6
|
+
disable :protection
|
7
|
+
disable :sessions
|
8
|
+
enable :static
|
9
|
+
|
10
|
+
set :public_folder, File.expand_path('../public', __FILE__)
|
11
|
+
set :view, File.expand_path('../views', __FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
get '/' do
|
15
|
+
erb :index
|
16
|
+
end
|
17
|
+
|
18
|
+
get '/notifications.json' do
|
19
|
+
content_type 'application/json'
|
20
|
+
(1..20).map { |id| { id: id, message: "this is notification ##{id}" } }.reverse.to_json
|
21
|
+
end
|
22
|
+
end
|