banker 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +9 -0
- data/.rvmrc +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +7 -0
- data/Guardfile +10 -0
- data/LICENSE +23 -0
- data/README.md +79 -0
- data/Rakefile +2 -0
- data/banker.gemspec +25 -0
- data/lib/banker.rb +14 -0
- data/lib/banker/account.rb +16 -0
- data/lib/banker/barclaycard_uk.rb +96 -0
- data/lib/banker/barclays.rb +89 -0
- data/lib/banker/base.rb +28 -0
- data/lib/banker/capital_one_uk.rb +88 -0
- data/lib/banker/credit_expert_uk.rb +112 -0
- data/lib/banker/error.rb +5 -0
- data/lib/banker/lloyds_tsb_uk.rb +130 -0
- data/lib/banker/version.rb +3 -0
- data/spec/banker/account_spec.rb +32 -0
- data/spec/banker/barclaycard_uk_spec.rb +167 -0
- data/spec/banker/barclays_spec.rb +225 -0
- data/spec/banker/base_spec.rb +35 -0
- data/spec/banker/capital_one_uk_spec.rb +134 -0
- data/spec/banker/credit_expert_uk_spec.rb +78 -0
- data/spec/banker/lloyds_tsb_uk_spec.rb +53 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/barclaycard_uk/data.ofx +180 -0
- data/spec/support/barclays/data.ofx +86 -0
- data/spec/support/credit_expert_uk/MCC.aspx.html +1914 -0
- data/spec/support/credit_expert_uk/MCCLogin.aspx.html +458 -0
- data/spec/support/credit_expert_uk/MCCLoginMemorableWord.aspx.html +360 -0
- data/spec/support/lloyds_tsb_uk/account_overview_personal.html +328 -0
- data/spec/support/lloyds_tsb_uk/entermemorableinformation.jsp.html +148 -0
- data/spec/support/lloyds_tsb_uk/interstitialpage.jsp.html +155 -0
- data/spec/support/lloyds_tsb_uk/login.jsp.html +281 -0
- metadata +167 -0
@@ -0,0 +1,225 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Banker::Barclays do
|
4
|
+
|
5
|
+
LOGIN_URL = "https://bank.barclays.co.uk/olb/auth/LoginLink.action"
|
6
|
+
EXPORT_URL = "https://bank.barclays.co.uk/olb/balances/ExportDataStep1.action"
|
7
|
+
|
8
|
+
let(:support_files) {File.expand_path('../../support/barclays/',__FILE__)}
|
9
|
+
let(:mechanize) {mock('mechanize') }
|
10
|
+
let(:form) {mock('form')}
|
11
|
+
let(:button) {mock('button')}
|
12
|
+
let(:check) {mock('check')}
|
13
|
+
let(:ofx) {
|
14
|
+
f = File.open(File.expand_path('data.ofx', support_files), 'r:iso-8859-1')
|
15
|
+
OFX(f.read)
|
16
|
+
}
|
17
|
+
let(:args) { { surname: 'Doe',
|
18
|
+
membership_number: '2010827349273',
|
19
|
+
passcode: '82736',
|
20
|
+
memorable_word: 'testing' } }
|
21
|
+
|
22
|
+
subject { Banker::Barclays }
|
23
|
+
|
24
|
+
describe 'Respond To' do
|
25
|
+
before do
|
26
|
+
subject.any_instance.stub(:params)
|
27
|
+
subject.any_instance.stub(:authenticate!)
|
28
|
+
subject.any_instance.stub(:download!)
|
29
|
+
subject.any_instance.stub(:delivery)
|
30
|
+
end
|
31
|
+
|
32
|
+
it { subject.new(args).should respond_to(:accounts) }
|
33
|
+
it { subject.new(args).should respond_to(:ofx) }
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'Parameters' do
|
37
|
+
before do
|
38
|
+
subject.any_instance.stub(:authenticate!)
|
39
|
+
subject.any_instance.stub(:download!)
|
40
|
+
subject.any_instance.stub(:delivery)
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'Valid Params' do
|
44
|
+
it { expect{ subject.new(args) }.to_not raise_error }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'Invalid Params' do
|
48
|
+
keys = %w(surname membership_number passcode memorable_word)
|
49
|
+
|
50
|
+
keys.each do |key|
|
51
|
+
it "should raise InvalidParams when #{key} is not passed" do
|
52
|
+
msg = "missing parameters `#{key}` "
|
53
|
+
expect{ subject.new(args.delete_if { |k,v| k if k == key.to_sym })}.
|
54
|
+
to raise_error(Banker::Error::InvalidParams, msg)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'Method Calls' do
|
61
|
+
before do
|
62
|
+
subject.any_instance.stub(:params)
|
63
|
+
subject.any_instance.stub(:authenticate!)
|
64
|
+
subject.any_instance.stub(:download!)
|
65
|
+
subject.any_instance.stub(:delivery)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should Call params Method" do
|
69
|
+
subject.any_instance.should_receive(:params)
|
70
|
+
Banker::Barclays.new({})
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should Call authenticate! Method" do
|
74
|
+
subject.any_instance.should_receive(:authenticate!)
|
75
|
+
Banker::Barclays.new({})
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should Call download_account_data! Method" do
|
79
|
+
subject.any_instance.should_receive(:download!)
|
80
|
+
Banker::Barclays.new({})
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#authenticate!" do
|
85
|
+
before do
|
86
|
+
Mechanize.stub(:new).and_return(mechanize.as_null_object)
|
87
|
+
Mechanize.any_instance.stub(get: mechanize)
|
88
|
+
mechanize.stub(form_with: form.as_null_object)
|
89
|
+
subject.any_instance.stub(:OFX).and_return(ofx)
|
90
|
+
subject.any_instance.stub(:get_letter).and_return('s')
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should call to LOGIN_URL' do
|
94
|
+
mechanize.should_receive(:get).with(LOGIN_URL).
|
95
|
+
and_return(mechanize.as_null_object)
|
96
|
+
|
97
|
+
subject.new(args)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should find the form element' do
|
101
|
+
mechanize.should_receive(:form_with).with( action: 'LoginLink.action' ).
|
102
|
+
and_return(form.as_null_object)
|
103
|
+
|
104
|
+
subject.new(args)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should fill in the form" do
|
108
|
+
form.should_receive(:[]=).with('surname', 'Doe')
|
109
|
+
form.should_receive(:[]=).with('membershipNumber', '2010827349273')
|
110
|
+
|
111
|
+
subject.new(args)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should submit" do
|
115
|
+
Mechanize.stub(:new).and_return(mechanize.as_null_object)
|
116
|
+
|
117
|
+
form.should_receive(:buttons).exactly(5).times.and_return([button])
|
118
|
+
mechanize.should_receive(:submit).exactly(5).times.with(form, button).
|
119
|
+
and_return(mechanize.as_null_object)
|
120
|
+
|
121
|
+
subject.new(args)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should find the form element' do
|
125
|
+
mechanize.should_receive(:form_with).with( action: 'LoginStep1i.action' ).
|
126
|
+
and_return(form.as_null_object)
|
127
|
+
|
128
|
+
subject.new(args)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should check passcode radio button" do
|
132
|
+
form.should_receive(:radiobuttons).and_return(check)
|
133
|
+
check.should_receive(:first).and_return(check)
|
134
|
+
check.should_receive(:check).and_return(check)
|
135
|
+
|
136
|
+
subject.new(args)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should submit" do
|
140
|
+
Mechanize.stub(:new).and_return(mechanize.as_null_object)
|
141
|
+
|
142
|
+
form.should_receive(:buttons).exactly(5).times.and_return([button])
|
143
|
+
mechanize.should_receive(:submit).exactly(5).times.with(form, button).
|
144
|
+
and_return(mechanize.as_null_object)
|
145
|
+
|
146
|
+
subject.new(args)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should find the form element' do
|
150
|
+
mechanize.should_receive(:form_with).with( action: 'LoginStep2.action' ).
|
151
|
+
and_return(form.as_null_object)
|
152
|
+
|
153
|
+
subject.new(args)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should fill in the form" do
|
157
|
+
form.should_receive(:[]=).with('passcode', '82736')
|
158
|
+
form.should_receive(:[]=).with("firstMemorableCharacter", "s")
|
159
|
+
form.should_receive(:[]=).with("secondMemorableCharacter", "s")
|
160
|
+
|
161
|
+
subject.new(args)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#download!' do
|
166
|
+
before do
|
167
|
+
subject.any_instance.stub(:authenticate!)
|
168
|
+
subject.any_instance.stub(:delivery)
|
169
|
+
subject.any_instance.stub(:OFX).and_return(ofx)
|
170
|
+
Mechanize.stub(:new).and_return(mechanize.as_null_object)
|
171
|
+
Mechanize.any_instance.stub(get: mechanize)
|
172
|
+
mechanize.stub(form_with: form.as_null_object)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should call to EXPORT_URL' do
|
176
|
+
mechanize.should_receive(:get).with(EXPORT_URL).
|
177
|
+
and_return(mechanize.as_null_object)
|
178
|
+
|
179
|
+
subject.new(args)
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'should find the form element' do
|
183
|
+
form_url = "/olb/balances/ExportDataStep1.action"
|
184
|
+
mechanize.should_receive(:form_with).with( action: form_url ).
|
185
|
+
and_return(form.as_null_object)
|
186
|
+
|
187
|
+
subject.new(args)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should fill in the form" do
|
191
|
+
form.should_receive(:[]=).with('reqSoftwarePkgCode', '6')
|
192
|
+
form.should_receive(:[]=).with('productIdentifier', 'All')
|
193
|
+
|
194
|
+
subject.new(args)
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should find the other form element' do
|
198
|
+
form_url = "/olb/balances/ExportDataStep2All.action"
|
199
|
+
mechanize.should_receive(:form_with).with( action: form_url ).
|
200
|
+
and_return(form.as_null_object)
|
201
|
+
|
202
|
+
subject.new(args)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'should parse ofx body' do
|
206
|
+
subject.any_instance.should_receive(:OFX).and_return(ofx)
|
207
|
+
|
208
|
+
subject.new(args)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe '#delivery()' do
|
213
|
+
let(:obj) { subject.new(args).accounts.first }
|
214
|
+
|
215
|
+
before do
|
216
|
+
subject.any_instance.stub(:authenticate!)
|
217
|
+
subject.any_instance.stub(:download!).and_return(ofx)
|
218
|
+
end
|
219
|
+
|
220
|
+
it { obj.uid.should == 'febcbc946e71cb09820c152c727b9b90' }
|
221
|
+
it { obj.name.should == 'Barclays' }
|
222
|
+
it { obj.amount.should == 410010 }
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Banker::Base do
|
4
|
+
describe "#params" do
|
5
|
+
it "raises Invalid Parameters if any defined parameters are missing" do
|
6
|
+
expect{
|
7
|
+
subject.keys = ['test']
|
8
|
+
subject.params({})
|
9
|
+
}.to raise_error(Banker::Error::InvalidParams,
|
10
|
+
"missing parameters `test` ")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#get" do
|
15
|
+
let(:mechanize) {mock('mechanize')}
|
16
|
+
let(:logger) {mock('logger')}
|
17
|
+
it "should assign agent with a new instance of Mechanize" do
|
18
|
+
Mechanize.should_receive(:new).and_return(mechanize)
|
19
|
+
Logger.should_receive(:new).and_return(logger)
|
20
|
+
|
21
|
+
mechanize.should_receive(:user_agent=).with("Mozilla/5.0 (Banker)")
|
22
|
+
mechanize.should_receive(:log=).with(logger)
|
23
|
+
mechanize.should_receive(:force_default_encoding=).with("utf8")
|
24
|
+
mechanize.should_receive(:get).with("http://google.com")
|
25
|
+
|
26
|
+
subject.get("http://google.com")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#get_letter" do
|
31
|
+
it "gets the index 1st character from a string" do
|
32
|
+
subject.get_letter("bank",1).should eql "b"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Banker::CapitalOneUK do
|
5
|
+
let(:mechanize) {mock('mechanize')}
|
6
|
+
let(:node) {mock('node')}
|
7
|
+
let(:form) {mock('form')}
|
8
|
+
let(:button) {mock('button')}
|
9
|
+
|
10
|
+
subject { Banker::CapitalOneUK}
|
11
|
+
|
12
|
+
before do
|
13
|
+
subject.any_instance.stub(:params)
|
14
|
+
subject.any_instance.stub(:authenticate!)
|
15
|
+
subject.any_instance.stub(:get_data)
|
16
|
+
end
|
17
|
+
|
18
|
+
it {subject.new().should respond_to(:accounts)}
|
19
|
+
|
20
|
+
context "Parameters" do
|
21
|
+
before do
|
22
|
+
subject.any_instance.unstub(:params)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises InvalidParams when email is missing" do
|
26
|
+
expect{
|
27
|
+
subject.new
|
28
|
+
}.to raise_error(Banker::Error::InvalidParams,
|
29
|
+
"missing parameters `username` `password` ")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "raises InvalidParams when password is missing" do
|
33
|
+
expect{
|
34
|
+
subject.new(email: 'test@test.com')
|
35
|
+
}.to raise_error(Banker::Error::InvalidParams)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'accepts email and password' do
|
39
|
+
expect{
|
40
|
+
subject.new(email: 'test@test.com', password: 'superduper')
|
41
|
+
}.to raise_error(Banker::Error::InvalidParams)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "calls" do
|
46
|
+
|
47
|
+
it "calls authenticate!" do
|
48
|
+
subject.any_instance.should_receive(:authenticate!)
|
49
|
+
subject.new
|
50
|
+
end
|
51
|
+
|
52
|
+
it "calls get_data" do
|
53
|
+
subject.any_instance.should_receive(:get_data)
|
54
|
+
subject.new
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "private" do
|
59
|
+
before do
|
60
|
+
Mechanize.stub(:new).and_return(mechanize.as_null_object)
|
61
|
+
mechanize.stub(form_with: form.as_null_object)
|
62
|
+
mechanize.stub(:at).and_return(node.as_null_object)
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#authenticate!" do
|
66
|
+
before do
|
67
|
+
subject.any_instance.unstub(:authenticate!)
|
68
|
+
subject.any_instance.stub(:get_letter)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "uses LOGIN_ENDPOINT" do
|
72
|
+
mechanize.should_receive(:get).
|
73
|
+
with("https://www.capitaloneonline.co.uk/CapitalOne_Consumer/Login.do").
|
74
|
+
and_return(mechanize)
|
75
|
+
subject.new
|
76
|
+
end
|
77
|
+
|
78
|
+
it "finds by form name" do
|
79
|
+
mechanize.should_receive(:form_with).with(
|
80
|
+
name: "logonForm"
|
81
|
+
).and_return(form)
|
82
|
+
subject.new
|
83
|
+
end
|
84
|
+
|
85
|
+
it "fills in form inputs" do
|
86
|
+
mechanize.should_receive(:at).with("#sign_in_box .password").
|
87
|
+
and_return(node)
|
88
|
+
node.should_receive(:content).
|
89
|
+
and_return("1st 2nd 3rd")
|
90
|
+
subject.any_instance.unstub(:get_letter)
|
91
|
+
|
92
|
+
form.should_receive(:[]=).with("username", "joebloggs")
|
93
|
+
form.should_receive(:[]=).with("password.randomCharacter0", "s")
|
94
|
+
form.should_receive(:[]=).with("password.randomCharacter1", "u")
|
95
|
+
form.should_receive(:[]=).with("password.randomCharacter2", "p")
|
96
|
+
subject.new(username: 'joebloggs', password: 'superduper')
|
97
|
+
end
|
98
|
+
|
99
|
+
it "submits form" do
|
100
|
+
form.should_receive(:buttons).and_return([button])
|
101
|
+
mechanize.should_receive(:submit).with(form, button)
|
102
|
+
subject.new
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#get_data" do
|
107
|
+
before do
|
108
|
+
subject.any_instance.stub(:get_letter)
|
109
|
+
|
110
|
+
subject.any_instance.unstub(:authenticate!)
|
111
|
+
subject.any_instance.unstub(:get_data)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "finds account balance" do
|
115
|
+
mechanize.should_receive(:at).
|
116
|
+
with("table[summary='account summary'] tr:nth-child(1) td.normalText:nth-child(2)").
|
117
|
+
and_return(node)
|
118
|
+
node.should_receive(:content).
|
119
|
+
at_least(:once).
|
120
|
+
and_return("£900.10")
|
121
|
+
subject.new
|
122
|
+
end
|
123
|
+
it "finds account limit" do
|
124
|
+
mechanize.should_receive(:at).
|
125
|
+
with("table[summary='account summary'] tr:nth-child(2) td.normalText:nth-child(2)").
|
126
|
+
and_return(node)
|
127
|
+
node.should_receive(:content).
|
128
|
+
at_least(:once).
|
129
|
+
and_return("£950.50")
|
130
|
+
subject.new
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Banker::CreditExpertUK do
|
4
|
+
let(:support_files) {File.expand_path('../../support/credit_expert_uk/',__FILE__)}
|
5
|
+
|
6
|
+
let(:mcclogin) {
|
7
|
+
File.read(
|
8
|
+
File.expand_path('MCCLogin.aspx.html', support_files)
|
9
|
+
)
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:mcclogin_memorable_word) {
|
13
|
+
File.read(
|
14
|
+
File.expand_path('MCCLoginMemorableWord.aspx.html', support_files)
|
15
|
+
)
|
16
|
+
}
|
17
|
+
|
18
|
+
let(:mcc) {
|
19
|
+
File.read(
|
20
|
+
File.expand_path('MCC.aspx.html', support_files)
|
21
|
+
)
|
22
|
+
}
|
23
|
+
|
24
|
+
|
25
|
+
subject {Banker::CreditExpertUK.new(:username => "Bloggs",
|
26
|
+
:password => "123456",
|
27
|
+
:memorable_word => "superduper")}
|
28
|
+
|
29
|
+
before do
|
30
|
+
stub_request(
|
31
|
+
:get,
|
32
|
+
'https://www.creditexpert.co.uk/MCCLogin.aspx'
|
33
|
+
).to_return(
|
34
|
+
:status => 200,
|
35
|
+
:body => mcclogin,
|
36
|
+
:headers => {
|
37
|
+
'Content-Type' => 'text/html'
|
38
|
+
}
|
39
|
+
)
|
40
|
+
|
41
|
+
stub_request(
|
42
|
+
:post,
|
43
|
+
'https://www.creditexpert.co.uk/MCCLogin.aspx'
|
44
|
+
).to_return(
|
45
|
+
:status => 200,
|
46
|
+
:body => mcclogin_memorable_word,
|
47
|
+
:headers => {
|
48
|
+
'Content-Type' => 'text/html'
|
49
|
+
}
|
50
|
+
)
|
51
|
+
|
52
|
+
stub_request(
|
53
|
+
:post,
|
54
|
+
%r{https://www.creditexpert.co.uk/MCCLoginMemorableWord.aspx}
|
55
|
+
).to_return(
|
56
|
+
:status => 200,
|
57
|
+
:body => mcc,
|
58
|
+
:headers => {
|
59
|
+
'Content-Type' => 'text/html'
|
60
|
+
}
|
61
|
+
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '.new' do
|
66
|
+
it{ subject.username.should eql 'Bloggs' }
|
67
|
+
it{ subject.password.should eql '123456' }
|
68
|
+
it{ subject.memorable_word.should eql 'superduper' }
|
69
|
+
it{ subject.score.should eql 648 }
|
70
|
+
|
71
|
+
it "should authenticate account" do
|
72
|
+
subject
|
73
|
+
|
74
|
+
WebMock.should have_requested(:get, 'https://www.creditexpert.co.uk/MCCLogin.aspx')
|
75
|
+
WebMock.should have_requested(:post, 'https://www.creditexpert.co.uk/MCCLogin.aspx')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|