foot_traffic 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f953b3b31b7d172bed8c19c4516f568969fca74c4677ad58f11f73200d11ef7b
4
+ data.tar.gz: bc6bd3f2bff7b92f7edc61aebf616c832cb49581f8197b50d9361552f277c4a5
5
+ SHA512:
6
+ metadata.gz: 5dc2c473dcbee1f5d4978f3b34d2bb087f3f5ed4cb7fe8e31fd9e98d5f976aa6429b3aa3c7337917bf3135df0e7cf4a9614288d6d8976d8e37a4ccead172d354
7
+ data.tar.gz: 5c039f20cf7d9ee37da0a70a958d4b78f7f154a7334f9455c96745017399d35ca4aba379e114625ab67a2fbd559750ae151a1b0d668281fd1d7c5140a5672497
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in foot_traffic.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
@@ -0,0 +1,48 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ foot_traffic (0.1.0)
5
+ ferrum (~> 0.8)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.7.0)
11
+ public_suffix (>= 2.0.2, < 5.0)
12
+ cliver (0.3.2)
13
+ concurrent-ruby (1.1.6)
14
+ diff-lcs (1.3)
15
+ ferrum (0.8)
16
+ addressable (~> 2.6)
17
+ cliver (~> 0.3)
18
+ concurrent-ruby (~> 1.1)
19
+ websocket-driver (>= 0.6, < 0.8)
20
+ public_suffix (4.0.5)
21
+ rake (12.3.3)
22
+ rspec (3.9.0)
23
+ rspec-core (~> 3.9.0)
24
+ rspec-expectations (~> 3.9.0)
25
+ rspec-mocks (~> 3.9.0)
26
+ rspec-core (3.9.2)
27
+ rspec-support (~> 3.9.3)
28
+ rspec-expectations (3.9.2)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.9.0)
31
+ rspec-mocks (3.9.1)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.9.0)
34
+ rspec-support (3.9.3)
35
+ websocket-driver (0.7.1)
36
+ websocket-extensions (>= 0.1.0)
37
+ websocket-extensions (0.1.4)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ foot_traffic!
44
+ rake (~> 12.0)
45
+ rspec (~> 3.0)
46
+
47
+ BUNDLED WITH
48
+ 2.1.4
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Andy B
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,243 @@
1
+ # Foot Traffic :dancers: :dancing_men:
2
+
3
+ A natural companion to to an amazing [Ferrum](https://github.com/rubycdp/ferrum) gem that controls a fleet of Chrome windows and tabs and simulates real user interaction with your web applications from any Ruby scripts. Works naturally with your system Chrome or Chromium, no extra magic like Selenium or WebDrivers needed.
4
+
5
+ ```rb
6
+ require "foot_traffic"
7
+ using FootTraffic
8
+
9
+ FootTraffic::Session.start do |window|
10
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
11
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
12
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com/paris" }
13
+ end
14
+ ```
15
+
16
+ ![FootTraffic in action](docs/in_action.gif)
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'foot_traffic'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle install
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install foot_traffic
33
+
34
+ ## Powered by Ferrum :heart:
35
+
36
+ At [Le Wagon](https://www.lewagon.com) we love Ferrum and use it as a Selenium replacement to run system tests for our learning platforms. We highly recommend you do the same, and here's why
37
+
38
+ > Ferrum connects to the browser by CDP protocol and there's no Selenium/WebDriver/ChromeDriver dependency. The emphasis was made on a raw CDP protocol because Chrome allows you to do so many things that are barely supported by WebDriver because it should have consistent design with other browsers.—[Ferrum on GitHub](https://github.com/rubycdp/ferrum)
39
+
40
+ Pure Ruby + pure Chrome—what's not to like?
41
+
42
+ ## Tutorial
43
+
44
+ In a simplest case, all you need is to `require "foot_traffic"`, put in the `using FootTraffic` to enable Ferrum [refinements](https://docs.ruby-lang.org/en/master/syntax/refinements_rdoc.html) and proceed with opening a session block. It yields a `window` object that is an instance of `Ferrum::Context` ([source](https://github.com/rubycdp/ferrum/blob/master/lib/ferrum/context.rb)). You can create a "tab" instance with a `new_tab` method and control it through Ferrum [methods](https://github.com/rubycdp/ferrum#examples) that are designed to be close to [Puppeteer](https://github.com/puppeteer/puppeteer/).
45
+
46
+ ```rb
47
+ require "foot_traffic"
48
+ using FootTraffic
49
+
50
+ FootTraffic::Session.start do |window|
51
+ window.new_tab.goto "https://www.lewagon.com"
52
+ window.new_tab.goto "https://www.lewagon.com/berlin"
53
+
54
+ paris = window.new_tab
55
+ paris.goto "https://www.lewagon.com/paris"
56
+ paris.at_css('[href="/paris/apply"]').click
57
+ paris.at_css("#apply_first_name").focus.type("Alan")
58
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
59
+ end
60
+ ```
61
+
62
+ In this case, all the instructions to the browser will run in a _single thread_ of execution, so you will see a browser performing actions consequetively.
63
+
64
+ As Ferrum is thread-safe by design, you can execute the same scenario in parallel. `tab_thread` method opens a block that yields the instance of `Ferrum::Page` ([source](https://github.com/rubycdp/ferrum/blob/master/lib/ferrum/page.rb)).
65
+
66
+ ```rb
67
+ require "foot_traffic"
68
+ using FootTraffic
69
+
70
+ FootTraffic::Session.start do |window|
71
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
72
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
73
+ window.tab_thread do |paris|
74
+ paris.goto "https://www.lewagon.com/paris"
75
+ paris.at_css('[href="/paris/apply"]').click
76
+ paris.at_css("#apply_first_name").focus.type("Alan")
77
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
78
+ end
79
+ end
80
+ ```
81
+
82
+ Now, all the tabs will run **in parallel** (with limits to Ruby concurrency model, of course). After the session block finishes execution, browser will stay open indefinitely—until you `Ctrl-C` the original script. That might be useful if you want to use Chrome Developer Tools on open pages.
83
+
84
+ If your script does not end with a session block and you want to continue running your code—set the duration time for the session.
85
+
86
+ ```rb
87
+ require "foot_traffic"
88
+ using FootTraffic
89
+
90
+ FootTraffic::Session.start(duration: 10) do |window|
91
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
92
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
93
+ window.tab_thread do |paris|
94
+ paris.goto "https://www.lewagon.com/paris"
95
+ paris.at_css('[href="/paris/apply"]').click
96
+ paris.at_css("#apply_first_name").focus.type("Alan")
97
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
98
+ end
99
+ end
100
+ ```
101
+
102
+ Now the block will exit after 10 seconds.
103
+
104
+ If you don't want to guess the time for pages to stay open—you can use the `quit` parameter of session. Note that in that case, you need to wait for all your threads to exit. As a convenience, `session` block yields a second argument that is a primitive implementation of a thread pool.
105
+
106
+ ```rb
107
+ require "foot_traffic"
108
+ using FootTraffic
109
+
110
+ FootTraffic::Session.start(quit: true) do |window, pool|
111
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
112
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
113
+ pool << window.tab_thread do |paris|
114
+ paris.goto "https://www.lewagon.com/paris"
115
+ paris.at_css('[href="/paris/apply"]').click
116
+ paris.at_css("#apply_first_name").focus.type("Alan")
117
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
118
+ end
119
+ pool.wait
120
+ end
121
+ ```
122
+
123
+ Now, the session block will exit as soon as the last action in the last tab completes—your script can run further!
124
+
125
+ If you want to see how your website handles multiple concurrent visits—you can use the `clone` parameter that will open as many Chrome windows as you want and run the tab scenario in each of them.
126
+
127
+ As this puts the strain on your system's resources, it makes sense to also use some of the Ferrum's [options](https://github.com/rubycdp/ferrum#customization) to set higher timeouts for Chrome startup and page loads. `slowmo` parameter might be particularly useful for simulating real user behavior, as it will add a small wait before executing each action, including sending keyboard keys.
128
+
129
+ ```rb
130
+ require "foot_traffic"
131
+ using FootTraffic
132
+
133
+ opts = {
134
+ process_timeout: 10,
135
+ timeout: 100,
136
+ slowmo: 0.1,
137
+ window_size: [1024, 768]
138
+ }
139
+
140
+ FootTraffic::Session.start(options: opts, quit: true, clones: 10) do |window, pool|
141
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
142
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
143
+ pool << window.tab_thread { |paris|
144
+ paris.goto "https://www.lewagon.com/paris"
145
+ paris.at_css('[href="/paris/apply"]').click
146
+ paris.at_css("#apply_first_name").focus.type("Alan")
147
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
148
+ }
149
+ pool.wait
150
+ end
151
+ ```
152
+
153
+ You can also set the `headless` option to `true` to perform script in headless mode. If you open too many concurrent tabs, or the number of clones too high—your system will run out of memory. To prevent that, Foot Traffic will raise the
154
+ `FootTraffic::ResourceOverloadError` once `ThreadError`, `RuntimeError`, `Errno::EMFILE`, or `Errno::ECONNRESET` start propagating.
155
+
156
+ ```rb
157
+ require "foot_traffic"
158
+ using FootTraffic
159
+
160
+ opts = {
161
+ headless: true,
162
+ process_timeout: 10,
163
+ timeout: 100,
164
+ slowmo: 0.1,
165
+ window_size: [1024, 768]
166
+ }
167
+
168
+ begin
169
+ FootTraffic::Session.start(options: opts, quit: true, clones: 10) do |window, pool|
170
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
171
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
172
+ pool << window.tab_thread { |paris|
173
+ paris.goto "https://www.lewagon.com/paris"
174
+ paris.at_css('[href="/paris/apply"]').click
175
+ paris.at_css("#apply_first_name").focus.type("Alan")
176
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
177
+ }
178
+ pool.wait
179
+ end
180
+ rescue FootTraffic::ResourceOverloadError
181
+ puts "Oops..."
182
+ exit(1)
183
+ end
184
+ ```
185
+
186
+ You can also control cookies for each tab. Keep in mind that in this case you don't want all your actions to run entirely concurrently, as the values of cookies may leak between tabs. Here's an example on how to avoid it:
187
+
188
+ ```rb
189
+ require "concurrent" # concurrent-ruby
190
+
191
+ tokens = [] # imaginary array of auth tokens
192
+
193
+ cookies = Concurrent::Hash.new
194
+
195
+ opts = {
196
+ headless: false, # Headless or not
197
+ timeout: 300, # How long to wait for new tab to open, set for high value
198
+ slowmo: 0.1, # How fast do you want bots to type
199
+ window_size: [1200, 800]
200
+ }
201
+
202
+ FootTraffic::Session.start(options: opts, quit: true) do |window, pool|
203
+ tokens.each do |token|
204
+ sleep(1) # Need to sleep so we can propely save cookies
205
+ pool << window.with_tab { |tab|
206
+ tab.goto("https://example.com/sign_in/#{token}")
207
+ cookies[token] = tab.cookies["_example_session"].value
208
+ }
209
+ end
210
+ pool.wait
211
+ end
212
+
213
+ FootTraffic::Session.start(options: opts) do |window|
214
+ tokens.each do |token|
215
+ sleep(1) # Wait to properly load cookies
216
+ window.with_tab do |tab|
217
+ tab.cookies.clear
218
+ tab.cookies.set(
219
+ name: "_example_session",
220
+ domain: "example.lewagon.co",
221
+ value: cookies[token]
222
+ )
223
+ tab.goto("https://example.com/protected_route")
224
+ end
225
+ end
226
+ end
227
+ ```
228
+
229
+ Check out the [examples](https://github.com/lewagon/foot_traffic/tree/master/examples) folder to study some of the scripts above.
230
+
231
+ ## Development
232
+
233
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
234
+
235
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
236
+
237
+ ## Contributing
238
+
239
+ Bug reports and pull requests are welcome on GitHub at https://github.com/lewagon/foot_traffic.
240
+
241
+ ## License
242
+
243
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "foot_traffic"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
Binary file
@@ -0,0 +1,27 @@
1
+ require "foot_traffic"
2
+ using FootTraffic
3
+
4
+ opts = {
5
+ headless: true,
6
+ process_timeout: 10,
7
+ timeout: 100,
8
+ slowmo: 0.1,
9
+ window_size: [1024, 768]
10
+ }
11
+
12
+ begin
13
+ FootTraffic::Session.start(options: opts, quit: true, clones: 100) do |window, pool|
14
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
15
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
16
+ pool << window.tab_thread { |paris|
17
+ paris.goto "https://www.lewagon.com/paris"
18
+ paris.at_css('[href="/paris/apply"]').click
19
+ paris.at_css("#apply_first_name").focus.type("Alan")
20
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
21
+ }
22
+ pool.wait
23
+ end
24
+ rescue FootTraffic::ResourceOverloadError
25
+ puts "Oops..."
26
+ exit(1)
27
+ end
@@ -0,0 +1,38 @@
1
+ require "concurrent" # concurrent-ruby
2
+
3
+ tokens = [] # imaginary array of auth tokens
4
+
5
+ cookies = Concurrent::Hash.new
6
+
7
+ opts = {
8
+ headless: false, # Headless or not
9
+ timeout: 300, # How long to wait for new tab to open, set for high value
10
+ slowmo: 0.1, # How fast do you want bots to type
11
+ window_size: [1200, 800]
12
+ }
13
+
14
+ FootTraffic::Session.start(options: opts, quit: true) do |window, pool|
15
+ tokens.each do |token|
16
+ sleep(1) # Need to sleep so we can propely save cookies
17
+ pool << window.with_tab { |tab|
18
+ tab.goto("https://example.com/sign_in/#{token}")
19
+ cookies[token] = tab.cookies["_example_session"].value
20
+ }
21
+ end
22
+ pool.wait
23
+ end
24
+
25
+ FootTraffic::Session.start(options: opts) do |window|
26
+ tokens.each do |token|
27
+ sleep(1) # Wait to properly load cookies
28
+ window.with_tab do |tab|
29
+ tab.cookies.clear
30
+ tab.cookies.set(
31
+ name: "_example_session",
32
+ domain: "example.lewagon.co",
33
+ value: cookies[token]
34
+ )
35
+ tab.goto("https://example.com/protected_route")
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ require "foot_traffic"
2
+ using FootTraffic
3
+
4
+ FootTraffic::Session.start do |window|
5
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
6
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
7
+ window.tab_thread do |paris|
8
+ paris.goto "https://www.lewagon.com/paris"
9
+ paris.at_css('[href="/paris/apply"]').click
10
+ paris.at_css("#apply_first_name").focus.type("Alan")
11
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ require "foot_traffic"
2
+ using FootTraffic
3
+
4
+ FootTraffic::Session.start(quit: true) do |window, pool|
5
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
6
+ pool << window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
7
+ pool << window.tab_thread { |paris|
8
+ paris.goto "https://www.lewagon.com/paris"
9
+ paris.at_css('[href="/paris/apply"]').click
10
+ paris.at_css("#apply_first_name").focus.type("Alan")
11
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
12
+ }
13
+ pool.wait
14
+ end
@@ -0,0 +1,8 @@
1
+ require "foot_traffic"
2
+ using FootTraffic
3
+
4
+ FootTraffic::Session.start do |window|
5
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com" }
6
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com/berlin" }
7
+ window.tab_thread { |tab| tab.goto "https://www.lewagon.com/paris" }
8
+ end
@@ -0,0 +1,13 @@
1
+ require "foot_traffic"
2
+ using FootTraffic
3
+
4
+ FootTraffic::Session.start do |window|
5
+ window.new_tab.goto "https://www.lewagon.com"
6
+ window.new_tab.goto "https://www.lewagon.com/berlin"
7
+
8
+ paris = window.new_tab
9
+ paris.goto "https://www.lewagon.com/paris"
10
+ paris.at_css('[href="/paris/apply"]').click
11
+ paris.at_css("#apply_first_name").focus.type("Alan")
12
+ paris.at_css("#apply_last_name").focus.type("Turing", :Tab)
13
+ end
@@ -0,0 +1,30 @@
1
+ require_relative "lib/foot_traffic/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "foot_traffic"
5
+ spec.version = FootTraffic::VERSION
6
+ spec.authors = ["Andy B"]
7
+ spec.email = ["andrey@lewagon.org"]
8
+
9
+ spec.summary = "Control a fleet of Chromes from a Ruby script. Built on Ferrum."
10
+ spec.description = "Foot Traffic allows to simulate real web users for load testing, debugging, or feature discovery"
11
+ spec.homepage = "https://github.com/lewagon/foot_traffic"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.com/lewagon/foot_traffic"
17
+ spec.metadata["changelog_uri"] = "https://github.com/lewagon/foot_traffic/CHANGELOG"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency "ferrum", "~> 0.8"
29
+ spec.add_development_dependency "rspec", "~> 3.2"
30
+ end
@@ -0,0 +1,6 @@
1
+ require "foot_traffic/version"
2
+ require "foot_traffic/session"
3
+
4
+ module FootTraffic
5
+ class ResourceOverloadError < StandardError; end
6
+ end
@@ -0,0 +1,20 @@
1
+ require "ferrum"
2
+
3
+ module FootTraffic
4
+ refine Ferrum::Context do
5
+ def new_tab
6
+ create_page
7
+ end
8
+
9
+ def in_thread(&block)
10
+ Thread.new do
11
+ block.call(create_page)
12
+ rescue ThreadError, RuntimeError, Errno::EMFILE, Errno::ECONNRESET
13
+ raise ResourceOverloadError
14
+ end
15
+ end
16
+
17
+ alias_method :with_tab, :in_thread
18
+ alias_method :tab_thread, :in_thread
19
+ end
20
+ end
@@ -0,0 +1,55 @@
1
+ require_relative "refinements"
2
+ require "ferrum"
3
+
4
+ module FootTraffic
5
+ class ThreadPool
6
+ def initialize
7
+ @threads = []
8
+ end
9
+
10
+ def <<(thread)
11
+ @threads << thread
12
+ end
13
+
14
+ def wait
15
+ @threads.map(&:join)
16
+ end
17
+ end
18
+
19
+ class Session
20
+ def self.start(options: {}, duration: nil, clones: 1, quit: false, &block)
21
+ new(options).start(duration: duration, clones: clones, quit: quit, &block)
22
+ end
23
+
24
+ def initialize(opts)
25
+ opts[:headless] ||= false
26
+ @browser ||= Ferrum::Browser.new(opts)
27
+ end
28
+
29
+ def start(duration: nil, clones: 1, quit: false, &block)
30
+ main = Thread.new {
31
+ threads = []
32
+ clones.times do
33
+ threads << Thread.new {
34
+ window = @browser.contexts.create
35
+ block.call(window, ThreadPool.new)
36
+ }
37
+ end
38
+ threads.map(&:join)
39
+ }
40
+
41
+ # A sleeping thread to keep Ferrum open for a given period of time
42
+ unless quit
43
+ wait = Thread.new {
44
+ duration.nil? ? sleep : sleep(duration)
45
+ }
46
+ wait.join
47
+ end
48
+
49
+ main.join
50
+ @browser.quit
51
+ rescue ThreadError, RuntimeError, Errno::EMFILE, Errno::ECONNRESET
52
+ raise ResourceOverloadError
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module FootTraffic
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: foot_traffic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andy B
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-05-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ferrum
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ description: Foot Traffic allows to simulate real web users for load testing, debugging,
42
+ or feature discovery
43
+ email:
44
+ - andrey@lewagon.org
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - ".rspec"
51
+ - Gemfile
52
+ - Gemfile.lock
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - bin/console
57
+ - bin/setup
58
+ - docs/in_action.gif
59
+ - examples/clones.rb
60
+ - examples/cookies.rb
61
+ - examples/multi_threaded.rb
62
+ - examples/multi_threaded_pool.rb
63
+ - examples/simple.rb
64
+ - examples/single_threaded.rb
65
+ - foot_traffic.gemspec
66
+ - lib/foot_traffic.rb
67
+ - lib/foot_traffic/refinements.rb
68
+ - lib/foot_traffic/session.rb
69
+ - lib/foot_traffic/version.rb
70
+ homepage: https://github.com/lewagon/foot_traffic
71
+ licenses:
72
+ - MIT
73
+ metadata:
74
+ homepage_uri: https://github.com/lewagon/foot_traffic
75
+ source_code_uri: https://github.com/lewagon/foot_traffic
76
+ changelog_uri: https://github.com/lewagon/foot_traffic/CHANGELOG
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: 2.3.0
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubygems_version: 3.1.2
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Control a fleet of Chromes from a Ruby script. Built on Ferrum.
96
+ test_files: []