Skip to content

Commit 5f10371

Browse files
authored
Begin system spec harness (#633)
* Begin system spec harness * Wire up frontend to backend for system specs * Linting fixes * Pass port env vars through to frontend server in dev This supports running system specs on different frontend and backend instances than the default ones. * Drop --proxy option to `ember serve` Appears to not be used or honored. The values in config/environment.js seem to take effect instead. * Exclude system specs by default * Slight tweak to config
1 parent cd60fa8 commit 5f10371

13 files changed

+214
-3
lines changed

Rakefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ desc "run application"
44
task :run do
55
pids = [
66
spawn("cd backend && bundle install && foreman start -f Procfile.local"),
7-
spawn("cd frontend && rm -rfd ./dist && ./node_modules/.bin/ember serve --port 4300 --proxy http://localhost:3000")
7+
spawn("cd frontend && rm -rfd ./dist && ./node_modules/.bin/ember serve --port 4300")
88
]
99

1010
trap "INT" do

backend/.rspec

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
--color
2+
--tag ~type:system

backend/Gemfile

+2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ group :development do
8989
end
9090

9191
group :test do
92+
gem "capybara"
93+
gem "cuprite"
9294
gem "mongoid-rspec"
9395
gem "shoulda-matchers"
9496
gem "vcr"

backend/Gemfile.lock

+23
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ GEM
9696
cancancan (3.3.0)
9797
cancancan-mongoid (2.0.0)
9898
cancancan (>= 2.0, < 4)
99+
capybara (3.39.2)
100+
addressable
101+
matrix
102+
mini_mime (>= 0.1.3)
103+
nokogiri (~> 1.8)
104+
rack (>= 1.6.0)
105+
rack-test (>= 0.6.3)
106+
regexp_parser (>= 1.5, < 3.0)
107+
xpath (~> 3.2)
99108
coderay (1.1.3)
100109
coercible (1.0.0)
101110
descendants_tracker (~> 0.0.1)
@@ -108,6 +117,9 @@ GEM
108117
crack (0.4.5)
109118
rexml
110119
crass (1.0.6)
120+
cuprite (0.14.3)
121+
capybara (~> 3.0)
122+
ferrum (~> 0.13.0)
111123
database_cleaner (2.0.2)
112124
database_cleaner-active_record (>= 2, < 3)
113125
database_cleaner-active_record (2.1.0)
@@ -168,6 +180,11 @@ GEM
168180
faraday-net_http_persistent (1.2.0)
169181
faraday-patron (1.0.0)
170182
faraday-rack (1.0.0)
183+
ferrum (0.13)
184+
addressable (~> 2.5)
185+
concurrent-ruby (~> 1.1)
186+
webrick (~> 1.7)
187+
websocket-driver (>= 0.6, < 0.8)
171188
ffaker (2.21.0)
172189
ffi (1.15.5-java)
173190
foreman (0.87.2)
@@ -210,6 +227,7 @@ GEM
210227
net-pop
211228
net-smtp
212229
marcel (1.0.2)
230+
matrix (0.4.2)
213231
method_source (1.0.0)
214232
mini_mime (1.1.5)
215233
mini_portile2 (2.8.5)
@@ -450,11 +468,14 @@ GEM
450468
addressable (>= 2.8.0)
451469
crack (>= 0.3.2)
452470
hashdiff (>= 0.4.0, < 2.0.0)
471+
webrick (1.8.1)
453472
websocket-driver (0.7.6)
454473
websocket-extensions (>= 0.1.0)
455474
websocket-driver (0.7.6-java)
456475
websocket-extensions (>= 0.1.0)
457476
websocket-extensions (0.1.5)
477+
xpath (3.2.0)
478+
nokogiri (~> 1.8)
458479
yard (0.9.34)
459480
zeitwerk (2.6.12)
460481

@@ -475,8 +496,10 @@ DEPENDENCIES
475496
byebug
476497
cancancan (~> 3.3.0)
477498
cancancan-mongoid (= 2.0.0)
499+
capybara
478500
colored (~> 1.2)
479501
countries
502+
cuprite
480503
database_cleaner
481504
database_cleaner-mongoid
482505
devise (= 4.8.0)

backend/bin/system_spec

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Eg:
2+
# bin/system_spec
3+
# bin/system_spec --format doc spec/system/signup_spec.rb
4+
bundle exec rspec --tag type:system "$@"

backend/env-example

+7
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,10 @@ FULLSTORY_ORG=
5555

5656
# MongoDB
5757
MONGODB_HOST=localhost
58+
59+
# System spec server ports
60+
SYSTEM_SPEC_BACKEND_PORT=3003
61+
SYSTEM_SPEC_FRONTEND_PORT=4303
62+
63+
# Run system specs with a headless chrome instance
64+
HEADLESS_CHROME=true

backend/lib/tasks/spec.rake

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace :spec do
2+
begin
3+
require "rspec/core/rake_task"
4+
5+
Rake::Task[:system].clear
6+
RSpec::Core::RakeTask.new(:system) do |t|
7+
t.rspec_opts = "--tag type:system"
8+
end
9+
rescue LoadError
10+
# no rspec available
11+
end
12+
13+
namespace :system do
14+
task :start_frontend do
15+
require_relative "../../spec/system/support/frontend_app"
16+
17+
frontend_app = SystemSpec::FrontendApp.instance
18+
frontend_app.start $stdout
19+
20+
trap "INT" do
21+
frontend_app.stop
22+
exit 1
23+
end
24+
25+
frontend_app.wait_on
26+
end
27+
end
28+
end

backend/spec/system/signup_spec.rb

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
require "system_spec_helper"
2+
3+
RSpec.describe "signup flow" do
4+
it "loads the root page" do
5+
visit "/"
6+
expect(page.title).to eq "Flaredown"
7+
end
8+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Most of the config here, and in cuprite_config, was borrowed, with many thanks, from:
2+
# https://evilmartians.com/chronicles/system-of-a-test-setting-up-end-to-end-rails-testing
3+
Capybara.configure do |config|
4+
# Usually, especially when using Selenium, developers tend to increase the max wait time.
5+
# With Cuprite, there is no need for that.
6+
# We use a Capybara default value here explicitly.
7+
config.default_max_wait_time = 2
8+
9+
# Normalize whitespaces when using `has_text?` and similar matchers,
10+
# i.e., ignore newlines, trailing spaces, etc.
11+
# That makes tests less dependent on slightly UI changes.
12+
config.default_normalize_ws = true
13+
14+
# Where to store system tests artifacts (e.g. screenshots, downloaded files, etc.).
15+
# It could be useful to be able to configure this path from the outside (e.g., on CI).
16+
config.save_path = ENV.fetch("CAPYBARA_ARTIFACTS", "./tmp/capybara")
17+
18+
# Port on which to fire up the rails backend.
19+
config.server_port = ENV["SYSTEM_SPEC_BACKEND_PORT"]
20+
21+
# Where to find frontend app; see ./frontend_app.rb
22+
config.app_host = "http://localhost:#{ENV["SYSTEM_SPEC_FRONTEND_PORT"]}"
23+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require "capybara/cuprite"
2+
3+
Capybara.register_driver(:cuprite) do |app|
4+
Capybara::Cuprite::Driver.new(
5+
app,
6+
window_size: [1200, 800],
7+
browser_options: {},
8+
process_timeout: 10,
9+
inspector: true,
10+
headless: ENV["HEADLESS_CHROME"] != "false"
11+
)
12+
end
13+
14+
Capybara.default_driver = Capybara.javascript_driver = :cuprite
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
module SystemSpec
2+
class FrontendApp
3+
include Singleton
4+
5+
def start(log = "/dev/null")
6+
if test_connection_to_frontend_app
7+
puts "Frontend app already running at #{frontend_app_url}"
8+
return
9+
end
10+
11+
puts "Starting frontend app at #{frontend_app_url}"
12+
@pid ||= Process.spawn(
13+
frontend_app_cmd,
14+
[:out, :err] => log,
15+
:chdir => "../frontend"
16+
)
17+
18+
timeout = 15.seconds
19+
unless test_connection_to_frontend_app(timeout)
20+
fail "Frontend app did not start within #{timeout} seconds"
21+
end
22+
end
23+
24+
def wait_on
25+
return unless @pid
26+
Process.wait @pid
27+
end
28+
29+
def stop
30+
return unless @pid
31+
Process.kill "QUIT", @pid
32+
Process.wait @pid
33+
end
34+
35+
private
36+
37+
def test_connection_to_frontend_app(timeout = 0)
38+
system(
39+
test_connection_cmd(timeout),
40+
out: "/dev/null"
41+
)
42+
end
43+
44+
def test_connection_cmd(timeout)
45+
[
46+
"curl --silent --head -X GET",
47+
"--retry-connrefused",
48+
"--retry", timeout.to_s,
49+
"--retry-delay", "1",
50+
frontend_app_url
51+
].join(" ")
52+
end
53+
54+
def frontend_app_cmd
55+
[
56+
"PORT=#{backend_port}",
57+
"BASE_URL=#{frontend_app_url}",
58+
"FRONTEND_PORT=#{frontend_port}",
59+
"./node_modules/.bin/ember", "serve",
60+
"--port", frontend_port
61+
].join(" ")
62+
end
63+
64+
def frontend_app_url
65+
"http://localhost:#{frontend_port}"
66+
end
67+
68+
def frontend_port
69+
ENV["SYSTEM_SPEC_FRONTEND_PORT"]
70+
end
71+
72+
def backend_port
73+
ENV["SYSTEM_SPEC_BACKEND_PORT"]
74+
end
75+
end
76+
end

backend/spec/system_spec_helper.rb

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require "rails_helper"
2+
3+
# Most of the system spec config was borrowed, with many thanks, from:
4+
# https://evilmartians.com/chronicles/system-of-a-test-setting-up-end-to-end-rails-testing
5+
require "system/support/frontend_app"
6+
require "system/support/capybara_config"
7+
require "system/support/cuprite_config"
8+
9+
RSpec.configure do |config|
10+
config.prepend_before(:each, type: :system) do
11+
driven_by Capybara.javascript_driver
12+
end
13+
14+
# System specs are excluded by default in ~/.rspec
15+
# bin/system_spec provides a thin shim that includes them
16+
unless config.exclusion_filter[:type] == "system"
17+
config.before(:suite) do
18+
SystemSpec::FrontendApp.instance.start
19+
end
20+
21+
config.after(:suite) do
22+
SystemSpec::FrontendApp.instance.stop
23+
end
24+
end
25+
end

frontend/config/environment.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ module.exports = function(environment) {
6363
// ENV.APP.LOG_TRANSITIONS = true;
6464
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
6565
// ENV.APP.LOG_VIEW_LOOKUPS = true;
66-
ENV.apiHost = 'http://localhost:3000';
67-
var STATIC_URL = 'http://localhost:4300';
66+
ENV.apiHost = 'http://localhost:' + (process.env.PORT || '3000');
67+
var STATIC_URL = 'http://localhost:' + (process.env.FRONTEND_PORT || '4300');
6868
}
6969

7070
if (environment === 'test') {

0 commit comments

Comments
 (0)