domreactor-redglass 0.0.2 → 0.0.3

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/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .idea/
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ chromedriver.log
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Frank O'Hara
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # DomReactor + RedGlass
2
+
3
+ A RedGlass plugin which enables snapshot archives to be analyzed by DomReactor.
4
+
5
+ ## Requirements
6
+
7
+ You will need a DomReactor account and an API token to use DomReactor's automated layout analysis service. Visit http://domreactor.com to sign up.
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ # Configurations
13
+ browsers = [:firefox, :chrome]
14
+ archive_location = '/Users/you/Desktop/red_glass_snapshots'
15
+ test_id = 1
16
+ opts = {
17
+ baseline_browser: {name: 'firefox', version: '20.0', platform: 'darwin'},
18
+ threshold: 0.02
19
+ }
20
+
21
+ # Take a RedGlass snapshot in each browser.
22
+ browsers.each do |browser|
23
+ listener = RedGlassListener.new
24
+ driver = Selenium::WebDriver.for browser, :listener => listener
25
+ red_glass = RedGlass.new driver, {listener: listener, archive_location: archive_location, test_id: test_id}
26
+ driver.navigate.to 'http://google.com'
27
+ red_glass.take_snapshot
28
+ driver.quit
29
+ end
30
+
31
+ # Send the page archives to DomReactor.
32
+ DomReactorRedGlass.auth_token = '12345'
33
+ DomReactorRedGlass.create_chain_reaction("#{archive_location}/#{test_id}", opts)
34
+ ```
35
+
36
+ # License
37
+
38
+ The MIT License - Copyright (c) 2013 Frank O'Hara
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ desc 'Run specs'
7
+ task :default => :spec
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'domreactor-redglass/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'domreactor-redglass'
8
+ s.version = DomReactorRedGlass::VERSION
9
+ s.date = '2013-05-31'
10
+ s.summary = 'DomReactor plugin for RedGlass.'
11
+ s.description = 'Send RedGlass page archives to DomReactor for automated layout analysis.'
12
+ s.authors = ["Frank O'Hara", "Chris Lamb"]
13
+ s.email = ["frankj.ohara@gmail.com", "lamb.chrisr@gmail.com"]
14
+ s.homepage = 'https://github.com/bimech/domreactor-redglass'
15
+
16
+ s.files = `git ls-files`.split($/)
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency 'rest-client'
21
+ s.add_dependency 'json'
22
+ s.add_dependency 'rubyzip'
23
+ s.add_dependency 'red-glass', '>= 0.1.1'
24
+
25
+ s.add_development_dependency "bundler", "~> 1.3"
26
+ s.add_development_dependency "rake"
27
+ s.add_development_dependency "rspec", "~> 2.13.0"
28
+ s.add_development_dependency 'selenium-webdriver'
29
+ end
@@ -0,0 +1,27 @@
1
+ require 'selenium-webdriver'
2
+ require 'red-glass'
3
+ require 'domreactor-redglass'
4
+ include DomReactorRedGlass
5
+
6
+ # Configurations
7
+ browsers = [:firefox, :chrome]
8
+ archive_location = '/Users/you/Desktop/red_glass_snapshots'
9
+ test_id = 1
10
+ opts = {
11
+ baseline_browser: {name: 'firefox', version: '20.0', platform: 'darwin'},
12
+ threshold: 0.02
13
+ }
14
+
15
+ # Take a RedGlass snapshot in each browser.
16
+ browsers.each do |browser|
17
+ listener = RedGlassListener.new
18
+ driver = Selenium::WebDriver.for browser, :listener => listener
19
+ red_glass = RedGlass.new driver, {listener: listener, archive_location: archive_location, test_id: test_id}
20
+ driver.navigate.to 'http://google.com'
21
+ red_glass.take_snapshot
22
+ driver.quit
23
+ end
24
+
25
+ # Send the page archives to DomReactor.
26
+ DomReactorRedGlass.auth_token = '12345'
27
+ DomReactorRedGlass.create_chain_reaction('http://google.com', "#{archive_location}/#{test_id}", opts)
@@ -0,0 +1,119 @@
1
+ require 'json'
2
+ require 'rest-client'
3
+
4
+ module DomReactorRedGlass
5
+ class ChainReaction
6
+ attr_reader :id, :info, :page_url
7
+
8
+ def initialize(page_url, opts)
9
+ @page_url = page_url
10
+ @opts = opts
11
+ @info = info
12
+ end
13
+
14
+ def auth_token
15
+ Config.auth_token
16
+ end
17
+
18
+ def resource_url
19
+ "#{Config.url}/api/v1/chain_reactions"
20
+ end
21
+
22
+ def chain_reaction_url
23
+ "#{resource_url}/#{id}"
24
+ end
25
+
26
+ def id
27
+ id ||= info[:id]
28
+ end
29
+
30
+ def info
31
+ @info ||= create_chain_reaction(@opts)
32
+ end
33
+
34
+ def post_archives(archive_location)
35
+ web_browser_ids = []
36
+ #TODO: Cache the web prowser for each archive location.
37
+ Dir.foreach(archive_location) do |file|
38
+ next if file == '.' or file == '..'
39
+ path = "#{archive_location}/#{file}"
40
+ if is_valid_page_archive? path
41
+ web_browser_ids << get_web_browser_info(parse_json_file("#{path}/metadata.json")[:browser])[:id]
42
+ end
43
+ end
44
+ update_chain_reaction(web_browser_ids: web_browser_ids)
45
+ dom_gun_reaction = create_dom_gun_reaction
46
+ Dir.foreach(archive_location) do |file|
47
+ next if file == '.' or file == '..'
48
+ path = "#{archive_location}/#{file}"
49
+ if is_valid_page_archive? path
50
+ payload = create_payload(path)
51
+ RestClient.put("#{chain_reaction_url}/dom_gun_reactions/#{dom_gun_reaction[:id]}", payload, content_type: 'application/json')
52
+ end
53
+ end
54
+ start_reaction(dom_gun_reaction[:id])
55
+ end
56
+
57
+ def start_reaction(id)
58
+ params = {
59
+ auth_token: auth_token,
60
+ }
61
+ RestClient.post("#{chain_reaction_url}/dom_gun_reactions/#{id}/start", params, content_type: 'application/json')
62
+ end
63
+
64
+ def baseline_browser
65
+ get_web_browser_info(@opts[:baseline_browser])
66
+ end
67
+
68
+ private
69
+
70
+ def get_web_browser_info(browser_info={})
71
+ parameters = {auth_token: auth_token,content_type: 'application/json', accept: :json}.merge(browser_info)
72
+ #TODO: Handle errors.
73
+ response = RestClient.get("#{Config.url}/api/v1/web_browsers", {params: parameters})
74
+ JSON.parse(response, symbolize_names: true)[:web_browsers].first
75
+ end
76
+
77
+ def update_chain_reaction(params)
78
+ RestClient.put(chain_reaction_url, params.merge(auth_token: auth_token), content_type: 'application/json')
79
+ end
80
+
81
+ def create_chain_reaction(opts)
82
+ payload = {
83
+ auth_token: auth_token,
84
+ analysis_only: true,
85
+ displacement_threshold: opts[:threshold] || 0.04,
86
+ baseline_browser_id: baseline_browser[:id]
87
+ }.to_json
88
+ response = RestClient.post(resource_url, payload, content_type: 'application/json') do |response|
89
+ unless response.code == 200 && response.body.match(/chain_reaction/)
90
+ raise "ChainReaction initialization failed with code #{response.code} : #{response.body}"
91
+ end
92
+ response
93
+ end
94
+ JSON.parse(response, symbolize_names: true)[:chain_reaction]
95
+ end
96
+
97
+ def create_dom_gun_reaction
98
+ payload = {
99
+ chain_reaction_id: id,
100
+ auth_token: auth_token,
101
+ page_url: page_url
102
+ }
103
+ dom_gun = RestClient.post("#{chain_reaction_url}/dom_gun_reactions", payload, content_type: 'application/json')
104
+ JSON.parse(dom_gun, symbolize_names: true)
105
+ end
106
+
107
+ def create_payload(path)
108
+ meta_data = parse_json_file("#{path}/metadata.json")
109
+ {
110
+ auth_token: auth_token,
111
+ meta_data: meta_data,
112
+ web_browser_id: get_web_browser_info(meta_data[:browser])[:id],
113
+ dom_elements: File.open("#{path}/dom.json", 'rb') {|f| f.read},
114
+ page_source: File.open("#{path}/source.html") {|f| f.read},
115
+ file: File.open("#{path}/screenshot.png")
116
+ }
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,24 @@
1
+ module DomReactorRedGlass
2
+ class Config
3
+ #DOMREACTOR_INIT_CHAIN_REACTION_URL = 'http://domreactor.com'
4
+ DOMREACTOR_INIT_CHAIN_REACTION_URL = 'http://localhost:3000'
5
+
6
+ class << self
7
+ def url
8
+ @url || DOMREACTOR_INIT_CHAIN_REACTION_URL
9
+ end
10
+
11
+ def url=(url)
12
+ @url = url
13
+ end
14
+
15
+ def auth_token
16
+ @auth_token
17
+ end
18
+
19
+ def auth_token=(auth_token)
20
+ @auth_token = auth_token
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module DomReactorRedGlass
2
+ VERSION = "0.0.3"
3
+ end
@@ -1,22 +1,26 @@
1
- require 'json'
2
- require 'rest-client'
3
- require 'zip/zip'
1
+ require 'domreactor-redglass/chain_reaction'
2
+ require 'domreactor-redglass/config'
3
+ require 'domreactor-redglass/version'
4
4
 
5
5
  module DomReactorRedGlass
6
6
 
7
7
  REQUIRED_ARCHIVE_FILES = %w(dom.json metadata.json screenshot.png source.html)
8
8
  REQUIRED_BASELINE_BROWSER_CONFIG_KEYS = [:name, :version, :platform]
9
- DOMREACTOR_INIT_CHAIN_REACTION_URL = 'http://domreactor.com/api/v1/chain_reactions'
10
9
 
11
- def create_chain_reaction(api_token, archive_location, config)
10
+ def auth_token=(auth_token)
11
+ Config.auth_token=auth_token
12
+ end
13
+
14
+ def auth_token
15
+ Config.auth_token
16
+ end
17
+
18
+ def create_chain_reaction(page_url, archive_location, opts)
12
19
  detect_archive_location archive_location
13
20
  detect_min_archive_quota archive_location
14
- detect_baseline_browser archive_location, config
15
- id = init_chain_reaction(api_token, archive_location, config)[:chain_reaction][:id]
16
- api_url = config[:api_url] || DOMREACTOR_INIT_CHAIN_REACTION_URL
17
- post_url = "#{api_url}/#{id}"
18
- archive_files = zip_archives archive_location
19
- post_archives(api_token, post_url, archive_files)
21
+ detect_baseline_browser archive_location, opts
22
+ @chain_reaction = ChainReaction.new(page_url, opts)
23
+ @chain_reaction.post_archives(archive_location)
20
24
  end
21
25
 
22
26
  def detect_archive_location(archive_location)
@@ -87,55 +91,8 @@ module DomReactorRedGlass
87
91
  metadata
88
92
  end
89
93
 
90
- def init_chain_reaction(api_token, archive_location, config)
91
- payload = {
92
- auth_token: api_token,
93
- analysis_only: true,
94
- threshold: config[:threshold] || 0.04,
95
- baseline_browser: config[:baseline_browser],
96
- archive_count: sum_archive_count(archive_location),
97
- metadata: archive_metadata(archive_location)
98
- }.to_json
99
- api_url = config[:api_url] || DOMREACTOR_INIT_CHAIN_REACTION_URL
100
- response = RestClient.post(api_url, payload, content_type: 'application/json') do |response|
101
- unless response.code == 200 && response.body.match(/chain_reaction/)
102
- raise "ChainReaction initialization failed with code #{response.code} : #{response.body}"
103
- end
104
- response
105
- end
106
- JSON.parse(response, symbolize_names: true)
107
- end
108
-
109
- def zip_archives(archive_location)
110
- zip_files = []
111
- Dir.foreach(archive_location) do |file|
112
- next if file == '.' or file == '..'
113
- path = "#{archive_location}/#{file}"
114
- if is_valid_page_archive? path
115
- zip_file = "#{path}.zip"
116
- zip_files << zip_file
117
- Zip::ZipFile.open(zip_file, Zip::ZipFile::CREATE) do |zipfile|
118
- REQUIRED_ARCHIVE_FILES.each do |filename|
119
- zipfile.add(filename, path + '/' + filename)
120
- end
121
- end
122
- end
123
- end
124
- zip_files
125
- end
126
-
127
- def post_archives(api_token, post_url, zip_files)
128
- zip_files.each do |file|
129
- payload = {
130
- auth_token: api_token,
131
- file: File.new(file, 'rb')
132
- }
133
- RestClient.put(post_url, payload)
134
- end
135
- end
136
-
137
94
  def parse_json_file(path)
138
95
  json_str = File.open(path, 'rb') {|f| f.read}
139
96
  JSON.parse(json_str, symbolize_names: true)
140
97
  end
141
- end
98
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe DomReactorRedGlass::ChainReaction do
4
+ let(:chain_reaction) { DomReactorRedGlass::ChainReaction.new('http://domreactor.com', {}) }
5
+ before do
6
+ #stub chain reaction creation call
7
+ RestClient.stub(:post) { {chain_reaction: {id: 42, some_info: 'yay'}}.to_json }
8
+ ChainReaction.any_instance.stub(:baseline_browser) { {id: 1} }
9
+ end
10
+
11
+ describe '#new' do
12
+ it 'posts to domreactor to create a chain reaction' do
13
+ expected_response = {chain_reaction: {id: 42, some_info: 'yay'}}
14
+ RestClient.should_receive(:post).once.and_return(expected_response.to_json)
15
+ chain_reaction.info.should eq(expected_response[:chain_reaction])
16
+ end
17
+ end
18
+
19
+ describe '#info' do
20
+ it "gets the id from the info" do
21
+ chain_reaction.info.should eq({id: 42, some_info: 'yay'})
22
+ end
23
+ end
24
+
25
+ describe '#post_archives' do
26
+ before do
27
+ chain_reaction.stub(:create_dom_gun_reaction) { {id: 81, stuff: true} }
28
+ chain_reaction.stub(:get_web_browser_info).exactly(4).times.
29
+ with({:name=>"firefox", :version=>"20.0", :platform=>"darwin"}) { {id: 22, browser: 'firefox'} }
30
+ RestClient.should_receive(:put).exactly(3).times
31
+ RestClient.should_receive(:post).once
32
+ end
33
+ it 'creates a dom gun reaction' do
34
+ chain_reaction.post_archives("#{SPEC_ROOT}/data/valid_archive")
35
+ end
36
+ end
37
+ describe '#id' do
38
+ it "gets the id from the info" do
39
+ chain_reaction.id.should eq(42)
40
+ end
41
+ end
42
+ end
43
+