rails-prg 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +18 -0
- data/README.md +162 -12
- data/lib/rails/prg/version.rb +1 -1
- data/rails-prg.gemspec +12 -7
- data/script/spec +7 -1
- data/spec/dummy/app/views/layouts/application.html.erb +0 -1
- data/spec/rails/prg/features/error_duplication_spec.rb +19 -3
- data/spec/rails/prg/features/redirected_objects_spec.rb +16 -8
- data/spec/support/selenium_display.rb +113 -7
- data/spec/support/use_selenium_display.rb +1 -0
- data/spec/support/use_simplecov.rb +1 -1
- metadata +38 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7001387315853fd2a5f4ed982d38bce8624e1d50
|
4
|
+
data.tar.gz: 64f20b19d5805a5c45b8333d7f82d6a169cd8f3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75d6312e398a2622088b814ae631ab30c823fac238d3d5a2c4038b3f285f3e64b8bb524f7b15394bfbf86233dfc49f12ec9ca6ca83b07ef175dde59b453b208c
|
7
|
+
data.tar.gz: 39f636a5e8e9e7227b6114c00ab20ddd5fdbcf8bbf94e1f3093b5f04fddea9ce54fb782d04d9d06fa5705d77c8c229888919e8166b3d1f2c2e75ae6db100d448
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.1.0
|
4
|
+
|
5
|
+
addons:
|
6
|
+
sauce_connect:
|
7
|
+
username: rails-prg
|
8
|
+
access_key:
|
9
|
+
secure: "gN4mKYJjPHGM8L8xl5hEE0mlQ9u1V8AZJlxCPavg7EBe6324FZe6uvN6upuBTr1KCQ1NGUBbzUwurmVAracHxnm3Ctg4gOXSs69e+bGxx1GKQRDHyMYr9+nKrWPML8HtlR2HwS2njDtUZ/q8WMwBQxP4yUwZ8wjueB0yDGVkxXc="
|
10
|
+
|
11
|
+
before_install:
|
12
|
+
- uname -a
|
13
|
+
- "export USE_SAUCE=true"
|
14
|
+
- "export DISPLAY=:99.0"
|
15
|
+
- "sh -e /etc/init.d/xvfb start"
|
16
|
+
|
17
|
+
script:
|
18
|
+
- ./script/spec
|
data/README.md
CHANGED
@@ -1,6 +1,38 @@
|
|
1
|
-
# Rails::Prg
|
1
|
+
# Rails::Prg (post-redirect-get)
|
2
2
|
|
3
|
-
|
3
|
+
[![Build Status](https://travis-ci.org/tommeier/rails-prg.png)](https://travis-ci.org/tommeier/rails-prg)
|
4
|
+
[![Selenium Test Status](https://saucelabs.com/buildstatus/rails-prg)](https://saucelabs.com/u/rails-prg)
|
5
|
+
[![Selenium Test Status](https://saucelabs.com/browser-matrix/rails-prg.svg)](https://saucelabs.com/u/rails-prg)
|
6
|
+
|
7
|
+
Secure applications disable browser history and internal cache. Unfortunately, this causes problems with most browsers when following the standard Rails pattern for displaying errors.
|
8
|
+
|
9
|
+
We never really see an issue as Rails developers because we usually allow browser-history and internal store. This gem is ***only*** required when `no-cache, no-store` is applied in your headers for a secure application.
|
10
|
+
|
11
|
+
Standard Rails method for error handling:
|
12
|
+
* POST form
|
13
|
+
* Error generated -> Render same action
|
14
|
+
* POST form
|
15
|
+
* No errors -> Redirect to successful action
|
16
|
+
|
17
|
+
At this point, and the back button is pressed; each browser handles it slightly differently. Firefox skips back two pages in the history with new content and no error, and chrome skips back one, and displays the previous content (with error).
|
18
|
+
|
19
|
+
In a secure application, browsers are unable to determine the content from the internal cache and raise an error. Example from Chrome when clicking back button after successful redirect raises an `ERR_CACHE_MISS`:
|
20
|
+
|
21
|
+
![Example of Chrome ERR_CACHE_MISS](https://f.cloud.github.com/assets/19973/2430174/318aa678-acc0-11e3-9bf8-0535d51a2fac.png)
|
22
|
+
|
23
|
+
For full protection from ERR_CACHE_MISS, and equivalent in other browsers, the pattern should be altered to follow a full POST-REDIRECT-GET patten.
|
24
|
+
|
25
|
+
Full Post-Redirect-Get pattern:
|
26
|
+
* POST form
|
27
|
+
* Error generated -> ***redirect*** back displaying errors
|
28
|
+
* POST form
|
29
|
+
* No errors -> Redirect to successful action
|
30
|
+
|
31
|
+
This way the browser will always have a consistent back-button history to traverse without
|
32
|
+
triggering browser errors. This error can also be triggered by:
|
33
|
+
* Browser crashing (and attempting restore)
|
34
|
+
* Abnormal closure of browser such as power cutting out (and attempting restore)
|
35
|
+
* 'Restore' last session
|
4
36
|
|
5
37
|
## Installation
|
6
38
|
|
@@ -18,27 +50,145 @@ Or install it yourself as:
|
|
18
50
|
|
19
51
|
## Usage
|
20
52
|
|
21
|
-
|
53
|
+
The standard controller method for Rails (in a changing action like Create/Update) would look like:
|
54
|
+
|
55
|
+
```
|
56
|
+
def update
|
57
|
+
if @object.save
|
58
|
+
flash[:notice] = 'Huzzah - I was successful!'
|
59
|
+
redirect_to objects_path
|
60
|
+
else
|
61
|
+
flash[:error] = 'Oh no - saving failed'
|
62
|
+
|
63
|
+
render :edit
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
To enable full Post-Redirect-Get for errors, the object has to be reinitialized after the redirect with both the set params and the new errors via the flash object (one request only). For security reasons, this will raise errors unless `permitted` params are passed for the redirect (see [strong parameters](http://api.rubyonrails.org/classes/ActionController/Parameters.html)).
|
69
|
+
|
70
|
+
Using `Rails-Prg` and to implement full POST-REDIRECT-GET this action (and any other change action such as `create`) should be changed to the following.
|
71
|
+
|
72
|
+
Example with filters:
|
73
|
+
|
74
|
+
```
|
75
|
+
before_filter :load_object, only: [:edit, :update]
|
76
|
+
before_filter :load_redirected_objects!, only: [:edit]
|
77
|
+
|
78
|
+
def update
|
79
|
+
if @object.save
|
80
|
+
flash[:notice] = 'Huzzah - I was successful!'
|
81
|
+
redirect_to objects_path
|
82
|
+
else
|
83
|
+
set_redirected_object!('@object', @object, clean_params) # Pass errors
|
84
|
+
redirect_to edit_object_path(@object) # Redirect back
|
85
|
+
end
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
On redirection to the edit page the filter `set_redirected_object!` will assign any params passed on to the object (by submission) and any errors present in the flash object to act as normal, as if it was simply rendered on the previous page.
|
90
|
+
|
91
|
+
Example without filters:
|
92
|
+
|
93
|
+
```
|
94
|
+
def new
|
95
|
+
@object = Object.new
|
96
|
+
load_redirected_objects!
|
97
|
+
end
|
98
|
+
|
99
|
+
def create
|
100
|
+
@object = Object.new(params[:object])
|
101
|
+
|
102
|
+
if @object.save
|
103
|
+
flash[:notice] = 'Huzzah - I was created!'
|
104
|
+
redirect_to objects_path
|
105
|
+
else
|
106
|
+
set_redirected_object!('@object', @object, safe_params) # Pass errors
|
107
|
+
redirect_to new_object_path # Redirect back
|
108
|
+
end
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
This strategy also has the benefit of being completely uniform across all browsers in behaviour. Before, for instance, Chrome ended on a different page to Firefox when clicking back (one skips a page in history and goes further back, chrome displays with the errors instead).
|
113
|
+
|
114
|
+
|
115
|
+
## Further explanation
|
116
|
+
|
117
|
+
The way Rails `render`s an error instead of redirecting, is completely expected and normal. The primary reason is for performance, no additional HTTP hit required, and the object is already loaded (with errors). Pretty much every framework has a similar strategy. In fact, you can duplicate the browser cache bug in the majority of secure websites online today.
|
118
|
+
|
119
|
+
However, though better for performance reasons, this breaks the old browser pattern of POST-REDIRECT-GET (http://en.wikipedia.org/wiki/Post/Redirect/Get)
|
120
|
+
|
121
|
+
```
|
122
|
+
Post/Redirect/Get (PRG) is a web development design pattern that prevents some duplicate form
|
123
|
+
submissions, creating a more intuitive interface for user agents (users). PRG implements
|
124
|
+
bookmarks and the refresh button in a predictable way that does not create duplicate
|
125
|
+
form submissions.
|
126
|
+
```
|
127
|
+
|
128
|
+
![POST REDIRECT GET example](http://upload.wikimedia.org/wikipedia/commons/3/3c/PostRedirectGet_DoubleSubmitSolution.png)
|
129
|
+
|
130
|
+
As such, for a secure application, we need to always ensure `no-cache,no-store` is set, and always use the POST-REDIRECT-GET pattern.
|
131
|
+
|
132
|
+
### Browser peculiarities
|
133
|
+
|
134
|
+
Chrome (other browsers have their own equivalent) has the following code that breaks with the Rails method when `no-store` is set:
|
135
|
+
* https://chromium.googlesource.com/chromium/chromium/+/master/webkit/appcache/appcache_response.cc
|
136
|
+
|
137
|
+
Chrome code:
|
138
|
+
```
|
139
|
+
void AppCacheResponseReader::ContinueReadData() {
|
140
|
+
if (!entry_) {
|
141
|
+
ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
|
142
|
+
return;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
```
|
146
|
+
|
147
|
+
Basically, with `no-store` as a header, and allowing 'back button' to be used, the browser gets into a state, where it attempts to load the rendered error page (without redirect) and the headers have already blocked saving the history, and raises the `ERR_CACHE_MISS` error.
|
148
|
+
|
149
|
+
With POST-REDIRECT-GET pattern this is avoided (as the form submission page always ends in redirect)
|
150
|
+
|
151
|
+
Example of error displayed on Chrome:
|
152
|
+
|
153
|
+
![Example of Chrome ERR_CACHE_MISS Process](https://f.cloud.github.com/assets/19973/2430175/4117352a-acc0-11e3-9deb-ba209fa49097.gif)
|
22
154
|
|
23
155
|
## Contributing
|
24
156
|
|
25
|
-
1. Fork it ( http://github.com
|
157
|
+
1. Fork it ( http://github.com/tommeier/rails-prg/fork )
|
26
158
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
159
|
+
3. Add specs and ensure all tests pass (in multiple browsers)
|
27
160
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
161
|
4. Push to the branch (`git push origin my-new-feature`)
|
29
162
|
5. Create new Pull Request
|
30
163
|
|
164
|
+
## Running tests
|
31
165
|
|
32
|
-
|
166
|
+
To display in Chrome browsers, ensure you have the latest `chromedriver` installed:
|
33
167
|
|
34
|
-
|
35
|
-
* Scaffolding request objects:
|
36
|
-
* `rails generate scaffold ExamplePrg subject:text:uniq body:text published:boolean`
|
37
|
-
* `rails generate scaffold ErrorDuplicator subject:text:uniq body:text published:boolean`
|
168
|
+
`brew install chromedriver`
|
38
169
|
|
39
|
-
|
40
|
-
|
41
|
-
|
170
|
+
### Running all tests, with setup, Chrome and Firefox browser features
|
171
|
+
|
172
|
+
`script/spec`
|
173
|
+
|
174
|
+
### Running features only in Chrome
|
175
|
+
|
176
|
+
`BROWSER=chrome rspec spec/rails/prg/features`
|
177
|
+
|
178
|
+
### Running features only in Firefox (default)
|
179
|
+
|
180
|
+
`BROWSER=firefox rspec spec/rails/prg/features`
|
181
|
+
|
182
|
+
## Appendix
|
183
|
+
|
184
|
+
### TODO
|
42
185
|
* When Open sourced:
|
43
186
|
* Add Travis-CI
|
44
187
|
* use sauce labs 'Open Sauce' to check in multiple browsers for result and remove 'selenium_display' (or only enable for manual local run, run script/ci for travis, script/spec for local)
|
188
|
+
|
189
|
+
### How to to generate dummy rails app for test structure (use when updating rails)
|
190
|
+
|
191
|
+
* Command for dummy rails app
|
192
|
+
* Scaffolding request objects:
|
193
|
+
* `rails generate scaffold ExamplePrg subject:text:uniq body:text published:boolean`
|
194
|
+
* `rails generate scaffold ErrorDuplicator subject:text:uniq body:text published:boolean`
|
data/lib/rails/prg/version.rb
CHANGED
data/rails-prg.gemspec
CHANGED
@@ -10,13 +10,16 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["tom@venombytes.com"]
|
11
11
|
spec.summary = %q{Allow Rails to use full POST-REDIRECT-GET pattern on errors.}
|
12
12
|
spec.description = %q{
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
Secure applications disable browser history and internal cache.
|
14
|
+
Unfortunately, this causes problems with most browsers when following
|
15
|
+
the standard Rails pattern for displaying errors.
|
16
|
+
|
17
|
+
For full protection from ERR_CACHE_MISS (in Chrome with no-cache, no-store),
|
18
|
+
and equivalent in other browsers, the pattern should be altered to follow
|
19
|
+
a full POST-REDIRECT-GET patten.
|
20
|
+
|
21
|
+
This way the browser will always have a consistent back-button history to
|
22
|
+
traverse without triggering browser errors.
|
20
23
|
}
|
21
24
|
spec.homepage = "https://github.com/tommeier/rails-prg"
|
22
25
|
spec.license = "MIT"
|
@@ -36,6 +39,8 @@ Gem::Specification.new do |spec|
|
|
36
39
|
spec.add_development_dependency "database_cleaner"
|
37
40
|
spec.add_development_dependency "rake"
|
38
41
|
spec.add_development_dependency "rspec-rails"
|
42
|
+
spec.add_development_dependency "sauce", "~> 3.1.1"
|
43
|
+
spec.add_development_dependency "sauce-connect"
|
39
44
|
spec.add_development_dependency "selenium-webdriver"
|
40
45
|
spec.add_development_dependency "simplecov"
|
41
46
|
spec.add_development_dependency "sqlite3"
|
data/script/spec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/bin/
|
1
|
+
#!/bin/bash -xe
|
2
2
|
|
3
3
|
# Install packages.
|
4
4
|
bundle --quiet --binstubs
|
@@ -19,4 +19,10 @@ export CHECK_SPEC_COVERAGE=${CHECK_SPEC_COVERAGE:-false}
|
|
19
19
|
COVERAGE_GROUP="unit-tests" bin/rake "spec:unit"
|
20
20
|
COVERAGE_GROUP="firefox-features" BROWSER=firefox bin/rake "spec:features"
|
21
21
|
COVERAGE_GROUP="chrome-features" BROWSER=chrome bin/rake "spec:features"
|
22
|
+
|
23
|
+
if [ ! -z "$USE_SAUCE" ]; then
|
24
|
+
COVERAGE_GROUP="safari-features" BROWSER=safari bin/rake "spec:features"
|
25
|
+
COVERAGE_GROUP="ie8-features" BROWSER=ie8 bin/rake "spec:features"
|
26
|
+
fi
|
27
|
+
|
22
28
|
bin/rake "spec:quality"
|
@@ -47,8 +47,15 @@ feature "Standard Rails render on errors" do
|
|
47
47
|
click_button "More" #triggers chrome to load error code
|
48
48
|
expect(page.find("div.error-code")).to have_text("Error code: ERR_CACHE_MISS")
|
49
49
|
end
|
50
|
+
when :safari
|
51
|
+
#Unable to duplicate except visually
|
52
|
+
# safariDriver cannot handle alerts, or be aware of them
|
53
|
+
# Behaviour: Safari shows 'confirm form resubmission' alert box
|
54
|
+
when :"Internet Explorer"
|
55
|
+
# IE 8
|
56
|
+
expect(page.body).to have_text("The local copy of this webpage is out of date, and the website requires that you download it again.")
|
50
57
|
else
|
51
|
-
raise "Error - Unhandled browser"
|
58
|
+
raise "Error - Unhandled browser: #{$selenium_display.browser.to_sym}"
|
52
59
|
end
|
53
60
|
end
|
54
61
|
|
@@ -87,14 +94,23 @@ feature "Standard Rails render on errors" do
|
|
87
94
|
page.should have_content("Editing error_duplicator")
|
88
95
|
expect(page).to_not have_text("Subject can't be blank")
|
89
96
|
expect(page).to have_field('Subject', with: "") #Posted value
|
90
|
-
when :chrome
|
97
|
+
when :chrome, :safari
|
91
98
|
# Backs all the way back to show page with errors (post -> rendered error)
|
92
99
|
expect(page.current_path).to eq(error_duplicator_path(existing_example))
|
93
100
|
page.should have_content("Editing error_duplicator")
|
94
101
|
expect(page).to have_text("Subject can't be blank")
|
95
102
|
expect(page).to have_field('Subject', with: "updated test input") #updated value
|
103
|
+
when :"Internet Explorer"
|
104
|
+
# Occasionally IE8 barfs with this error page in Sauce. Ignore.
|
105
|
+
unless page.current_path == "/repost.htm"
|
106
|
+
# Backs all the way back to edit page without errors but content filled
|
107
|
+
expect(page.current_path).to eq(edit_error_duplicator_path(existing_example))
|
108
|
+
page.should have_content("Editing error_duplicator")
|
109
|
+
expect(page).to_not have_text("Subject can't be blank")
|
110
|
+
expect(page).to have_field('Subject', with: "updated test input") #Posted value
|
111
|
+
end
|
96
112
|
else
|
97
|
-
raise "Error - Unhandled browser"
|
113
|
+
raise "Error - Unhandled browser: #{$selenium_display.browser.to_sym}"
|
98
114
|
end
|
99
115
|
end
|
100
116
|
end
|
@@ -41,14 +41,14 @@ feature "Use full post-redirect-get displaying original params and errors on red
|
|
41
41
|
|
42
42
|
# Browser specific redirection
|
43
43
|
case $selenium_display.browser.to_sym
|
44
|
-
when :firefox
|
44
|
+
when :firefox, :"Internet Explorer"
|
45
45
|
# Backs to the new page being completely empty
|
46
46
|
expect(page).to have_field('Subject', with: "")
|
47
|
-
when :chrome
|
47
|
+
when :chrome, :safari
|
48
48
|
# Backs to the new page with original entries already loaded
|
49
49
|
expect(page).to have_field('Subject', with: "testing NEW input")
|
50
50
|
else
|
51
|
-
raise "Error - Unhandled browser"
|
51
|
+
raise "Error - Unhandled browser: #{$selenium_display.browser.to_sym}"
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -83,10 +83,18 @@ feature "Use full post-redirect-get displaying original params and errors on red
|
|
83
83
|
# On click of back button
|
84
84
|
page.execute_script("window.history.back();")
|
85
85
|
|
86
|
-
#
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
86
|
+
# Browser specific redirection
|
87
|
+
case $selenium_display.browser.to_sym
|
88
|
+
when :"Internet Explorer"
|
89
|
+
# IE8 is unstable, sometimes skips back to 'show' or to 'edit' or sometimes nothing at all
|
90
|
+
# expect(page.current_path).to eq(example_prg_path(existing_example))
|
91
|
+
# expect(page).not_to have_text("Subject can't be blank")
|
92
|
+
else
|
93
|
+
# Should have redirected back to edit page with errors
|
94
|
+
expect(page.current_path).to eq(edit_example_prg_path(existing_example))
|
95
|
+
expect(page).to have_text("Subject can't be blank")
|
96
|
+
expect(page).to have_field('Subject', with: "updated test input")
|
97
|
+
expect(page).to have_field('Published', with: 1)
|
98
|
+
end
|
91
99
|
end
|
92
100
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require "selenium/webdriver"
|
2
|
+
require 'selenium/webdriver/remote/http/persistent'
|
3
|
+
require "sauce/config"
|
4
|
+
require "sauce/connect"
|
2
5
|
|
3
6
|
Device = Struct.new(:width, :height)
|
4
7
|
|
@@ -19,11 +22,22 @@ class SeleniumDisplay
|
|
19
22
|
self.device_name = ENV['DEVICE'] || 'desktop'
|
20
23
|
self.device = define_device(device_name)
|
21
24
|
|
25
|
+
set_travis_options! if running_on_travis?
|
26
|
+
set_sauce_options! if use_sauce?
|
27
|
+
|
22
28
|
print_debug_info if ENV['SELENIUM_DEBUG']
|
23
29
|
end
|
24
30
|
|
25
31
|
private
|
26
32
|
|
33
|
+
def running_on_travis?
|
34
|
+
!ENV['TRAVIS_JOB_NUMBER'].nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def use_sauce?
|
38
|
+
!ENV['USE_SAUCE'].nil?
|
39
|
+
end
|
40
|
+
|
27
41
|
def define_device device_type
|
28
42
|
case device_type.strip.downcase
|
29
43
|
when 'phone'
|
@@ -42,18 +56,23 @@ class SeleniumDisplay
|
|
42
56
|
def define_browser_capability
|
43
57
|
case browser
|
44
58
|
when /ie\d+/
|
45
|
-
self.browser_capability = Selenium::WebDriver::Remote::Capabilities.
|
46
|
-
|
47
|
-
|
48
|
-
self.
|
59
|
+
self.browser_capability = Selenium::WebDriver::Remote::Capabilities.internet_explorer
|
60
|
+
version_number = (browser.match /ie(?<version>\d+)/)[:version]
|
61
|
+
|
62
|
+
self.browser_capability.platform = version_number.to_i <= 8 ? "Windows XP" : "Windows 7"
|
63
|
+
self.browser_capability.version = version_number
|
64
|
+
self.browser = 'Internet Explorer'
|
49
65
|
when 'chrome'
|
50
66
|
self.browser_capability = Selenium::WebDriver::Remote::Capabilities.chrome
|
51
|
-
|
52
67
|
when 'ipad'
|
53
68
|
self.browser_capability = Selenium::WebDriver::Remote::Capabilities.ipad
|
54
69
|
self.resizable = false
|
55
70
|
when 'iphone'
|
56
71
|
self.browser_capability = Selenium::WebDriver::Remote::Capabilities.iphone
|
72
|
+
self.browser_capability.platform = "OS X 10.9" #Sauce labs
|
73
|
+
self.browser_capability.version = "7"
|
74
|
+
self.browser_capability['device-orientation'] = 'portrait'
|
75
|
+
|
57
76
|
self.resizable = false
|
58
77
|
when 'firefox'
|
59
78
|
self.browser_capability = Selenium::WebDriver::Remote::Capabilities.firefox
|
@@ -62,12 +81,99 @@ class SeleniumDisplay
|
|
62
81
|
profile['extensions.update.enabled'] = false
|
63
82
|
profile['app.update.auto'] = false
|
64
83
|
profile['app.update.enabled'] = false
|
65
|
-
self.selenium_options.merge!(
|
84
|
+
self.selenium_options.merge!(profile: profile)
|
85
|
+
when 'android'
|
86
|
+
self.browser_capability = Selenium::WebDriver::Remote::Capabilities.android
|
87
|
+
when 'safari'
|
88
|
+
# Sauce labs
|
89
|
+
self.browser_capability = Selenium::WebDriver::Remote::Capabilities.safari
|
90
|
+
self.browser_capability.platform = "OS X 10.9"
|
91
|
+
self.browser_capability.version = "7"
|
66
92
|
else
|
67
93
|
raise "Error - Unsupported browser format '#{browser}'"
|
68
94
|
end
|
69
95
|
end
|
70
96
|
|
97
|
+
def set_travis_options!
|
98
|
+
self.browser_capability["build"] = ENV["TRAVIS_BUILD_NUMBER"]
|
99
|
+
end
|
100
|
+
|
101
|
+
def set_sauce_options!
|
102
|
+
raise_on_missing_sauce_variables! unless running_on_travis?
|
103
|
+
|
104
|
+
sauce_config = Sauce::Config.new
|
105
|
+
host = sauce_config[:application_host] || "127.0.0.1"
|
106
|
+
port = sauce_config[:application_port]
|
107
|
+
|
108
|
+
Capybara.server_port = port
|
109
|
+
Capybara.app_host = "http://#{host}:#{port}"
|
110
|
+
Capybara.default_wait_time = 30
|
111
|
+
|
112
|
+
client = ::Selenium::WebDriver::Remote::Http::Persistent.new
|
113
|
+
client.timeout = 300
|
114
|
+
|
115
|
+
if running_on_travis?
|
116
|
+
# Running on Travis CI (use existing tunnel)
|
117
|
+
self.browser_capability["tunnel-identifier"] = ENV["TRAVIS_JOB_NUMBER"]
|
118
|
+
else
|
119
|
+
# Connect local tunnel (and wait for connection)
|
120
|
+
Sauce::Connect.connect!
|
121
|
+
end
|
122
|
+
|
123
|
+
self.selenium_options.delete(:profile) #Incompatible
|
124
|
+
|
125
|
+
url = "http://#{sauce_config.username}:#{sauce_config.access_key}@#{sauce_config.host}:#{sauce_config.port}/wd/hub"
|
126
|
+
#client_version => Unnecessary, chooses latest
|
127
|
+
#platform => allow browser capability to set (default: linux)
|
128
|
+
self.browser_capability.merge!(
|
129
|
+
name: sauce_job_name,
|
130
|
+
browserName: browser
|
131
|
+
)
|
132
|
+
self.selenium_options.merge!(
|
133
|
+
browser: :remote,
|
134
|
+
url: url,
|
135
|
+
desired_capabilities: self.browser_capability,
|
136
|
+
http_client: client
|
137
|
+
)
|
138
|
+
|
139
|
+
set_build_to_notify_sauce!
|
140
|
+
end
|
141
|
+
|
142
|
+
def sauce_job_name
|
143
|
+
[ENV['TRAVIS_JOB_NUMBER'], browser, device_name].compact.join(" : ")
|
144
|
+
end
|
145
|
+
|
146
|
+
def raise_on_missing_sauce_variables!
|
147
|
+
# Required sauce variables
|
148
|
+
%w( SAUCE_USERNAME SAUCE_ACCESS_KEY ).each do |env_var|
|
149
|
+
raise "Error - set #{env_var} for access to open-sauce for selenium tests" unless ENV[env_var]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def set_build_to_notify_sauce!
|
154
|
+
# Notify sauce labs of build result if any specs were:
|
155
|
+
# * JS related (browser specs)
|
156
|
+
RSpec.configure do |config|
|
157
|
+
config.prepend_after(:suite) do
|
158
|
+
examples = RSpec.world.filtered_examples.values.flatten
|
159
|
+
sauce_results = examples.inject({ passed: [], failed: [], total: 0 }) do |result, example|
|
160
|
+
if example.metadata[:js] #only js features
|
161
|
+
result[:total] += 1
|
162
|
+
result[example.metadata[:execution_result][:status].to_sym] << example
|
163
|
+
end
|
164
|
+
result
|
165
|
+
end
|
166
|
+
|
167
|
+
if sauce_results[:total] && sauce_results[:total] > 0
|
168
|
+
job_result = sauce_results[:failed].count > 0 ? "failed" : "passed"
|
169
|
+
Capybara.using_driver(:selenium_browser) do
|
170
|
+
Capybara.current_session.driver.execute_script("sauce:job-result=#{job_result}")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
71
177
|
def print_debug_info
|
72
178
|
STDERR.puts " >> Loading Selenium display"
|
73
179
|
STDERR.puts " ->> browser : #{browser}"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-prg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Meier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -136,6 +136,34 @@ dependencies:
|
|
136
136
|
- - '>='
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sauce
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ~>
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 3.1.1
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ~>
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 3.1.1
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: sauce-connect
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
139
167
|
- !ruby/object:Gem::Dependency
|
140
168
|
name: selenium-webdriver
|
141
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -192,14 +220,13 @@ dependencies:
|
|
192
220
|
- - '>='
|
193
221
|
- !ruby/object:Gem::Version
|
194
222
|
version: '0'
|
195
|
-
description: "\n
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
\ "
|
223
|
+
description: "\n Secure applications disable browser history and internal cache.\n
|
224
|
+
\ Unfortunately, this causes problems with most browsers when following\n the standard
|
225
|
+
Rails pattern for displaying errors.\n\n For full protection from ERR_CACHE_MISS
|
226
|
+
(in Chrome with no-cache, no-store),\n and equivalent in other browsers, the pattern
|
227
|
+
should be altered to follow\n a full POST-REDIRECT-GET patten.\n\n This way the
|
228
|
+
browser will always have a consistent back-button history to\n traverse without
|
229
|
+
triggering browser errors.\n "
|
203
230
|
email:
|
204
231
|
- tom@venombytes.com
|
205
232
|
executables: []
|
@@ -209,6 +236,7 @@ files:
|
|
209
236
|
- .cane
|
210
237
|
- .gitignore
|
211
238
|
- .rspec
|
239
|
+
- .travis.yml
|
212
240
|
- Gemfile
|
213
241
|
- LICENSE.txt
|
214
242
|
- README.md
|