click_session 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +286 -0
  7. data/Rakefile +1 -0
  8. data/click_session.gemspec +36 -0
  9. data/lib/click_session/async.rb +45 -0
  10. data/lib/click_session/click_session_processor.rb +64 -0
  11. data/lib/click_session/configuration.rb +142 -0
  12. data/lib/click_session/exceptions.rb +12 -0
  13. data/lib/click_session/failure_status_reporter.rb +15 -0
  14. data/lib/click_session/notifier.rb +23 -0
  15. data/lib/click_session/response_serializer.rb +34 -0
  16. data/lib/click_session/s3_connection.rb +34 -0
  17. data/lib/click_session/s3_file_uploader.rb +24 -0
  18. data/lib/click_session/session_state.rb +64 -0
  19. data/lib/click_session/status_reporter.rb +81 -0
  20. data/lib/click_session/successful_status_reporter.rb +15 -0
  21. data/lib/click_session/sync.rb +76 -0
  22. data/lib/click_session/version.rb +3 -0
  23. data/lib/click_session/web_runner.rb +60 -0
  24. data/lib/click_session/web_runner_processor.rb +65 -0
  25. data/lib/click_session/webhook.rb +24 -0
  26. data/lib/click_session/webhook_model_serializer.rb +7 -0
  27. data/lib/click_session.rb +34 -0
  28. data/lib/generators/click_session/db/migration/create_session_states.rb +13 -0
  29. data/lib/generators/click_session/initializers/click_session.rb +4 -0
  30. data/lib/generators/click_session/install_generator.rb +54 -0
  31. data/lib/tasks/click_session.rake +52 -0
  32. data/spec/click_session/async_spec.rb +66 -0
  33. data/spec/click_session/click_session_processor_spec.rb +292 -0
  34. data/spec/click_session/configuration_spec.rb +168 -0
  35. data/spec/click_session/failure_status_reporter_spec.rb +87 -0
  36. data/spec/click_session/notifier_spec.rb +72 -0
  37. data/spec/click_session/response_serializer_spec.rb +50 -0
  38. data/spec/click_session/s3_file_uploader_spec.rb +24 -0
  39. data/spec/click_session/session_state_spec.rb +54 -0
  40. data/spec/click_session/status_reporter_spec.rb +199 -0
  41. data/spec/click_session/successful_status_reporter_spec.rb +85 -0
  42. data/spec/click_session/sync_spec.rb +259 -0
  43. data/spec/click_session/web_runner_processor_spec.rb +143 -0
  44. data/spec/click_session/web_runner_spec.rb +77 -0
  45. data/spec/click_session/webhook_spec.rb +75 -0
  46. data/spec/factories/test_unit_model_factory.rb +5 -0
  47. data/spec/spec_helper.rb +42 -0
  48. data/spec/support/click_session_runner.rb +5 -0
  49. data/spec/support/dummy_web_runner.rb +2 -0
  50. data/spec/support/schema.rb +16 -0
  51. data/spec/support/test_unit_model.rb +3 -0
  52. metadata +310 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8d180275fd38f126e826853293950674857701ee
4
+ data.tar.gz: 86ed88478e1de5c0b2fd245819617c82f56e5d4b
5
+ SHA512:
6
+ metadata.gz: 481b241d4dab8796a7a9c8fcd78e535f184c396859f135369a6ac722dd768f03f3be35c2884dc7918c224ee5aec25695328756b04baf15f747707641da934856
7
+ data.tar.gz: 1abddccdb8cbbc18b1c87fb7444c836633c28835b6490d432368df4632197c61f135ee47bbd6c6f4bb64fa3e4688bb30e166a17b88d103ee8cc1a001a97d5989
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in click_session.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Tobias Talltorp
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,286 @@
1
+ # click_session
2
+ Turn any repeatable web navigation process into an api
3
+
4
+ ## Why?
5
+ Modern web apps rely more and more on html to be loaded asyncronously after the page has been loaded. The current solutions for automating a series of clicks, form posts and navigation changes relies on all html being rendered at once.
6
+
7
+ The Capybara team has put a lot of thought into how these web apps can be tested and because of this, it also makes a good tool for scraping these web sites.
8
+
9
+ ## Installation
10
+
11
+ ## How to set up
12
+
13
+ ### Generate a migration
14
+ `rails generate click_session:install`
15
+ This will create a migration and generate an initializer with configuration parameters needed for click_session
16
+
17
+ ### Define the steps in a class
18
+ Name the class ```ClickSessionRunner``` and add a method called ```run```.
19
+
20
+ This class must extend the ```WebRunner``` class
21
+
22
+ The ```model``` is an ActiveRecord model which holds the data needed for the session.
23
+
24
+ ```ruby
25
+ class ClickSessionRunner < ClickSession::WebRunner
26
+
27
+ # Steps to simulate
28
+ def run(model)
29
+ visit "https://www.stackoverflow.com"
30
+ fill_in "q", with: "Capybara"
31
+ press_enter_to_submit
32
+
33
+ model.name = first_search_result.text
34
+
35
+ model.save
36
+ end
37
+
38
+ private
39
+
40
+ def press_enter_to_submit
41
+ find_field('q').native.send_key(:enter)
42
+ end
43
+
44
+ def first_search_result
45
+ page.first(".summary")
46
+ end
47
+ end
48
+ ```
49
+
50
+ ### Run session syncronously
51
+ __Note:__ The response time for this type of request is totally dependant of the time it takes to visit all the pages.
52
+
53
+ ```ruby
54
+ user = User.new
55
+ sync_click_session = ClickSession::Sync.new(user)
56
+ result = sync_click_session.run
57
+ # --> saves the User
58
+ # --> run the steps in the ClickSessionRunner
59
+ # --> result contains the serialized user data
60
+ ```
61
+
62
+ ### Run session asyncronously
63
+ ```ruby
64
+ user = User.new
65
+ async_click_session = ClickSession::Async.new(user)
66
+ result = async_click_session.run
67
+ # --> saves the User
68
+ # --> saves the SessionState
69
+ # --> result contains the ID of the saved SessionState
70
+
71
+ # $ rake click_session:process_active
72
+ # --> run the steps in the ClickSessionRunner
73
+
74
+ # $ rake click_session:report_successful
75
+ # --> the request sent contains the serialized user data
76
+ ```
77
+
78
+ ### result hash
79
+ Example:
80
+ ```
81
+ {
82
+ id: 1234,
83
+ status: {
84
+ success: true, # Boolean
85
+ },
86
+ data: { # This is the output of the Serialized model
87
+ name: "Joe",
88
+ facebook_avatar: "http://fb.com/i/joe.png"
89
+ }
90
+ }
91
+ ```
92
+ The only optional part of the result is the ```data```.
93
+
94
+ ### Example of how to use it in a rails controller action
95
+ ```ruby
96
+ def show
97
+ user = User.new
98
+ sync_click_session = ClickSession::Sync.new(user)
99
+
100
+ result = sync_click_session.run
101
+
102
+ if result.status.success
103
+ render json: result.as_json, status: 201
104
+ else
105
+ render json: result.as_json, status: :unprocessable_entity
106
+ end
107
+ end
108
+
109
+ ```
110
+
111
+ ## Mandatory configurations
112
+ ```ruby
113
+ ClickSession.configure do | config |
114
+ config.model_class = YourModel
115
+ end
116
+ ```
117
+
118
+ ## Optional configurations and extentions
119
+
120
+ ```ruby
121
+ ClickSession.configure do | config |
122
+ config.processor_class = MyCustomProcessor
123
+ config.notifier_class = MyCustomNotifier
124
+ config.serializer_class = MyCustomSerializer
125
+ config.success_callback_url = "https://my.domain.com/webhook_success"
126
+ config.failure_callback_url = "https://my.domain.com/webhook_failure"
127
+ config.enable_screenshot = false # true
128
+ config.screenshot = {
129
+ s3_bucket: ENV['S3_BUCKET'],
130
+ s3_key_id: ENV['S3_KEY_ID'],
131
+ s3_access_key: ENV['S3_ACCESS_KEY']
132
+ }
133
+ config.driver_client = :poltergeist # :selenium
134
+ end
135
+ ```
136
+
137
+ Option | Description
138
+ ------- | ------------
139
+ ```notifier_class``` | The name of the class with your [custom notifications](#define-how-you-want-to-be-notified)
140
+ ```serializer_class``` | The name of the class with your [custom serializer](#define-how-you-want-to-serialize-the-result)
141
+ ```success_callback_url``` | The url you want us to ```POST``` to with the successful result. Only needed when using ```AsyncClickSession```
142
+ ```failure_callback_url``` | The url you want us to ```POST``` to with the error message. Only needed when using ```AsyncClickSession```
143
+ ```enable_screenshot``` | Must be set to true if you want to save screenshots.
144
+ ```screenshot``` | A hash containing the configuration information needed to be able to save screenshots. ```s3_bucket```, ```s3_key_id``` and ```s3_access_key``` are all required.
145
+ ```driver_client``` | The driver you want to use to run the ClickSession. ```:poltergeist``` is the default, but ```:selenium``` is a good choice if you are developing in a local environment and want to see the browser appear.
146
+
147
+
148
+ ### Define how you want to serialize the result
149
+ The serializer class takes the ```model``` that you accociated with the click_session and lets you transform it to whatever structure you like.
150
+
151
+ If you don't specify this class, we do a simple ```.as_json``` of the model and return that as the serialized result.
152
+
153
+ This can be good when there might be things that you save on the model that are not needed in the result, such as generated tokens or simple placeholders of data.
154
+
155
+ ```ruby
156
+ class MyUserSerializer
157
+ def serialize(model)
158
+ api_user = {
159
+ name: model.name,
160
+ facebook_avatar: model.user_image
161
+ }
162
+
163
+ api_user.as_json
164
+ end
165
+ end
166
+ ```
167
+
168
+ ### Define how you want to be notified
169
+ We will notify you when the following things happen
170
+ * The ClickSession was successfully completed
171
+ * The ClickSession failed because the max number of retries to run the SessionRunner has been exceeded
172
+ * The status of the ClickSession was successfully reported back to the webhook
173
+ * The max number of retries to report the asyncronous result (success or failure) back to your web hook has been exceeded.
174
+ * Every time we rescue an error
175
+
176
+ If this class is not defined, the information is logged to ```stdout```
177
+
178
+ All of these notifications are executed after the model has been successfully persisted.
179
+
180
+ ```ruby
181
+ # Override any number of methods to
182
+ # customize the behaviour of the notifications
183
+
184
+ class MyCustomNotifier < ClickSession::Notifier
185
+ def session_successful(session)
186
+ # Post to slack channel
187
+ # Send an email to the boss
188
+ super # log to stdout
189
+ end
190
+
191
+ def session_failed(session)
192
+ # Post to "alerts" channel on slack
193
+ # Send email to developers
194
+ end
195
+
196
+ def session_reported(session)
197
+ # Post to slack
198
+ end
199
+
200
+ def session_failed_to_report(session)
201
+ # Send email to developers
202
+ # Alert operations!
203
+ end
204
+
205
+ def rescued_error(e)
206
+ # Send the error to airbrake
207
+ end
208
+ end
209
+ ```
210
+
211
+ All other types of possible errors must be handled by your own code.
212
+
213
+ ### Save screen shots to S3
214
+ If you have enabled screenshots in your configuration, we will take a screenshot after the run has been successful or failed.
215
+
216
+ __Note:__ This requires you to add the S3 credentials and bucket name to the configuration
217
+
218
+
219
+ ## Rake tasks
220
+ ### click_session:process_active
221
+ Processes all the active click sessions, meaning the ones that are ready to be run.
222
+
223
+ __Note:__ Only needed for ```ClickSession::Async```
224
+
225
+ ### click_session:report_succesful
226
+ Reports all click_sessions, which were successfully run, to the configured ```success_callback_url```
227
+
228
+ __Note:__ Only needed for ```ClickSession::Async```
229
+
230
+ ### click_session:report_failed
231
+ Reports all click_sessions, which failed to run, to the configured ```failure_callback_url```
232
+
233
+ __Note:__ Only needed for ```ClickSession::Async```
234
+
235
+ ### click_session:report_failed
236
+ Reports all click_sessions, which failed to run, to the configured ```failure_callback_url```
237
+
238
+ __Note:__ Only needed for ```ClickSession::Async```
239
+
240
+ ### click_session:validate (not yet implemented)
241
+ Runs the steps you defined that validates that the steps in the session has not changed.
242
+
243
+ ```ruby
244
+ class ClickSessionRunner < WebRunner
245
+ def run(search_result_model)
246
+ # ...
247
+ end
248
+
249
+ def validate(model)
250
+ visit "http://www.google.com"
251
+
252
+ unless search_field_accesible?
253
+ raise ValidateClickSessionError("There are no results!!")
254
+ end
255
+ end
256
+
257
+ private
258
+ def search_field_accesible?
259
+ page.find("input[name='query']") != nil
260
+ end
261
+ end
262
+ ```
263
+
264
+ ## Dependencies
265
+ This gem is dependant on you having a browser installed which can be run by the capybara driver.
266
+
267
+ We have tested it with
268
+ * poltergeist (PhantomJS)
269
+ * selenium (FireFox)
270
+
271
+ ## Deployment
272
+ If you like to deploy your code to heroku, you need to use the ```build-pack-multi```.
273
+
274
+ Create a file in the root of your application called ```.buildpacks```with this content
275
+ ```
276
+ https://github.com/stomita/heroku-buildpack-phantomjs
277
+ https://github.com/heroku/heroku-buildpack-ruby
278
+ ```
279
+
280
+ ## Contributing
281
+
282
+ 1. Fork it ( http://github.com/<my-github-username>/click_session/fork )
283
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
284
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
285
+ 4. Push to the branch (`git push origin my-new-feature`)
286
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'click_session/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "click_session"
8
+ spec.version = ClickSession::VERSION
9
+ spec.authors = ["Tobias Talltorp"]
10
+ spec.email = ["tobias@talltorp.se"]
11
+ spec.summary = "Navigates the web for you"
12
+ spec.description = "Navigates the web for you"
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'rails', '>= 4.1'
22
+ spec.add_dependency 'capybara', "~> 2.4"
23
+ spec.add_dependency 'poltergeist', "~> 1.6"
24
+ spec.add_dependency 'rest-client', "~> 1.7"
25
+ spec.add_dependency 'state_machine', "~> 1.2"
26
+ spec.add_dependency 'aws-sdk-v1', "~> 1.63"
27
+ spec.add_dependency 'selenium-webdriver', "~> 2.45"
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.5"
30
+ spec.add_development_dependency "rake"
31
+ spec.add_development_dependency "rspec-rails", "~> 3.1"
32
+ spec.add_development_dependency "factory_girl_rails", "~> 4.2"
33
+ spec.add_development_dependency 'shoulda-matchers'
34
+ spec.add_development_dependency 'webmock', '~> 1.18'
35
+ spec.add_development_dependency "sqlite3"
36
+ end
@@ -0,0 +1,45 @@
1
+ module ClickSession
2
+ class Async
3
+ attr_reader :model
4
+ attr_accessor :click_session
5
+
6
+ def initialize(model)
7
+ @model = model
8
+ end
9
+
10
+ def run
11
+ validate_async_configuration
12
+
13
+ @click_session = SessionState.create(model: model)
14
+ serialize_success_response
15
+ end
16
+
17
+ private
18
+
19
+ def validate_async_configuration
20
+ if success_callback_missing? || failure_callback_missing?
21
+ raise ConfigurationError.new("You need to configure the callback URLs in order to use the AsyncClickSession")
22
+ end
23
+ end
24
+
25
+ def success_callback_missing?
26
+ ClickSession.configuration.success_callback_url == nil
27
+ end
28
+
29
+ def failure_callback_missing?
30
+ ClickSession.configuration.failure_callback_url == nil
31
+ end
32
+
33
+ def serialize_success_response
34
+ serializer.serialize_success(click_session)
35
+ end
36
+
37
+ def serialize_failure_response
38
+ serializer.serialize_failure(click_session)
39
+ end
40
+
41
+ def serializer
42
+ @serializer ||= ClickSession::ResponseSerializer.new
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,64 @@
1
+ require_relative './exceptions'
2
+
3
+ module ClickSession
4
+ class ClickSessionProcessor
5
+ attr_accessor :click_session
6
+ attr_reader :web_runner_processor, :notifier, :screenshot_enabled, :screenshot_options
7
+
8
+ def initialize(click_session, web_runner_processor, notifier, options = {})
9
+ @click_session = click_session
10
+ @web_runner_processor = web_runner_processor
11
+ @notifier = notifier
12
+ @screenshot_enabled = options[:screenshot_enabled] || false
13
+ @screenshot_options = options[:screenshot_options] || nil
14
+ end
15
+
16
+ def process
17
+ validate_screenshot_configuration
18
+
19
+ begin
20
+ process_provided_steps_in_session
21
+ rescue TooManyRetriesError => e
22
+ take_care_of_failed_session
23
+ raise e
24
+ end
25
+
26
+ click_session
27
+ end
28
+
29
+ private
30
+
31
+ def validate_screenshot_configuration
32
+ if screenshot_enabled
33
+ if screenshot_options == nil
34
+ raise ConfigurationError.new(<<-ERROR.strip_heredoc)
35
+ In order to save screenshots, you need to enter s3 information
36
+ in the 'screenshot' option of the configuration
37
+ See https://github.com/talltorp/click_session for more information.
38
+ ERROR
39
+ end
40
+ end
41
+ end
42
+
43
+ def process_provided_steps_in_session
44
+ web_runner_processor.process(click_session.model)
45
+ click_session.success!
46
+ notifier.session_successful(click_session)
47
+
48
+ if screenshot_enabled
49
+ click_session.screenshot_url = web_runner_processor.save_screenshot(click_session.id)
50
+ click_session.save
51
+ end
52
+ end
53
+
54
+ def take_care_of_failed_session
55
+ click_session.failure!
56
+ notifier.session_failed(click_session)
57
+
58
+ if screenshot_enabled
59
+ click_session.screenshot_url = web_runner_processor.save_screenshot(click_session.id)
60
+ click_session.save
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,142 @@
1
+ module ClickSession
2
+ @@configuration = nil
3
+
4
+ def self.configure
5
+ @@configuration = Configuration.new
6
+
7
+ if block_given?
8
+ yield configuration
9
+ end
10
+
11
+ configuration
12
+ end
13
+
14
+ def self.configuration
15
+ @@configuration || configure
16
+ end
17
+
18
+ class Configuration
19
+ attr_accessor :processor_class,
20
+ :success_callback_url,
21
+ :failure_callback_url,
22
+ :serializer_class
23
+
24
+ def model_class
25
+ if @model_class == nil
26
+ raise NameError.new(<<-ERROR.strip_heredoc, 'model_class')
27
+ To use ClickSession, you must define the name of the active model
28
+ you want ClickSession to operate on.
29
+ See https://github.com/talltorp/click_session for more information.
30
+ ERROR
31
+ end
32
+
33
+ @model_class.constantize
34
+ end
35
+
36
+ def model_class=(klass)
37
+ @model_class = klass.to_s
38
+ end
39
+
40
+ def processor_class
41
+ @processor_class ||=
42
+ begin
43
+ if Kernel.const_defined?(:ClickSessionRunner)
44
+ "ClickSessionRunner"
45
+ else
46
+ raise NameError.new(<<-ERROR.strip_heredoc, 'ClickSessionRunner')
47
+ To use ClickSession, you must either define `ClickSessionRunner` or configure a
48
+ different processor. See https://github.com/talltorp/click_session for
49
+ more information.
50
+ ERROR
51
+ end
52
+ end
53
+
54
+ @processor_class.constantize
55
+ end
56
+
57
+ def processor_class=(klass)
58
+ @processor_class = klass.to_s
59
+ end
60
+
61
+ def serializer_class
62
+ @serializer_class ||= "ClickSession::WebhookModelSerializer"
63
+ @serializer_class.constantize
64
+ end
65
+
66
+ def serializer_class=(klass)
67
+ @serializer_class = klass.to_s
68
+ end
69
+
70
+ def notifier_class
71
+ @notifier_class ||= "ClickSession::Notifier"
72
+ constantized_notifier = @notifier_class.constantize
73
+
74
+ if notifier_class_violates_interface(constantized_notifier)
75
+ raise ArgumentError.new(<<-ERROR.strip_heredoc)
76
+ Your custom notifier must inherit ClickSession::Notifier
77
+ See https://github.com/talltorp/click_session
78
+ ERROR
79
+ end
80
+
81
+ constantized_notifier
82
+ end
83
+
84
+ def notifier_class=(klass)
85
+ @notifier_class = klass.to_s
86
+ end
87
+
88
+ def driver_client
89
+ @driver_client ||= :poltergeist
90
+ end
91
+
92
+ def driver_client=(client)
93
+ @driver_client = client
94
+ end
95
+
96
+ def screenshot_enabled?
97
+ @screenshot_enabled ||= false
98
+ end
99
+
100
+ def enable_screenshot=(enable)
101
+ @screenshot_enabled = enable
102
+ end
103
+
104
+ def screenshot
105
+ if @screenshot == nil
106
+ raise ArgumentError.new("In order to save screenshots, you need to configure \
107
+ the information.
108
+ https://github.com/talltorp/click_session#optional-configurations-and-extentions
109
+ ")
110
+ end
111
+
112
+ @screenshot
113
+ end
114
+
115
+ def screenshot=(screenshot)
116
+ if screenshot[:s3_bucket] == nil
117
+ raise ArgumentError.new("The s3_bucket is required")
118
+ end
119
+
120
+ if screenshot[:s3_key_id] == nil
121
+ raise ArgumentError.new("The s3_key_id is required")
122
+ end
123
+
124
+ if screenshot[:s3_access_key] == nil
125
+ raise ArgumentError.new("The s3_access_key is required")
126
+ end
127
+
128
+ @screenshot = screenshot
129
+ end
130
+
131
+ private
132
+
133
+ def notifier_class_violates_interface(notifier_class)
134
+ required_methods = ClickSession::Notifier.instance_methods(false)
135
+ actual_methods = notifier_class.instance_methods
136
+
137
+ intersection = required_methods - actual_methods
138
+
139
+ intersection.length != 0
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,12 @@
1
+ module ClickSession
2
+ # A general ClickSession exception
3
+ class Error < StandardError; end
4
+
5
+ # Raised when the threshold has been reached for how many retries we
6
+ # make of running the steps provided before we consider the entire
7
+ # session a failure
8
+ class TooManyRetriesError < Error; end
9
+
10
+ # Raised when the configuration is not properly initialized
11
+ class ConfigurationError < Error; end
12
+ end
@@ -0,0 +1,15 @@
1
+ module ClickSession
2
+ class FailureStatusReporter < StatusReporter
3
+ def initialize(
4
+ webhook = Webhook.new(ClickSession.configuration.failure_callback_url)
5
+ )
6
+ super(webhook)
7
+ end
8
+
9
+ def report(click_session)
10
+ raise ArgumentError unless click_session.failed_to_process?
11
+
12
+ super(click_session)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module ClickSession
2
+ class Notifier
3
+ def session_successful(click_session)
4
+ puts "SUCCESS: #{click_session.id} completed"
5
+ end
6
+
7
+ def session_failed(click_session)
8
+ $stderr.puts "FAILURE: #{click_session.id} failed"
9
+ end
10
+
11
+ def session_reported(click_session)
12
+ puts "REPORTED: #{click_session.id} successfully reported"
13
+ end
14
+
15
+ def session_failed_to_report(click_session)
16
+ $stderr.puts "REPORT_FAIL: #{click_session.id} failed to report"
17
+ end
18
+
19
+ def rescued_error(e)
20
+ puts "#{e.class.name}: #{e.message}"
21
+ end
22
+ end
23
+ end