Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add browser local/session storage option #1853

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/capybara.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ class << self
# [test_id = Symbol/String/nil] Optional attribute to match locator aginst with builtin selectors along with id (Default: nil)
# [predicates_wait = Boolean] Whether Capybaras predicate matchers use waiting behavior by default (Default: true)
# [default_normalize_ws = Boolean] Whether text predicates and matchers use normalize whitespace behaviour (Default: false)
# [clear_storage_on_reset = [Boolean, :local, :storage]] Clear localStorage and sessionStorage when session is reset (Default: false)
#

# === DSL Options
#
# when using capybara/dsl, the following options are also available:
Expand Down Expand Up @@ -513,6 +515,7 @@ module Selenium; end
config.test_id = nil
config.predicates_wait = true
config.default_normalize_ws = false
config.clear_storage_on_reset = false
end

Capybara.register_driver :rack_test do |app|
Expand Down
10 changes: 10 additions & 0 deletions lib/capybara/driver/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ def wait?

def reset!; end

##
#
# Clear local and/or session storage
# @param only_clear [nil, :local, :session]
#
#
def clear_storage(only_clear = nil)
yield
end

def needs_server?
false
end
Expand Down
4 changes: 2 additions & 2 deletions lib/capybara/selenium/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def native_args(args)

def clear_browser_state
delete_all_cookies
clear_storage
deprecated_clear_storage
rescue Selenium::WebDriver::Error::UnhandledError # rubocop:disable Lint/HandleExceptions
# delete_all_cookies fails when we've previously gone
# to about:blank, so we rescue this error and do nothing
Expand All @@ -259,7 +259,7 @@ def delete_all_cookies
@browser.manage.delete_all_cookies
end

def clear_storage
def deprecated_clear_storage
clear_session_storage if options[:clear_session_storage]
clear_local_storage if options[:clear_local_storage]
end
Expand Down
16 changes: 16 additions & 0 deletions lib/capybara/server/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ def clear_error
def call(env)
if env['PATH_INFO'] == '/__identify__'
[200, {}, [@app.object_id.to_s]]
elsif (m = env["PATH_INFO"].match(%r{/__clear_storage__(?:/(local|session))?}))
[200, {}, [<<~HTML
<html>
<head>
<title>Clear Storage</title>
<script>
#{'if (window.localStorage) window.localStorage.clear();' if m[1].nil? || m[1] == 'local'}
#{'if (window.sessionStorage) window.sessionStorage.clear();' if m[1].nil? || m[1] == 'session'}
</script>
</head>
<body>
Clearing Storage
</body>
</html>
HTML
]]
else
@counter.increment
begin
Expand Down
52 changes: 34 additions & 18 deletions lib/capybara/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ def driver
#
def reset!
if @touched
if @server && Capybara.clear_storage_on_reset
what_to_clear = Capybara.clear_storage_on_reset
what_to_clear = nil if what_to_clear == true
clear_storage(what_to_clear)
end
driver.reset!
@touched = false
end
Expand Down Expand Up @@ -245,24 +250,7 @@ def current_url
def visit(visit_uri)
raise_server_error!
@touched = true

visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
base_uri = ::Addressable::URI.parse(config.app_host || server_url)

if base_uri && [nil, 'http', 'https'].include?(visit_uri.scheme)
if visit_uri.relative?
visit_uri_parts = visit_uri.to_hash.delete_if { |_k, value| value.nil? }

# Useful to people deploying to a subdirectory
# and/or single page apps where only the url fragment changes
visit_uri_parts[:path] = base_uri.path + visit_uri.path

visit_uri = base_uri.merge(visit_uri_parts)
end
adjust_server_port(visit_uri)
end

driver.visit(visit_uri.to_s)
driver.visit(computed_uri(visit_uri).to_s)
end

##
Expand Down Expand Up @@ -854,6 +842,34 @@ def adjust_server_port(uri)
uri.port ||= @server.port if @server && config.always_include_port
end

def computed_uri(visit_uri)
visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
base_uri = ::Addressable::URI.parse(config.app_host || server_url)

if base_uri && [nil, 'http', 'https'].include?(visit_uri.scheme)
if visit_uri.relative?
visit_uri_parts = visit_uri.to_hash.delete_if { |_k, value| value.nil? }

# Useful to people deploying to a subdirectory
# and/or single page apps where only the url fragment changes
visit_uri_parts[:path] = base_uri.path + visit_uri.path

visit_uri = base_uri.merge(visit_uri_parts)
end
adjust_server_port(visit_uri)
end
visit_uri
end

def clear_storage(only_clear = nil)
driver.clear_storage(only_clear) do
uri = "/__clear_storage__#{"/#{only_clear}" unless only_clear.nil?}"
driver.visit(computed_uri(uri))
end
rescue StandardError => e
warn "Session storage may not have been cleared due to #{e.message}"
end

def _find_frame(*args)
return find(:frame) if args.length.zero?

Expand Down
6 changes: 4 additions & 2 deletions lib/capybara/session/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ class SessionConfig
OPTIONS = %i[always_include_port run_server default_selector default_max_wait_time ignore_hidden_elements
automatic_reload match exact exact_text raise_server_errors visible_text_only
automatic_label_click enable_aria_label save_path asset_host default_host app_host
server_host server_port server_errors default_set_options disable_animation test_id
predicates_wait default_normalize_ws].freeze
server_host server_port server_errors default_set_options disable_animation clear_storage_on_reset
test_id predicates_wait default_normalize_ws].freeze

attr_accessor(*OPTIONS)

Expand Down Expand Up @@ -59,6 +59,8 @@ class SessionConfig
# See {Capybara.configure}
# @!method default_normalize_ws
# See {Capybara.configure}
# @!method clear_storage_on_reset
# See {Capybara.configure}

remove_method :server_host

Expand Down
41 changes: 41 additions & 0 deletions lib/capybara/spec/session/reset_session_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,45 @@
@session.visit('/')
expect(@session.current_path).to eq('/')
end

context "Capybara.clear_storage_on_reset" do
it "resets all storage when true", requires: [:server] do
Capybara.clear_storage_on_reset = true
@session.visit("/set_storage")
expect(@session.evaluate_script(
"[window.localStorage.getItem('capybara'), window.sessionStorage.getItem('capybara_unload')]"
)).to eq %w[42 42]
@session.reset!
@session.visit("/")
expect(@session.evaluate_script(
"[window.localStorage.getItem('capybara'), window.sessionStorage.getItem('capybara_unload')]"
)).to eq [nil, nil]
end

it "resets local storage when :local", requires: [:server] do
Capybara.clear_storage_on_reset = :local
@session.visit("/set_storage")
expect(@session.evaluate_script(
"[window.localStorage.getItem('capybara'), window.sessionStorage.getItem('capybara_unload')]"
)).to eq %w[42 42]
@session.reset!
@session.visit("/")
expect(@session.evaluate_script(
"[window.localStorage.getItem('capybara'), window.sessionStorage.getItem('capybara_unload')]"
)).to eq [nil, '43']
end

it "resets session storage when :session", requires: [:server] do
Capybara.clear_storage_on_reset = :session
@session.visit("/set_storage")
expect(@session.evaluate_script(
"[window.localStorage.getItem('capybara'), window.sessionStorage.getItem('capybara_unload')]"
)).to eq %w[42 42]
@session.reset!
@session.visit("/")
expect(@session.evaluate_script(
"[window.localStorage.getItem('capybara'), window.sessionStorage.getItem('capybara_unload')]"
)).to eq ['42', nil]
end
end
end
1 change: 1 addition & 0 deletions lib/capybara/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def reset!
Capybara.test_id = nil
Capybara.predicates_wait = true
Capybara.default_normalize_ws = false
Capybara.clear_storage_on_reset = true
reset_threadsafe
end

Expand Down
18 changes: 18 additions & 0 deletions lib/capybara/spec/views/set_storage.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<html>
<head>
<script>
window.localStorage.setItem("capybara", 42);
window.sessionStorage.setItem("capybara", 42);
window.localStorage.setItem("capybara_unload", 42);
window.sessionStorage.setItem("capybara_unload", 42);

window.onbeforeunload = function(e) {
window.localStorage.setItem("capybara_unload", 43);
window.sessionStorage.setItem("capybara_unload", 43);
};
</script>
</head>
<body>
<div>This sets storage</div>
</body>
</html>
12 changes: 9 additions & 3 deletions spec/selenium_spec_chrome.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,15 @@ module TestSessions
include_examples 'Capybara::Session', TestSessions::Chrome, CHROME_DRIVER
include_examples Capybara::RSpecMatchers, TestSessions::Chrome, CHROME_DRIVER

context 'storage' do
describe '#reset!' do
it 'does not clear either storage by default' do
context "storage" do
describe "#reset!" do
before do
# TODO: remove these tests
# only test the driver resetting
Capybara.clear_storage_on_reset = false
end

it "does not clear either storage by default" do
@session = TestSessions::Chrome
@session.visit('/with_js')
@session.find(:css, '#set-storage').click
Expand Down
12 changes: 9 additions & 3 deletions spec/selenium_spec_marionette.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,15 @@ module TestSessions
end
end

context 'storage' do
describe '#reset!' do
it 'does not clear either storage by default' do
context "storage" do
describe "#reset!" do
before do
# TODO: remove these tests
# only test the driver resetting
Capybara.clear_storage_on_reset = false
end

it "does not clear either storage by default" do
@session = TestSessions::SeleniumMarionette
@session.visit('/with_js')
@session.find(:css, '#set-storage').click
Expand Down