voyage 1.44.0.9 → 1.44.0.10
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 +4 -4
- data/lib/voyage/app_builder.rb +59 -2
- data/lib/voyage/generators/app_generator.rb +10 -0
- data/lib/voyage/templates/Gemfile.erb +11 -3
- data/lib/voyage/templates/admin_controller.rb +13 -0
- data/lib/voyage/templates/admin_users_controller.rb +5 -8
- data/lib/voyage/templates/application_mailer.rb.erb +11 -0
- data/lib/voyage/templates/config_initializers_ams.rb +1 -0
- data/lib/voyage/templates/devise_registrations_controller.rb +1 -5
- data/lib/voyage/templates/rails_helper.rb.erb +3 -2
- data/lib/voyage/templates/rubocop.yml +14 -0
- data/lib/voyage/templates/simplecov.rb +6 -1
- data/lib/voyage/templates/specs/controllers/admin/users_controller_spec.rb +69 -0
- data/lib/voyage/templates/specs/controllers/application_controller_spec.rb +81 -0
- data/lib/voyage/templates/specs/features/user_impersonation_spec.rb +38 -0
- data/lib/voyage/templates/specs/features/user_list_spec.rb +21 -0
- data/lib/voyage/templates/specs/features/user_signup_spec.rb +18 -0
- data/lib/voyage/templates/specs/mailers/application_mailer_spec.rb.erb +26 -0
- data/lib/voyage/templates/specs/requests/user_api_spec.rb +44 -0
- data/lib/voyage/templates/specs/support/api/schemas/user.json +58 -0
- data/lib/voyage/templates/specs/support/features/session_helpers.rb +27 -0
- data/lib/voyage/templates/specs/support/matchers/api_schema_matcher.rb +7 -0
- data/lib/voyage/templates/specs/support/request_spec_helper.rb +22 -0
- data/lib/voyage/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2d85038fd4f02fe75a941bfbeb66bfb7c1ae2fc
|
4
|
+
data.tar.gz: 00f7aa3152f0e7ec2bc036420d0f96a87926690b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60ea2891a535d8abc0f216f5801f3cadbf37a97541b1fb5deaa2b9dae70d390d919e6fafe930b22145b9f6b6253881b72c4f85ee81aadf51920439187a413357
|
7
|
+
data.tar.gz: 3309b6cefdefdc3696329becf3c23960fdd590f0ffd046f3122ca806ddf91db4e91c65f6c5c34752f61ab44da5f4250ab264494ab474c06636f21db52bc1c3e8
|
data/lib/voyage/app_builder.rb
CHANGED
@@ -86,6 +86,11 @@ module Suspenders
|
|
86
86
|
def install_devise
|
87
87
|
if @@accept_defaults || agree?('Would you like to install Devise? (Y/n)')
|
88
88
|
@@use_devise = true
|
89
|
+
|
90
|
+
if @@accept_defaults || agree?('Would you like to install Devise token authentication? (Y/n)')
|
91
|
+
devise_token_auth = true
|
92
|
+
end
|
93
|
+
|
89
94
|
bundle_command 'exec rails generate devise:install'
|
90
95
|
|
91
96
|
if @@accept_defaults || agree?("Would you like to add first_name and last_name to the devise model? (Y/n)")
|
@@ -112,7 +117,7 @@ module Suspenders
|
|
112
117
|
end
|
113
118
|
|
114
119
|
customize_devise_views if adding_first_and_last_name
|
115
|
-
customize_application_controller_for_devise(adding_first_and_last_name)
|
120
|
+
customize_application_controller_for_devise(adding_first_and_last_name, devise_token_auth)
|
116
121
|
add_devise_registrations_controller
|
117
122
|
customize_resource_controller_for_devise(adding_first_and_last_name)
|
118
123
|
add_admin_views_for_devise_resource(adding_first_and_last_name)
|
@@ -124,6 +129,7 @@ module Suspenders
|
|
124
129
|
customize_user_factory(adding_first_and_last_name)
|
125
130
|
generate_seeder_templates(using_devise: true)
|
126
131
|
customize_user_spec
|
132
|
+
add_token_auth
|
127
133
|
else
|
128
134
|
generate_seeder_templates(using_devise: false)
|
129
135
|
end
|
@@ -149,7 +155,7 @@ module Suspenders
|
|
149
155
|
end
|
150
156
|
end
|
151
157
|
|
152
|
-
def customize_application_controller_for_devise(adding_first_and_last_name)
|
158
|
+
def customize_application_controller_for_devise(adding_first_and_last_name, devise_token_auth)
|
153
159
|
inject_into_file 'app/controllers/application_controller.rb', before: "class ApplicationController < ActionController::Base" do <<-RUBY.gsub(/^ {8}/, '')
|
154
160
|
# rubocop:disable Metrics/ClassLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/LineLength
|
155
161
|
RUBY
|
@@ -159,6 +165,7 @@ module Suspenders
|
|
159
165
|
|
160
166
|
check_authorization unless: :devise_or_pages_controller?
|
161
167
|
impersonates :user
|
168
|
+
#{'acts_as_token_authentication_handler_for User' if devise_token_auth}
|
162
169
|
|
163
170
|
before_action :configure_permitted_parameters, if: :devise_controller?
|
164
171
|
before_action :authenticate_user!, unless: -> { is_a?(HighVoltage::PagesController) }
|
@@ -316,6 +323,7 @@ module Suspenders
|
|
316
323
|
end
|
317
324
|
|
318
325
|
template '../templates/admin_users_controller.rb', 'app/controllers/admin/users_controller.rb'
|
326
|
+
template '../templates/admin_controller.rb', 'app/controllers/admin/admin_controller.rb'
|
319
327
|
end
|
320
328
|
|
321
329
|
def authorize_devise_resource_for_index_action
|
@@ -508,11 +516,45 @@ module Suspenders
|
|
508
516
|
expect(user.tester?).to eq(false)
|
509
517
|
end
|
510
518
|
end
|
519
|
+
|
520
|
+
context 'new user creation' do
|
521
|
+
it 'ensures uniqueness of the uuid' do
|
522
|
+
allow(User).to receive(:exists?).and_return(true, false)
|
523
|
+
|
524
|
+
expect do
|
525
|
+
create(:user)
|
526
|
+
end.to change { User.count }.by(1)
|
527
|
+
|
528
|
+
expect(User).to have_received(:exists?).exactly(2).times
|
529
|
+
end
|
530
|
+
end
|
511
531
|
RUBY
|
512
532
|
|
513
533
|
replace_in_file 'spec/models/user_spec.rb', find, replace
|
514
534
|
end
|
515
535
|
|
536
|
+
def add_token_auth
|
537
|
+
inject_into_file 'app/models/user.rb', after: "class User < ApplicationRecord" do <<-'RUBY'.gsub(/^ {6}/, '')
|
538
|
+
|
539
|
+
acts_as_token_authenticatable
|
540
|
+
RUBY
|
541
|
+
end
|
542
|
+
|
543
|
+
generate 'migration add_authentication_token_to_users "authentication_token:string{30}:uniq"'
|
544
|
+
|
545
|
+
# specs
|
546
|
+
template '../templates/specs/features/user_impersonation_spec.rb', 'spec/features/user_impersonation_spec.rb', force: true
|
547
|
+
template '../templates/specs/features/user_list_spec.rb', 'spec/features/user_list_spec.rb', force: true
|
548
|
+
template '../templates/specs/features/user_signup_spec.rb', 'spec/features/user_signup_spec.rb', force: true
|
549
|
+
template '../templates/specs/requests/user_api_spec.rb', 'spec/requests/user_api_spec.rb', force: true
|
550
|
+
template '../templates/specs/support/api/schemas/user.json', 'spec/support/api/schemas/user.json', force: true
|
551
|
+
template '../templates/config_initializers_ams.rb', 'config/initializers/ams.rb', force: true
|
552
|
+
template '../templates/specs/support/matchers/api_schema_matcher.rb', 'spec/support/matchers/api_schema_matcher.rb', force: true
|
553
|
+
template '../templates/specs/mailers/application_mailer_spec.rb.erb', 'spec/mailers/application_mailer_spec.rb', force: true
|
554
|
+
template '../templates/specs/support/features/session_helpers.rb', 'spec/support/features/session_helpers.rb', force: true
|
555
|
+
template '../templates/specs/support/request_spec_helper.rb', 'spec/support/request_spec_helper.rb', force: true
|
556
|
+
end
|
557
|
+
|
516
558
|
def customize_application_js
|
517
559
|
template '../templates/application.js', 'app/assets/javascripts/application.js', force: true
|
518
560
|
|
@@ -712,6 +754,20 @@ module Suspenders
|
|
712
754
|
template '../templates/favicon.ico', 'app/assets/images/favicon.ico', force: true
|
713
755
|
end
|
714
756
|
|
757
|
+
def customize_application_mailer
|
758
|
+
template '../templates/application_mailer.rb.erb', 'app/mailers/application_mailer.rb', force: true
|
759
|
+
end
|
760
|
+
|
761
|
+
def add_specs
|
762
|
+
inject_into_file 'app/jobs/application_job.rb', before: "class ApplicationJob < ActiveJob::Base" do <<-RUBY.gsub(/^ {8}/, '')
|
763
|
+
# :nocov:
|
764
|
+
RUBY
|
765
|
+
end
|
766
|
+
|
767
|
+
template '../templates/specs/controllers/admin/users_controller_spec.rb', 'spec/controllers/admin/users_controller_spec.rb', force: true
|
768
|
+
template '../templates/specs/controllers/application_controller_spec.rb', 'spec/controllers/application_controller_spec.rb', force: true
|
769
|
+
end
|
770
|
+
|
715
771
|
# Do this last
|
716
772
|
def rake_db_setup
|
717
773
|
rake 'db:migrate'
|
@@ -750,6 +806,7 @@ module Suspenders
|
|
750
806
|
.zenflow-log
|
751
807
|
errors.err
|
752
808
|
.ctags
|
809
|
+
.cadre/coverage.vim
|
753
810
|
RUBY
|
754
811
|
end
|
755
812
|
end
|
@@ -43,6 +43,8 @@ module Suspenders
|
|
43
43
|
invoke :add_auto_annotate_models_rake_task
|
44
44
|
invoke :update_delayed_job_migration_rails_5_1_specify_4_2
|
45
45
|
invoke :add_favicon
|
46
|
+
invoke :customize_application_mailer
|
47
|
+
invoke :add_specs
|
46
48
|
|
47
49
|
|
48
50
|
# Do these last
|
@@ -137,6 +139,14 @@ module Suspenders
|
|
137
139
|
build :add_favicon
|
138
140
|
end
|
139
141
|
|
142
|
+
def customize_application_mailer
|
143
|
+
build :customize_application_mailer
|
144
|
+
end
|
145
|
+
|
146
|
+
def add_specs
|
147
|
+
build :add_specs
|
148
|
+
end
|
149
|
+
|
140
150
|
def spin_up_webpacker
|
141
151
|
build :spin_up_webpacker
|
142
152
|
end
|
@@ -41,8 +41,10 @@ gem 'canard', git: 'https://github.com/jondkinney/canard.git', branch: 'feature/
|
|
41
41
|
gem 'cancancan' # authorization library
|
42
42
|
gem 'devise'
|
43
43
|
gem 'pretender' # impersonate users as an admin
|
44
|
+
gem 'simple_token_authentication', '~> 1.0' # adds token authentication to Devise
|
44
45
|
|
45
46
|
# Database Tweaks
|
47
|
+
gem 'active_model_serializers', '~> 0.10.0'
|
46
48
|
gem 'dynamic_form' # for custom messages without the database column
|
47
49
|
gem 'friendly_id' # slugs in the url auto-generated
|
48
50
|
gem 'nested_form_fields' # Dynamically add and remove nested has_many association fields in a Ruby on Rails form
|
@@ -80,7 +82,6 @@ group :development do
|
|
80
82
|
gem 'fix-db-schema-conflicts' # when working with multiple developers schema order of columns is standardized.
|
81
83
|
gem 'meta_request' # for chrome rails console plugin found here: https://chrome.google.com/webstore/detail/railspanel/gjpfobpafnhjhbajcjgccbbdofdckggg?hl=en-US
|
82
84
|
gem 'rails-erd' # auto gen ERD Diagram of models in the app on rake db:migrate
|
83
|
-
gem 'redcarpet' # used to render the readme inside a static welcome page from the high_voltage gem
|
84
85
|
gem 'zenflow', git: 'https://github.com/zencoder/zenflow.git'
|
85
86
|
end
|
86
87
|
|
@@ -89,16 +90,16 @@ group :development, :test do
|
|
89
90
|
gem "bullet"
|
90
91
|
gem "bundler-audit", ">= 0.5.0", require: false
|
91
92
|
gem "dotenv-rails"
|
92
|
-
gem "factory_girl_rails"
|
93
|
+
<%# gem "factory_girl_rails" %>
|
93
94
|
<%# gem "pry-byebug" %>
|
94
95
|
<%# gem "pry-rails" %>
|
95
96
|
gem "rspec-rails", "~> 3.5"
|
96
97
|
|
97
98
|
# Customizations
|
98
|
-
gem 'faker' # provides auto generated names for factories, can be customized
|
99
99
|
gem 'letter_opener' # auto-open emails when they're sent
|
100
100
|
gem 'rubocop'
|
101
101
|
gem 'rubocop-rspec', require: false
|
102
|
+
gem 'redcarpet' # used to render the readme inside a static welcome page from the high_voltage gem
|
102
103
|
end
|
103
104
|
|
104
105
|
group :development, :staging do
|
@@ -118,10 +119,17 @@ group :test do
|
|
118
119
|
# Customizations
|
119
120
|
gem 'cadre' # highlights code coverage in vim
|
120
121
|
gem 'capybara' # DSL for finding elements on a page during integration testing
|
122
|
+
gem 'json-schema' # Allows testing API responses against JSON schema
|
121
123
|
gem 'poltergeist' # Headless browser, used in integration tests
|
124
|
+
gem 'rspec_junit_formatter' # Creates JUnit style XML for use in CircleCI
|
122
125
|
gem 'selenium-webdriver' # `brew install chromedriver`, used for acceptance tests in an actual browser.
|
123
126
|
end
|
124
127
|
|
128
|
+
group :development, :test, :staging do
|
129
|
+
gem 'factory_girl_rails'
|
130
|
+
gem 'faker' # provides auto generated names for factories, can be customized
|
131
|
+
end
|
132
|
+
|
125
133
|
group :staging, :production do
|
126
134
|
gem "rack-timeout"
|
127
135
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Admin
|
2
|
+
class AdminController < ApplicationController
|
3
|
+
before_action :require_admin!
|
4
|
+
skip_authorization_check
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def require_admin!
|
9
|
+
txt = 'You must be an admin to perform that action'
|
10
|
+
redirect_to root_path, alert: txt unless current_user.admin?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
module Admin
|
2
|
-
class UsersController <
|
3
|
-
|
4
|
-
|
2
|
+
class UsersController < AdminController
|
3
|
+
skip_before_action :require_admin!, only: [:stop_impersonating]
|
4
|
+
respond_to :html, :json
|
5
5
|
|
6
6
|
def index
|
7
7
|
@users = User.all
|
8
|
+
|
9
|
+
respond_with(@users)
|
8
10
|
end
|
9
11
|
|
10
12
|
def impersonate
|
@@ -22,11 +24,6 @@ module Admin
|
|
22
24
|
|
23
25
|
private
|
24
26
|
|
25
|
-
def require_admin!
|
26
|
-
txt = 'You must be an admin to perform that action'
|
27
|
-
redirect_to root_path, notice: txt unless current_user.admin?
|
28
|
-
end
|
29
|
-
|
30
27
|
def track_impersonation(user, status)
|
31
28
|
analytics_track(
|
32
29
|
true_user,
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class ApplicationMailer < ActionMailer::Base
|
2
|
+
default from: "no-reply@<%= app_name %>.com",
|
3
|
+
return_path: "contact@<%= app_name %>.com"
|
4
|
+
|
5
|
+
layout 'mailer'
|
6
|
+
|
7
|
+
def email(to_address, subject, body)
|
8
|
+
options = { to: to_address, subject: subject, body: body }
|
9
|
+
mail options
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
ActiveModelSerializers.config.adapter = :json_api
|
@@ -37,8 +37,9 @@ RSpec.configure do |config|
|
|
37
37
|
|
38
38
|
config.use_transactional_fixtures = false
|
39
39
|
|
40
|
-
config.include Devise::
|
41
|
-
config.include
|
40
|
+
config.include Devise::Test::ControllerHelpers, type: :controller
|
41
|
+
config.include RequestSpecHelper, type: :request
|
42
|
+
config.include Features::SessionHelpers, type: :feature
|
42
43
|
# config.include Paperclip::Shoulda::Matchers
|
43
44
|
|
44
45
|
Capybara.javascript_driver = :poltergeist
|
@@ -15,6 +15,19 @@ RSpec/FilePath:
|
|
15
15
|
RSpec/LeadingSubject:
|
16
16
|
Enabled: false
|
17
17
|
|
18
|
+
Metrics/BlockLength:
|
19
|
+
Exclude:
|
20
|
+
- 'spec/**/*'
|
21
|
+
|
22
|
+
RSpec/NestedGroups:
|
23
|
+
Max: 6
|
24
|
+
|
25
|
+
RSpec/MultipleExpectations:
|
26
|
+
Max: 2
|
27
|
+
|
28
|
+
RSpec/ExampleLength:
|
29
|
+
Max: 15
|
30
|
+
|
18
31
|
Rails:
|
19
32
|
Enabled: true
|
20
33
|
|
@@ -39,6 +52,7 @@ AllCops:
|
|
39
52
|
- 'config/initializers/wrap_parameters.rb'
|
40
53
|
- 'bin/**/*'
|
41
54
|
- 'Rakefile'
|
55
|
+
- 'node_modules/**/*'
|
42
56
|
Include:
|
43
57
|
- 'Gemfile'
|
44
58
|
- '.simplecov'
|
@@ -1,4 +1,4 @@
|
|
1
|
-
if ENV['COVERAGE'].match?(/\Atrue\z/i)
|
1
|
+
if ENV['COVERAGE'] && ENV['COVERAGE'].match?(/\Atrue\z/i)
|
2
2
|
require 'cadre/simplecov'
|
3
3
|
|
4
4
|
SimpleCov.start do
|
@@ -9,6 +9,7 @@ if ENV['COVERAGE'].match?(/\Atrue\z/i)
|
|
9
9
|
add_group 'Helpers', 'app/helpers'
|
10
10
|
add_group 'Mailers', 'app/mailers'
|
11
11
|
add_group 'Models', 'app/models'
|
12
|
+
add_group 'Abilities', 'app/abilities'
|
12
13
|
add_group 'Serializers', 'app/serializers'
|
13
14
|
add_group 'Services', 'app/services'
|
14
15
|
add_group 'Workers', 'app/workers'
|
@@ -19,6 +20,10 @@ if ENV['COVERAGE'].match?(/\Atrue\z/i)
|
|
19
20
|
add_group 'Ignored Code' do |src_file|
|
20
21
|
File.readlines(src_file.filename).grep(/:nocov:/).any?
|
21
22
|
end
|
23
|
+
|
24
|
+
add_filter 'app/channels'
|
25
|
+
add_filter 'lib/tasks'
|
26
|
+
add_filter 'lib/seeder'
|
22
27
|
end
|
23
28
|
|
24
29
|
SimpleCov.formatters = [
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe Admin::UsersController, type: :request do
|
4
|
+
let(:admin_user) { create(:user, :admin) }
|
5
|
+
let(:user) { create(:user) }
|
6
|
+
|
7
|
+
describe '#index' do
|
8
|
+
context 'authenticated' do
|
9
|
+
context 'admin' do
|
10
|
+
it 'loads all users in the browser' do
|
11
|
+
sign_in(admin_user)
|
12
|
+
get admin_users_path
|
13
|
+
expect(response).to be_success
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'lists all users as json' do
|
17
|
+
token_header_params = {
|
18
|
+
'X-User-Email': admin_user.email,
|
19
|
+
'X-User-Token': admin_user.authentication_token,
|
20
|
+
}
|
21
|
+
|
22
|
+
get admin_users_url, headers: token_header_params, as: :json
|
23
|
+
|
24
|
+
expect(response.content_type).to eq('application/json')
|
25
|
+
expect(response).to have_http_status(:success)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'user' do
|
30
|
+
it 'redirects with an alert that you need to be an admin' do
|
31
|
+
sign_in(user)
|
32
|
+
get admin_users_path
|
33
|
+
expect(response).to be_redirect
|
34
|
+
|
35
|
+
txt = 'You must be an admin to perform that action'
|
36
|
+
expect(flash[:alert]).to eq(txt)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'NOT authenticated' do
|
42
|
+
it 'redirects to sign in page with an alert' do
|
43
|
+
get admin_users_path
|
44
|
+
expect(response).to be_redirect
|
45
|
+
|
46
|
+
txt = 'You need to sign in or sign up before continuing.'
|
47
|
+
expect(flash[:alert]).to eq(txt)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#impersonate' do
|
53
|
+
it 'changes the current user from admin to the specified user' do
|
54
|
+
sign_in(admin_user)
|
55
|
+
get impersonate_admin_user_path(user)
|
56
|
+
expect(controller.current_user).to eq(user)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#stop_impersonating' do
|
61
|
+
it 'returns the current_user to the admin user' do
|
62
|
+
sign_in(admin_user)
|
63
|
+
get impersonate_admin_user_path(user)
|
64
|
+
expect(controller.current_user).to eq(user)
|
65
|
+
get stop_impersonating_admin_users_path
|
66
|
+
expect(controller.current_user).to eq(admin_user)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe ApplicationController, type: :controller do
|
4
|
+
let(:user) { create(:user) }
|
5
|
+
let(:admin_user) { create(:user, :admin) }
|
6
|
+
|
7
|
+
controller do
|
8
|
+
skip_authorization_check only: :show
|
9
|
+
|
10
|
+
def index
|
11
|
+
raise CanCan::AccessDenied
|
12
|
+
end
|
13
|
+
|
14
|
+
def show
|
15
|
+
render body: 'show called'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'errors' do
|
20
|
+
it 'rescues from cancan errors and redirects' do
|
21
|
+
sign_in(user)
|
22
|
+
get :index
|
23
|
+
expect(response).to be_redirect
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'analytics' do
|
28
|
+
before do
|
29
|
+
allow(user).to receive(:tester?).and_return(false)
|
30
|
+
allow(Analytics).to receive(:track)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'tracks with some global defaults' do
|
34
|
+
controller.analytics_track(user, 'Signed In', page_name: 'Page')
|
35
|
+
expect(Analytics).to have_received(:track)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'trys to pull roles on a non-user object' do
|
39
|
+
allow(user).to receive(:roles).and_return(StandardError.new('bazinga'))
|
40
|
+
controller.analytics_track(user, 'Signed In', page_name: 'Page')
|
41
|
+
expect(Analytics).to have_received(:track)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'variants' do
|
46
|
+
it 'allows iPad variants' do
|
47
|
+
request.env['HTTP_USER_AGENT'] = 'iPad'
|
48
|
+
sign_in(user)
|
49
|
+
get :show, params: { id: 'anyid' }
|
50
|
+
expect(request.variant).to include(:tablet)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'allows iPhone variants' do
|
54
|
+
request.env['HTTP_USER_AGENT'] = 'iPhone'
|
55
|
+
sign_in(user)
|
56
|
+
get :show, params: { id: 'anyid' }
|
57
|
+
expect(request.variant).to include(:phone)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'allows Android phone variants' do
|
61
|
+
request.env['HTTP_USER_AGENT'] = 'Android mobile'
|
62
|
+
sign_in(user)
|
63
|
+
get :show, params: { id: 'anyid' }
|
64
|
+
expect(request.variant).to include(:phone)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'allows Android tablet variants' do
|
68
|
+
request.env['HTTP_USER_AGENT'] = 'Android'
|
69
|
+
sign_in(user)
|
70
|
+
get :show, params: { id: 'anyid' }
|
71
|
+
expect(request.variant).to include(:tablet)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'allows Windows phone variants' do
|
75
|
+
request.env['HTTP_USER_AGENT'] = 'Windows Phone'
|
76
|
+
sign_in(user)
|
77
|
+
get :show, params: { id: 'anyid' }
|
78
|
+
expect(request.variant).to include(:phone)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
feature 'Impersonate user' do
|
4
|
+
given(:admin_user) { create(:user, :admin) }
|
5
|
+
given(:regular_user) { create(:user) }
|
6
|
+
|
7
|
+
scenario 'With admin privileges' do
|
8
|
+
sign_in(admin_user.email, admin_user.password)
|
9
|
+
|
10
|
+
visit impersonate_admin_user_path(id: regular_user.id)
|
11
|
+
|
12
|
+
expect(page).to have_content('Stop Impersonating')
|
13
|
+
end
|
14
|
+
|
15
|
+
scenario 'With admin privileges: End Impersonation' do
|
16
|
+
sign_in(admin_user.email, admin_user.password)
|
17
|
+
|
18
|
+
visit impersonate_admin_user_path(id: regular_user.id)
|
19
|
+
visit stop_impersonating_admin_users_path
|
20
|
+
|
21
|
+
expect(page).to_not have_content('Stop Impersonating')
|
22
|
+
end
|
23
|
+
|
24
|
+
scenario 'Without admin privileges' do
|
25
|
+
sign_in(regular_user.email, regular_user.password)
|
26
|
+
|
27
|
+
visit impersonate_admin_user_path(id: admin_user.id)
|
28
|
+
|
29
|
+
expect(page).to have_content('You must be an admin to perform that action')
|
30
|
+
end
|
31
|
+
|
32
|
+
scenario 'Stop impersonating when not impersonating' do
|
33
|
+
visit stop_impersonating_admin_users_path
|
34
|
+
|
35
|
+
expect(page)
|
36
|
+
.to have_content('You need to sign in or sign up before continuing.')
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
feature 'Retrieve user list from browser' do
|
4
|
+
scenario 'without admin privileges' do
|
5
|
+
user = create(:user)
|
6
|
+
|
7
|
+
sign_in(user.email, user.password)
|
8
|
+
visit admin_users_path
|
9
|
+
|
10
|
+
expect(page).to have_content('You must be an admin to perform that action')
|
11
|
+
end
|
12
|
+
|
13
|
+
scenario 'with admin privileges' do
|
14
|
+
user = create(:user, :admin)
|
15
|
+
|
16
|
+
sign_in(user.email, user.password)
|
17
|
+
visit admin_users_path
|
18
|
+
|
19
|
+
expect(page).to have_content(user.email)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
feature 'User signup' do
|
4
|
+
scenario 'Valid user information' do
|
5
|
+
sign_up_with('First', 'Last', 'test@example.com', 'password', 'password')
|
6
|
+
expect(page).to have_content('Welcome! You have signed up successfully.')
|
7
|
+
end
|
8
|
+
|
9
|
+
scenario 'Passwords do not match' do
|
10
|
+
sign_up_with('First', 'Last', 'test@example.com', 'password', 'password2')
|
11
|
+
expect(page).to have_content("doesn't match Password")
|
12
|
+
end
|
13
|
+
|
14
|
+
scenario 'Email is invalid' do
|
15
|
+
sign_up_with('First', 'Last', 'test2example.com', 'password', 'password')
|
16
|
+
expect(page).to have_content('is invalid')
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe ApplicationMailer do
|
4
|
+
it 'sets the correct subject' do
|
5
|
+
subject = 'Test email subject'
|
6
|
+
mail = described_class.email('test@example.com',
|
7
|
+
subject, 'Test email body')
|
8
|
+
|
9
|
+
expect(mail.subject).to eq(subject)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'includes content in the body of the email' do
|
13
|
+
body = 'Test email body'
|
14
|
+
mail = described_class.email('test@example.com',
|
15
|
+
'Test email subject', body)
|
16
|
+
|
17
|
+
expect(mail.body).to eq(body)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'sends from no-reply@<%= app_name %>.com' do
|
21
|
+
mail = described_class.email('test@example.com',
|
22
|
+
'Test email subject', 'Test email body')
|
23
|
+
|
24
|
+
expect(mail.from).to include('no-reply@<%= app_name %>.com')
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe 'Retrieve user list from API', type: :request do
|
4
|
+
context 'with token authentication via query params' do
|
5
|
+
it 'returns status code 200' do
|
6
|
+
user = create(:user, :admin)
|
7
|
+
|
8
|
+
get admin_users_url(user_email: user.email,
|
9
|
+
user_token: user.authentication_token), as: :json
|
10
|
+
|
11
|
+
expect(response.status).to eq 200
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns valid user JSON matching schema' do
|
15
|
+
user = create(:user, :admin)
|
16
|
+
|
17
|
+
get admin_users_url(user_email: user.email,
|
18
|
+
user_token: user.authentication_token), as: :json
|
19
|
+
|
20
|
+
expect(response).to match_response_schema('user')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with token authentication via request headers' do
|
25
|
+
it 'returns status code 200' do
|
26
|
+
user = create(:user, :admin)
|
27
|
+
token_header_params = { 'X-User-Email': user.email,
|
28
|
+
'X-User-Token': user.authentication_token }
|
29
|
+
|
30
|
+
get admin_users_url, headers: token_header_params, as: :json
|
31
|
+
|
32
|
+
expect(response.status).to eq 200
|
33
|
+
end
|
34
|
+
it 'returns value user JSON matching schema' do
|
35
|
+
user = create(:user, :admin)
|
36
|
+
token_header_params = { 'X-User-Email': user.email,
|
37
|
+
'X-User-Token': user.authentication_token }
|
38
|
+
|
39
|
+
get admin_users_url, headers: token_header_params, as: :json
|
40
|
+
|
41
|
+
expect(response).to match_response_schema('user')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "Users schema based on JSON API",
|
4
|
+
"type": "object",
|
5
|
+
"required": [
|
6
|
+
"data"
|
7
|
+
],
|
8
|
+
"data": {
|
9
|
+
"description": "List of user resources.",
|
10
|
+
"type": "array",
|
11
|
+
"items": {
|
12
|
+
"type": "object",
|
13
|
+
"required": [
|
14
|
+
"type",
|
15
|
+
"id"
|
16
|
+
],
|
17
|
+
"properties": {
|
18
|
+
"type": {
|
19
|
+
"type": "string"
|
20
|
+
},
|
21
|
+
"id": {
|
22
|
+
"type": "integer"
|
23
|
+
},
|
24
|
+
"attributes": {
|
25
|
+
"required": [
|
26
|
+
"first-name",
|
27
|
+
"last-name",
|
28
|
+
"email",
|
29
|
+
"authentication-token"
|
30
|
+
],
|
31
|
+
"properties": {
|
32
|
+
"first-name": {
|
33
|
+
"type": "string"
|
34
|
+
},
|
35
|
+
"last-name": {
|
36
|
+
"type": "string"
|
37
|
+
},
|
38
|
+
"email": {
|
39
|
+
"type": "string"
|
40
|
+
},
|
41
|
+
"authentication-token": {
|
42
|
+
"type": "string"
|
43
|
+
},
|
44
|
+
"created-at": {
|
45
|
+
"type": "string",
|
46
|
+
"format": "date-time"
|
47
|
+
},
|
48
|
+
"updated-at": {
|
49
|
+
"type": "string",
|
50
|
+
"format": "date-time"
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
},
|
56
|
+
"uniqueItems": true
|
57
|
+
}
|
58
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Features
|
2
|
+
module SessionHelpers
|
3
|
+
def sign_in(email, password)
|
4
|
+
visit sign_in_path
|
5
|
+
within '.form-inputs' do
|
6
|
+
fill_in 'user_email', with: email
|
7
|
+
fill_in 'user_password', with: password
|
8
|
+
end
|
9
|
+
|
10
|
+
click_button 'Log in'
|
11
|
+
end
|
12
|
+
|
13
|
+
def sign_up_with(first, last, email, password, password_confirmation)
|
14
|
+
visit new_user_registration_path
|
15
|
+
within '.form-inputs' do
|
16
|
+
fill_in 'First name', with: first
|
17
|
+
fill_in 'Last name', with: last
|
18
|
+
|
19
|
+
fill_in 'Email', with: email
|
20
|
+
fill_in 'user_password', with: password
|
21
|
+
fill_in 'user_password_confirmation', with: password_confirmation
|
22
|
+
end
|
23
|
+
|
24
|
+
click_button 'Sign up'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
RSpec::Matchers.define :match_response_schema do |schema|
|
2
|
+
match do |response|
|
3
|
+
schema_directory = "#{Dir.pwd}/spec/support/api/schemas"
|
4
|
+
schema_path = "#{schema_directory}/#{schema}.json"
|
5
|
+
JSON::Validator.validate!(schema_path, response.body, strict: true)
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RequestSpecHelper
|
2
|
+
include Warden::Test::Helpers
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.before(:each) { Warden.test_mode! }
|
6
|
+
base.after(:each) { Warden.test_reset! }
|
7
|
+
end
|
8
|
+
|
9
|
+
def sign_in(resource)
|
10
|
+
login_as(resource, scope: warden_scope(resource))
|
11
|
+
end
|
12
|
+
|
13
|
+
def sign_out(resource)
|
14
|
+
logout(warden_scope(resource))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def warden_scope(resource)
|
20
|
+
resource.class.name.underscore.to_sym
|
21
|
+
end
|
22
|
+
end
|
data/lib/voyage/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: voyage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.44.0.
|
4
|
+
version: 1.44.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- thoughtbot, headway
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bitters
|
@@ -109,13 +109,16 @@ files:
|
|
109
109
|
- lib/voyage/templates/Gemfile.erb
|
110
110
|
- lib/voyage/templates/README.md.erb
|
111
111
|
- lib/voyage/templates/about.html.erb
|
112
|
+
- lib/voyage/templates/admin_controller.rb
|
112
113
|
- lib/voyage/templates/admin_users_controller.rb
|
113
114
|
- lib/voyage/templates/analytics_alias.html.erb.erb
|
114
115
|
- lib/voyage/templates/analytics_identify.html.erb.erb
|
115
116
|
- lib/voyage/templates/analytics_ruby_initializer.rb
|
116
117
|
- lib/voyage/templates/application.js
|
118
|
+
- lib/voyage/templates/application_mailer.rb.erb
|
117
119
|
- lib/voyage/templates/auto_annotate_models.rake
|
118
120
|
- lib/voyage/templates/config_dj_rails5_patches.rb
|
121
|
+
- lib/voyage/templates/config_initializers_ams.rb
|
119
122
|
- lib/voyage/templates/config_locales_en.yml.erb
|
120
123
|
- lib/voyage/templates/controller_helpers.rb
|
121
124
|
- lib/voyage/templates/custom_cancan_matchers.rb
|
@@ -126,6 +129,17 @@ files:
|
|
126
129
|
- lib/voyage/templates/seeder.rb.erb
|
127
130
|
- lib/voyage/templates/seeds.rb.erb
|
128
131
|
- lib/voyage/templates/simplecov.rb
|
132
|
+
- lib/voyage/templates/specs/controllers/admin/users_controller_spec.rb
|
133
|
+
- lib/voyage/templates/specs/controllers/application_controller_spec.rb
|
134
|
+
- lib/voyage/templates/specs/features/user_impersonation_spec.rb
|
135
|
+
- lib/voyage/templates/specs/features/user_list_spec.rb
|
136
|
+
- lib/voyage/templates/specs/features/user_signup_spec.rb
|
137
|
+
- lib/voyage/templates/specs/mailers/application_mailer_spec.rb.erb
|
138
|
+
- lib/voyage/templates/specs/requests/user_api_spec.rb
|
139
|
+
- lib/voyage/templates/specs/support/api/schemas/user.json
|
140
|
+
- lib/voyage/templates/specs/support/features/session_helpers.rb
|
141
|
+
- lib/voyage/templates/specs/support/matchers/api_schema_matcher.rb
|
142
|
+
- lib/voyage/templates/specs/support/request_spec_helper.rb
|
129
143
|
- lib/voyage/templates/users_index.html.erb
|
130
144
|
- lib/voyage/templates/voyage_layout.html.erb.erb
|
131
145
|
- lib/voyage/templates/welcome.html.erb
|