WebAccount
Description
Easily log into web accounts by supplying just a username and password, the URL of the login page, and a few selectors for page elements to a sub-class of WebAccount.
Installation
Add this line to your application's Gemfile:
gem 'webaccount'And then execute:
$ bundleOr install it yourself as:
$ gem install webaccountUsage
Instantiation
1. With implicit defaults
require 'webaccount'
class MyAccount < WebAccount; end
MyAccount.new(username: username, password: password)
# => #<MyAccount @username=username, @password=password, @login_page_url=nil,
# @login_page_password_field_selector=nil, @login_page_username_field_selector=nil,
# @login_page_submit_button_selector=nil,
# @logout_button_selector=nil, @logged_out_xpath=nil>2. With explicit defaults, then in the comment the ultimate defaults if those values remain nil
require 'webaccount'
class MyAccount < WebAccount
PAGE_SELECTORS = {
login_page_url: nil, # There are no defaults, so something like 'https://mysite.com/login' is required.
login_page_username_field_selector: nil, # Will default to: {name: 'username'}
login_page_password_field_selector: nil, # Will default to: {name: 'password'}
login_page_submit_button_selector: nil, # Will default to: {xpath: '//input[@type="submit" and @value="Login"]'}
logout_button_selector: nil, # Will default to: {xpath: '//input[@type="submit" and @value="Logout"]'}
logged_out_xpath: nil, # No default value for this, and logout and shutdown require it.
}
end
MyAccount.new(
**{username: username, password: password}\
.merge(MyAccount::PAGE_SELECTORS)
)
# => #<MyAccount @username=username, @password=password...>3. With supplied values
require 'webaccount'
class MyAccount < WebAccount
PAGE_SELECTORS = {
login_page_url: 'https://mysite.com/login',
login_page_username_field_selector: {id: 'username'},
login_page_password_field_selector: {id: 'password'},
login_page_submit_button_selector: {xpath: '//button[@type="submit" and @value="Login"]'},
logout_button_selector: {css: '#button-logout'},
logged_out_xpath: '//div[@class="logged-out"]',
}
end
MyAccount.new(
**{username: username, password: password}
.merge(MyAccount::PAGE_SELECTORS)
)
# => #<MyAccount @username=username, @password=password...>Choosing a browser
browser is passed to Selenium::WebDriver.for, so it accepts whatever Selenium does: :chrome, :firefox, :safari, :edge, :ie. It defaults to :chrome, and a String or a capitalised Symbol is fine.
MyAccount.new(username: username, password: password, browser: :firefox)Driver options
driver_options is passed to Selenium::WebDriver.for as options:. It defaults to nil, which is what Selenium itself defaults to, so leaving it alone gets you a stock browser.
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('--headless=new')
MyAccount.new(username: username, password: password, driver_options: options)Or in a sub-class, if every instance should be configured the same way:
class MyAccount < WebAccount
private
def default_driver_options
Selenium::WebDriver::Chrome::Options.new.tap do |options|
options.add_argument('--headless=new')
end
end
endTimeouts
There are two, both overridable in a sub-class:
| Method | Default | Waits for |
|---|---|---|
default_find_timeout |
5s | An element on a page which has already loaded. |
default_wait_timeout |
30s | Navigation: a submit, the authentication behind it, and the redirect. |
class SlowAccount < WebAccount
private
def default_wait_timeout
60
end
endLogging in, out, and shutting down
require 'webaccount'
class MyAccount < WebAccount; end
my_account = MyAccount.new(username: username, password: password)
my_account.login
my_account.logout
my_account.shutdown # If necessary it logs out and then kills the selenium session.Driving the browser once logged in
driver is the logged-in browser, a Selenium::WebDriver::Driver, for whatever WebAccount doesn't do itself: fetching pages behind the login, reading them, or taking screenshots of them.
my_account = MyAccount.new(username: username, password: password)
my_account.login
my_account.driver.get('https://mysite.com/members/page')
my_account.driver.page_source
my_account.driver.save_screenshot('page.png')
my_account.shutdownstill_logged_in? says whether the page now showing still has the logout button, at once rather than after waiting, so that something moving from page to page can tell when the session has ended, and log in again:
my_account.driver.get(url)
my_account.login unless my_account.still_logged_in?Overriding the login method to customise logins
require 'webaccount'
class MyAccount < WebAccount
def login
# Put custom login code here.
end
endCreate new methods for selection and navigation
Prefer find over driver.find_element: it waits up to default_find_timeout for the element, whereas driver.find_element raises the moment the element isn't there.
require 'webaccount'
class MyAccount < WebAccount
def list_page_link
find(id: 'list')
end
def list_page
login unless logged_in?
list_page_link.click
end
end
my_account = MyAccount.new(username: username, password: password)
my_account.list_pageContributing
- Fork it: https://github.com/thoran/webaccount/fork
- Create your feature branch:
git checkout -b my-new-feature - Commit your changes:
git commit -am 'Add some feature' - Push to the branch:
git push origin my-new-feature - Create a new pull request
License
MIT