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.
@@ -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
Binary file
Binary file
@@ -0,0 +1 @@
1
+ *.log
@@ -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.
@@ -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
+
@@ -0,0 +1,12 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+
6
+ desc 'Test the Janus rack middleware.'
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'test'
9
+ t.pattern = 'test/**_test.rb'
10
+ t.verbose = true
11
+ end
12
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -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-----
@@ -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,11 @@
1
+ begin
2
+ block = lambda do
3
+ Calliper.driver.quit if Calliper.driver?
4
+ end
5
+
6
+ if Minitest.respond_to?(:after_run)
7
+ Minitest.after_run(&block)
8
+ else
9
+ Minitest::Unit.after_tests(&block)
10
+ end
11
+ 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,2 @@
1
+ require 'calliper/server'
2
+ Calliper::Config.application = Rails.application
@@ -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,10 @@
1
+ module Calliper
2
+ def self.version
3
+ Gem::Version.new File.read(File.expand_path('../../../VERSION', __FILE__))
4
+ end
5
+
6
+ module VERSION
7
+ MAJOR, MINOR, TINY, PRE = Calliper.version.segments
8
+ STRING = Calliper.version.to_s
9
+ end
10
+ 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
@@ -0,0 +1,2 @@
1
+ require './application'
2
+ run SampleApplication