dinero 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 54db7e4b1d206d902c99a0bc1240c2e9f9c69cde
4
- data.tar.gz: 0e5c793a6055e095edd3849a91ddd7368fca5e48
3
+ metadata.gz: 00ee87276f3b118d80e13ae95262189b13f2f5ce
4
+ data.tar.gz: b9a357d02cc2517f326f7c15455713c3eacfb091
5
5
  SHA512:
6
- metadata.gz: 2794d5f6d5338f69d586e9a1b7ba146a6d597b6e6eeb52d4cc1df3e207200db5591888cd3965723b2f0423be30fbbb1617e7ca97f2c3ec33ed82fcde8ed406ac
7
- data.tar.gz: cce7bb0830d8644a4539d728dc882cca84e5ff7f082283f77027479b5fb32e4547a808684e6c52f880782808befaaec2a2a4cc2943f2a97f26a4f7dcbdcf4b9c
6
+ metadata.gz: fe0825fa56a8d1c86758200c3632b711b8b2e75e82ca3b86b526a4da2527f4672d55659f15300075ad5a061ee0531fc84b889657395d681eb8b3f02cf84ff22c
7
+ data.tar.gz: c2a802e4c94235d4669539b4abbc56bbe063fe05909dea0767b24a2e3ce087a554298dfae8417ba74b8ed05b1533b1e8b6be951d71f4d08ff3067836bcbbf41c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dinero (0.0.3)
4
+ dinero (0.0.4)
5
5
  nokogiri (~> 1.6.6)
6
6
  selenium-webdriver (~> 3.0)
7
7
 
data/README.md CHANGED
@@ -29,6 +29,7 @@ The following banks are implemented:
29
29
  * Capital One - https://capitalone.com (only U.S. credit card logins -- there's also banking, loans, investing, business, and Canada/UK credit cards)
30
30
  * Capital One 360 - https://capitalone360.com (formerly Ing Direct).
31
31
  * South State Bank - https://www.southstatebank.com/
32
+ * San Diego County Credit Union - https://www.sdccu.com/
32
33
 
33
34
  Currently, only Accounts balances are implemented. The following properties are available on each Account:
34
35
 
@@ -1,5 +1,6 @@
1
1
  module Dinero
2
2
  NUMERIC_REGEXP = /[\d|\-|\.]+/
3
+
3
4
  class Account
4
5
  attr_reader :account_type, :name, :name_other, :number, :balance, :available
5
6
  def initialize account_type, name, number, balance, available
@@ -10,6 +11,14 @@ module Dinero
10
11
  @number = number
11
12
  @balance = balance.scan(NUMERIC_REGEXP).join.to_f
12
13
  @available = available.scan(NUMERIC_REGEXP).join.to_f
14
+ @balance *= -1 if @account_type == :loan
15
+ end
16
+
17
+ %w(bank loan brokerage).each do |acct_type|
18
+ define_method "#{acct_type}_account?" do
19
+ self.account_type == acct_type.to_sym
20
+ end
13
21
  end
14
22
  end
23
+
15
24
  end
@@ -0,0 +1,116 @@
1
+ module Dinero
2
+ module Bank
3
+ # San Diego County Credit Union
4
+ class Sdccu < Base
5
+ LOGIN_URL = "https://internetbranch.sdccu.com/SDCCU/Login.aspx"
6
+ ACCOUNTS_SUMMARY_PATH = "/accounts"
7
+ CONNECTION_TIMEOUT = 10
8
+
9
+ def default_options
10
+ { timeout: CONNECTION_TIMEOUT, login_url: LOGIN_URL }
11
+ end
12
+
13
+ def post_username!
14
+ screenshot_on_error do
15
+ wait.until { connection.find_element(id: "ctlSignon_txtUserID") }
16
+ username_field = connection.find_element(id: "ctlSignon_txtUserID")
17
+ username_field.send_keys username
18
+
19
+ submit_button = connection.find_element(id: "ctlSignon_btnNext")
20
+ submit_button.click
21
+ end
22
+ end
23
+
24
+ def post_password!
25
+ screenshot_on_error do
26
+ wait.until { connection.find_element(id: "ctlSignon_txtPassword") }
27
+
28
+ password_field = connection.find_element(id: "ctlSignon_txtPassword")
29
+ password_field.send_keys password
30
+
31
+ login_btn = connection.find_element(id: "ctlSignon_btnLogin")
32
+ login_btn.click
33
+ end
34
+ end
35
+
36
+ def find_answer question
37
+ if q = security_questions.detect{ |qa| qa["question"] == question }
38
+ return q["answer"]
39
+ else
40
+ raise "Unknown security question: #{question.inspect}"
41
+ end
42
+ end
43
+
44
+ def post_security_answer!
45
+ return if on_accounts_summary_page?
46
+ screenshot_on_error do
47
+ wait.until { connection.find_element(id: "lblChallengeQuestion") }
48
+ question_text = connection.find_element(id: "lblChallengeQuestion").text
49
+ answer = find_answer question_text
50
+
51
+ answer_field = connection.find_element(id: "QuestionAnswer")
52
+ answer_field.send_keys answer
53
+
54
+ submit_button = logon_form.find_element(id:"btnSubmitAnswer")
55
+ submit_button.click
56
+ end
57
+ end
58
+
59
+ def post_credentials!
60
+ post_username!
61
+ post_password!
62
+ post_security_answer!
63
+ end
64
+
65
+ def after_successful_login
66
+ # the subdomain frequently changes, so capture the actual URL
67
+ # so we can return to the page if necessary.
68
+ @accounts_summary_url = connection.current_url
69
+ end
70
+
71
+ def on_accounts_summary_page?
72
+ connection.page_source =~ /Account Balances/
73
+ end
74
+
75
+ def goto_accounts_summary_page
76
+ return if authenticated? && on_accounts_summary_page?
77
+ authenticated? ? connection.navigate.to(@accounts_summary_url) : login!
78
+ end
79
+
80
+ def account_tables
81
+ accounts_summary_document.xpath("//div[@id='accountBalancesContainer_accountBalancesModule_accountList']//table")
82
+ end
83
+
84
+ def bank_accounts_table
85
+ account_tables[0]
86
+ end
87
+
88
+ def loan_accounts_table
89
+ account_tables[1]
90
+ end
91
+
92
+ def account_rows table, type
93
+ rows = table.xpath(".//tr").map{|m| m.xpath(".//td").map{|m| m.text.strip}}
94
+ rows.select{|s| s.size == 7}.map{|m| m.reject(&:empty?) << type}
95
+ end
96
+
97
+ def account_table_rows
98
+ account_rows(bank_accounts_table, :bank) + account_rows(loan_accounts_table, :loan)
99
+ end
100
+
101
+ # extract account data from the account summary page
102
+ def accounts
103
+ return @accounts if @accounts
104
+
105
+ @accounts = account_table_rows.map do |row|
106
+ acct_type = row.pop
107
+ number = row.shift
108
+ name = row.shift
109
+ balance = row.shift.scan(NUMERIC_REGEXP).join
110
+ available = acct_type == :loan ? "0.0" : balance
111
+ Account.new(acct_type, name, number, balance, available)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
data/lib/dinero/banks.rb CHANGED
@@ -30,9 +30,8 @@ module Dinero
30
30
 
31
31
  def establish_connection
32
32
  capabilities = Selenium::WebDriver::Remote::Capabilities.phantomjs(
33
- 'acceptSslCerts' => true,
34
33
  'phantomjs.page.settings.userAgent' => 'Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/33.0',
35
- 'service_args' => ['--ignore-ssl-errors=true']
34
+ 'service_args' => ['--ignore-ssl-errors=true', '--ssl-protocol=any']
36
35
  )
37
36
 
38
37
  driver = Selenium::WebDriver.for :phantomjs, :desired_capabilities => capabilities
@@ -1,3 +1,3 @@
1
1
  module Dinero
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/dinero.rb CHANGED
@@ -3,9 +3,12 @@ require 'nokogiri'
3
3
 
4
4
  require_relative 'dinero/version'
5
5
 
6
+ # Supported Banks
6
7
  require_relative 'dinero/banks'
7
8
  require_relative 'dinero/banks/capital_one'
8
9
  require_relative 'dinero/banks/capital_one_360'
9
10
  require_relative 'dinero/banks/south_state_bank'
11
+ require_relative 'dinero/banks/sdccu'
10
12
 
13
+ # Models
11
14
  require_relative 'dinero/account'
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ if bank_configured? :sdccu
4
+
5
+ RSpec.describe Dinero::Bank::Sdccu do
6
+ let(:bank_configuration) { bank_configurations[:sdccu] }
7
+ let(:account_types) { bank_configuration[:account_types].sort }
8
+ let(:accounts) { bank_configuration[:accounts] }
9
+
10
+ before(:all) do
11
+ VCR.use_cassette("accounts_sdccu", record: :new_episodes) do
12
+ @bank = Dinero::Bank::Sdccu.new(bank_configurations[:sdccu])
13
+ @bank.accounts
14
+ end
15
+ end
16
+
17
+ it "has security questions" do
18
+ expect(@bank.security_questions.count).to eq 4
19
+ end
20
+
21
+ it "finds favorite hobby answer" do
22
+ expect(@bank.find_answer("What is your favorite hobby?")).to eq "tennis"
23
+ end
24
+
25
+ it "posts credentials" do
26
+ expect(@bank.authenticated?).to be true
27
+ end
28
+
29
+ it "authenticates" do
30
+ @bank.login!
31
+ expect(@bank.authenticated?).to eq true
32
+ end
33
+
34
+ it "retrieves accounts_summary_document" do
35
+ expect(@bank.accounts_summary_document).to be_kind_of Nokogiri::HTML::Document
36
+ end
37
+
38
+ it "has account_tables" do
39
+ expect(@bank.account_tables.size).to eq 2
40
+ end
41
+
42
+ it "has bank_accounts_table" do
43
+ expect(@bank.bank_accounts_table).to be_a Nokogiri::XML::Element
44
+ end
45
+
46
+ it "has loan_accounts_table" do
47
+ expect(@bank.loan_accounts_table).to be_a Nokogiri::XML::Element
48
+ end
49
+
50
+ it "has 2 bank accounts" do
51
+ expect(@bank.account_rows(@bank.bank_accounts_table, :bank).size).to eq 2
52
+ end
53
+
54
+ it "has 1 loan account" do
55
+ expect(@bank.account_rows(@bank.loan_accounts_table, :loan).size).to eq 1
56
+ end
57
+
58
+ it "gets expected accounts" do
59
+ expect(@bank.accounts.size).to eq accounts
60
+ end
61
+
62
+ it "extracts account names" do
63
+ expect(@bank.accounts.map(&:name).select{|s| s =~ /Primary/}).to_not be_empty
64
+ end
65
+
66
+ it "extracts account numbers" do
67
+ expect(@bank.accounts.map(&:number).first).to eq "1"
68
+ end
69
+
70
+ it "expects balances to be greater than zero" do
71
+ expect(@bank.accounts.map(&:balance).any?(&:zero?)).to eq false
72
+ end
73
+
74
+ it "expects availables to be greater than zero for bank accts" do
75
+ expect(@bank.accounts.select{|s| s.bank_account?}.map(&:available).any?(&:zero?)).to eq false
76
+ end
77
+
78
+ it "expects availables to be zero for loan accts" do
79
+ expect(@bank.accounts.select{|s| s.loan_account?}.map(&:available).all?(&:zero?)).to eq true
80
+ end
81
+
82
+ it "sets account types" do
83
+ expect(@bank.accounts.map(&:account_type).uniq).to eq account_types
84
+ end
85
+ end
86
+
87
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dinero
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Lang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-30 00:00:00.000000000 Z
11
+ date: 2016-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -173,11 +173,13 @@ files:
173
173
  - lib/dinero/banks.rb
174
174
  - lib/dinero/banks/capital_one.rb
175
175
  - lib/dinero/banks/capital_one_360.rb
176
+ - lib/dinero/banks/sdccu.rb
176
177
  - lib/dinero/banks/south_state_bank.rb
177
178
  - lib/dinero/version.rb
178
179
  - log/.keep
179
180
  - spec/banks/capital_one_360_spec.rb
180
181
  - spec/banks/capital_one_spec.rb
182
+ - spec/banks/sdccu_spec.rb
181
183
  - spec/banks/south_state_bank_spec.rb
182
184
  - spec/fixtures/vcr_cassettes/.keep
183
185
  - spec/spec_helper.rb
@@ -210,6 +212,7 @@ summary: Dinero automates the process of logging into banking and financial webs
210
212
  test_files:
211
213
  - spec/banks/capital_one_360_spec.rb
212
214
  - spec/banks/capital_one_spec.rb
215
+ - spec/banks/sdccu_spec.rb
213
216
  - spec/banks/south_state_bank_spec.rb
214
217
  - spec/fixtures/vcr_cassettes/.keep
215
218
  - spec/spec_helper.rb