A very fine release.
This commit is contained in:
commit
12a1502037
@ -5,6 +5,9 @@ AllCops:
|
|||||||
- bin/**/*
|
- bin/**/*
|
||||||
- vendor/assets/**/*
|
- vendor/assets/**/*
|
||||||
|
|
||||||
|
Lint/Debugger:
|
||||||
|
AutoCorrect: False
|
||||||
|
|
||||||
Style/AndOr:
|
Style/AndOr:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
52
.sass-lint.yml
Normal file
52
.sass-lint.yml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
files:
|
||||||
|
include: site/**/*.scss
|
||||||
|
ignore:
|
||||||
|
- site/assets/scss/*bootstrap*
|
||||||
|
|
||||||
|
options:
|
||||||
|
formatter: stylish
|
||||||
|
merge-default-rules: true
|
||||||
|
|
||||||
|
# https://github.com/sasstools/sass-lint/tree/master/docs/rules
|
||||||
|
rules:
|
||||||
|
|
||||||
|
class-name-format:
|
||||||
|
- 1
|
||||||
|
- convention: 'hyphenatedbem'
|
||||||
|
|
||||||
|
force-pseudo-nesting: 0
|
||||||
|
|
||||||
|
id-name-format: 0
|
||||||
|
|
||||||
|
leading-zero:
|
||||||
|
- 1
|
||||||
|
- include: true
|
||||||
|
|
||||||
|
nesting-depth:
|
||||||
|
- 1
|
||||||
|
- max-depth: 4
|
||||||
|
|
||||||
|
no-css-comments: 0
|
||||||
|
|
||||||
|
no-color-literals:
|
||||||
|
- 1
|
||||||
|
-
|
||||||
|
allow-rgba: true
|
||||||
|
|
||||||
|
no-duplicate-properties: 1
|
||||||
|
|
||||||
|
no-qualifying-elements:
|
||||||
|
- 1
|
||||||
|
- allow-element-with-attribute: true # input[type='email'] but not div.class-name
|
||||||
|
|
||||||
|
no-vendor-prefixes: 1
|
||||||
|
|
||||||
|
property-sort-order:
|
||||||
|
- 1
|
||||||
|
-
|
||||||
|
# https://github.com/sasstools/sass-lint/blob/develop/lib/config/property-sort-orders/concentric.yml
|
||||||
|
order: concentric
|
||||||
|
# https://github.com/sasstools/sass-lint/blob/develop/lib/config/property-sort-orders/smacss.yml
|
||||||
|
# order: smacss
|
||||||
|
|
||||||
|
quotes: 0
|
4
Gemfile
4
Gemfile
@ -4,11 +4,12 @@ source 'https://rubygems.org'
|
|||||||
gem 'figaro', '~> 1.1.1'
|
gem 'figaro', '~> 1.1.1'
|
||||||
gem 'bcrypt', '~> 3.1.7'
|
gem 'bcrypt', '~> 3.1.7'
|
||||||
gem 'mysql2', '>= 0.3.18', '< 0.5'
|
gem 'mysql2', '>= 0.3.18', '< 0.5'
|
||||||
gem 'rails', '~> 5.0', '>= 5.0.0.1'
|
gem 'rails', '~> 5.0', '>= 5.0.1'
|
||||||
|
|
||||||
gem 'jbuilder', '~> 2.6'
|
gem 'jbuilder', '~> 2.6'
|
||||||
gem 'jquery-rails'
|
gem 'jquery-rails'
|
||||||
gem 'json', '~> 2.0.2'
|
gem 'json', '~> 2.0.2'
|
||||||
|
gem 'kaminari'
|
||||||
gem 'mailjet', '~> 1.3.8'
|
gem 'mailjet', '~> 1.3.8'
|
||||||
gem 'puma', '~> 3.0'
|
gem 'puma', '~> 3.0'
|
||||||
gem 'pundit'
|
gem 'pundit'
|
||||||
@ -53,6 +54,7 @@ group :development, :test do
|
|||||||
gem 'byebug', platform: :mri
|
gem 'byebug', platform: :mri
|
||||||
gem 'pry-byebug'
|
gem 'pry-byebug'
|
||||||
gem 'pry-rails'
|
gem 'pry-rails'
|
||||||
|
gem 'faker'
|
||||||
|
|
||||||
gem 'brakeman'
|
gem 'brakeman'
|
||||||
gem 'rubocop', '~> 0.42.0'
|
gem 'rubocop', '~> 0.42.0'
|
||||||
|
168
Gemfile.lock
168
Gemfile.lock
@ -1,46 +1,47 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (5.0.0.1)
|
actioncable (5.0.1)
|
||||||
actionpack (= 5.0.0.1)
|
actionpack (= 5.0.1)
|
||||||
nio4r (~> 1.2)
|
nio4r (~> 1.2)
|
||||||
websocket-driver (~> 0.6.1)
|
websocket-driver (~> 0.6.1)
|
||||||
actionmailer (5.0.0.1)
|
actionmailer (5.0.1)
|
||||||
actionpack (= 5.0.0.1)
|
actionpack (= 5.0.1)
|
||||||
actionview (= 5.0.0.1)
|
actionview (= 5.0.1)
|
||||||
activejob (= 5.0.0.1)
|
activejob (= 5.0.1)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (5.0.0.1)
|
actionpack (5.0.1)
|
||||||
actionview (= 5.0.0.1)
|
actionview (= 5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0)
|
||||||
rack-test (~> 0.6.3)
|
rack-test (~> 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (5.0.0.1)
|
actionview (5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubis (~> 2.7.0)
|
erubis (~> 2.7.0)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
activejob (5.0.0.1)
|
activejob (5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (5.0.0.1)
|
activemodel (5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
activerecord (5.0.0.1)
|
activerecord (5.0.1)
|
||||||
activemodel (= 5.0.0.1)
|
activemodel (= 5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
arel (~> 7.0)
|
arel (~> 7.0)
|
||||||
activesupport (5.0.0.1)
|
activesupport (5.0.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (~> 0.7)
|
i18n (~> 0.7)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.4.0)
|
addressable (2.5.0)
|
||||||
|
public_suffix (~> 2.0, >= 2.0.2)
|
||||||
ansi (1.5.0)
|
ansi (1.5.0)
|
||||||
arel (7.1.1)
|
arel (7.1.4)
|
||||||
ast (2.3.0)
|
ast (2.3.0)
|
||||||
awesome_print (1.7.0)
|
awesome_print (1.7.0)
|
||||||
bcrypt (3.1.11)
|
bcrypt (3.1.11)
|
||||||
@ -53,29 +54,31 @@ GEM
|
|||||||
bourbon (4.2.7)
|
bourbon (4.2.7)
|
||||||
sass (~> 3.4)
|
sass (~> 3.4)
|
||||||
thor (~> 0.19)
|
thor (~> 0.19)
|
||||||
brakeman (3.4.0)
|
brakeman (3.4.1)
|
||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
byebug (9.0.5)
|
byebug (9.0.6)
|
||||||
choice (0.2.0)
|
choice (0.2.0)
|
||||||
coderay (1.1.1)
|
coderay (1.1.1)
|
||||||
concurrent-ruby (1.0.2)
|
concurrent-ruby (1.0.4)
|
||||||
css_parser (1.4.5)
|
css_parser (1.4.7)
|
||||||
addressable
|
addressable
|
||||||
debug_inspector (0.0.2)
|
debug_inspector (0.0.2)
|
||||||
docile (1.1.5)
|
docile (1.1.5)
|
||||||
domain_name (0.5.20160615)
|
domain_name (0.5.20161129)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
em-websocket (0.5.1)
|
em-websocket (0.5.1)
|
||||||
eventmachine (>= 0.12.9)
|
eventmachine (>= 0.12.9)
|
||||||
http_parser.rb (~> 0.6.0)
|
http_parser.rb (~> 0.6.0)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
eventmachine (1.2.0.1)
|
eventmachine (1.2.1)
|
||||||
execjs (2.7.0)
|
execjs (2.7.0)
|
||||||
|
faker (1.7.3)
|
||||||
|
i18n (~> 0.5)
|
||||||
ffi (1.9.14)
|
ffi (1.9.14)
|
||||||
figaro (1.1.1)
|
figaro (1.1.1)
|
||||||
thor (~> 0.14)
|
thor (~> 0.14)
|
||||||
formatador (0.2.5)
|
formatador (0.2.5)
|
||||||
foundation_emails (2.2.0.0)
|
foundation_emails (2.2.1.0)
|
||||||
globalid (0.3.7)
|
globalid (0.3.7)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
guard (2.14.0)
|
guard (2.14.0)
|
||||||
@ -106,27 +109,40 @@ GEM
|
|||||||
guard (>= 2.0.0)
|
guard (>= 2.0.0)
|
||||||
guard-compat (~> 1.0)
|
guard-compat (~> 1.0)
|
||||||
htmlentities (4.3.4)
|
htmlentities (4.3.4)
|
||||||
http-cookie (1.0.2)
|
http-cookie (1.0.3)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
http_parser.rb (0.6.0)
|
http_parser.rb (0.6.0)
|
||||||
i18n (0.7.0)
|
i18n (0.7.0)
|
||||||
inky-rb (1.3.6.1)
|
inky-rb (1.3.7.2)
|
||||||
foundation_emails (~> 2)
|
foundation_emails (~> 2)
|
||||||
jbuilder (2.6.0)
|
nokogiri
|
||||||
|
jbuilder (2.6.1)
|
||||||
activesupport (>= 3.0.0, < 5.1)
|
activesupport (>= 3.0.0, < 5.1)
|
||||||
multi_json (~> 1.2)
|
multi_json (~> 1.2)
|
||||||
jquery-rails (4.2.1)
|
jquery-rails (4.2.2)
|
||||||
rails-dom-testing (>= 1, < 3)
|
rails-dom-testing (>= 1, < 3)
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
json (2.0.2)
|
json (2.0.3)
|
||||||
|
kaminari (1.0.1)
|
||||||
|
activesupport (>= 4.1.0)
|
||||||
|
kaminari-actionview (= 1.0.1)
|
||||||
|
kaminari-activerecord (= 1.0.1)
|
||||||
|
kaminari-core (= 1.0.1)
|
||||||
|
kaminari-actionview (1.0.1)
|
||||||
|
actionview
|
||||||
|
kaminari-core (= 1.0.1)
|
||||||
|
kaminari-activerecord (1.0.1)
|
||||||
|
activerecord
|
||||||
|
kaminari-core (= 1.0.1)
|
||||||
|
kaminari-core (1.0.1)
|
||||||
listen (3.1.5)
|
listen (3.1.5)
|
||||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||||
rb-inotify (~> 0.9, >= 0.9.7)
|
rb-inotify (~> 0.9, >= 0.9.7)
|
||||||
ruby_dep (~> 1.2)
|
ruby_dep (~> 1.2)
|
||||||
loofah (2.0.3)
|
loofah (2.0.3)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
lumberjack (1.0.10)
|
lumberjack (1.0.11)
|
||||||
mail (2.6.4)
|
mail (2.6.4)
|
||||||
mime-types (>= 1.16, < 4)
|
mime-types (>= 1.16, < 4)
|
||||||
mailjet (1.3.8)
|
mailjet (1.3.8)
|
||||||
@ -138,29 +154,27 @@ GEM
|
|||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2016.0521)
|
mime-types-data (3.2016.0521)
|
||||||
mini_portile2 (2.1.0)
|
mini_portile2 (2.1.0)
|
||||||
minitest (5.9.0)
|
minitest (5.10.1)
|
||||||
minitest-reporters (1.1.11)
|
minitest-reporters (1.1.13)
|
||||||
ansi
|
ansi
|
||||||
builder
|
builder
|
||||||
minitest (>= 5.0)
|
minitest (>= 5.0)
|
||||||
ruby-progressbar
|
ruby-progressbar
|
||||||
multi_json (1.12.1)
|
multi_json (1.12.1)
|
||||||
mysql2 (0.4.4)
|
mysql2 (0.4.5)
|
||||||
neat (1.8.0)
|
neat (1.8.0)
|
||||||
sass (>= 3.3)
|
sass (>= 3.3)
|
||||||
thor (~> 0.19)
|
thor (~> 0.19)
|
||||||
nenv (0.3.0)
|
nenv (0.3.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
nio4r (1.2.1)
|
nio4r (1.2.1)
|
||||||
nokogiri (1.6.8)
|
nokogiri (1.7.0.1)
|
||||||
mini_portile2 (~> 2.1.0)
|
mini_portile2 (~> 2.1.0)
|
||||||
pkg-config (~> 1.1.7)
|
|
||||||
notiffany (0.1.1)
|
notiffany (0.1.1)
|
||||||
nenv (~> 0.1)
|
nenv (~> 0.1)
|
||||||
shellany (~> 0.0)
|
shellany (~> 0.0)
|
||||||
parser (2.3.1.2)
|
parser (2.3.3.1)
|
||||||
ast (~> 2.2)
|
ast (~> 2.2)
|
||||||
pkg-config (1.1.7)
|
|
||||||
policy-assertions (0.0.3)
|
policy-assertions (0.0.3)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
pundit (>= 1.0.0)
|
pundit (>= 1.0.0)
|
||||||
@ -168,19 +182,20 @@ GEM
|
|||||||
premailer (1.8.7)
|
premailer (1.8.7)
|
||||||
css_parser (>= 1.4.5)
|
css_parser (>= 1.4.5)
|
||||||
htmlentities (>= 4.0.0)
|
htmlentities (>= 4.0.0)
|
||||||
premailer-rails (1.9.4)
|
premailer-rails (1.9.5)
|
||||||
actionmailer (>= 3, < 6)
|
actionmailer (>= 3, < 6)
|
||||||
premailer (~> 1.7, >= 1.7.9)
|
premailer (~> 1.7, >= 1.7.9)
|
||||||
pry (0.10.4)
|
pry (0.10.4)
|
||||||
coderay (~> 1.1.0)
|
coderay (~> 1.1.0)
|
||||||
method_source (~> 0.8.1)
|
method_source (~> 0.8.1)
|
||||||
slop (~> 3.4)
|
slop (~> 3.4)
|
||||||
pry-byebug (3.4.0)
|
pry-byebug (3.4.2)
|
||||||
byebug (~> 9.0)
|
byebug (~> 9.0)
|
||||||
pry (~> 0.10)
|
pry (~> 0.10)
|
||||||
pry-rails (0.3.4)
|
pry-rails (0.3.4)
|
||||||
pry (>= 0.9.10)
|
pry (>= 0.9.10)
|
||||||
puma (3.6.0)
|
public_suffix (2.0.5)
|
||||||
|
puma (3.6.2)
|
||||||
pundit (1.1.0)
|
pundit (1.1.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
rack (2.0.1)
|
rack (2.0.1)
|
||||||
@ -188,25 +203,25 @@ GEM
|
|||||||
rack
|
rack
|
||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails (5.0.0.1)
|
rails (5.0.1)
|
||||||
actioncable (= 5.0.0.1)
|
actioncable (= 5.0.1)
|
||||||
actionmailer (= 5.0.0.1)
|
actionmailer (= 5.0.1)
|
||||||
actionpack (= 5.0.0.1)
|
actionpack (= 5.0.1)
|
||||||
actionview (= 5.0.0.1)
|
actionview (= 5.0.1)
|
||||||
activejob (= 5.0.0.1)
|
activejob (= 5.0.1)
|
||||||
activemodel (= 5.0.0.1)
|
activemodel (= 5.0.1)
|
||||||
activerecord (= 5.0.0.1)
|
activerecord (= 5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
bundler (>= 1.3.0, < 2.0)
|
bundler (>= 1.3.0, < 2.0)
|
||||||
railties (= 5.0.0.1)
|
railties (= 5.0.1)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.1)
|
rails-controller-testing (1.0.1)
|
||||||
actionpack (~> 5.x)
|
actionpack (~> 5.x)
|
||||||
actionview (~> 5.x)
|
actionview (~> 5.x)
|
||||||
activesupport (~> 5.x)
|
activesupport (~> 5.x)
|
||||||
rails-dom-testing (2.0.1)
|
rails-dom-testing (2.0.2)
|
||||||
activesupport (>= 4.2.0, < 6.0)
|
activesupport (>= 4.2.0, < 6.0)
|
||||||
nokogiri (~> 1.6.0)
|
nokogiri (~> 1.6)
|
||||||
rails-erd (1.5.0)
|
rails-erd (1.5.0)
|
||||||
activerecord (>= 3.2)
|
activerecord (>= 3.2)
|
||||||
activesupport (>= 3.2)
|
activesupport (>= 3.2)
|
||||||
@ -214,15 +229,15 @@ GEM
|
|||||||
ruby-graphviz (~> 1.2)
|
ruby-graphviz (~> 1.2)
|
||||||
rails-html-sanitizer (1.0.3)
|
rails-html-sanitizer (1.0.3)
|
||||||
loofah (~> 2.0)
|
loofah (~> 2.0)
|
||||||
railties (5.0.0.1)
|
railties (5.0.1)
|
||||||
actionpack (= 5.0.0.1)
|
actionpack (= 5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.18.1, < 2.0)
|
thor (>= 0.18.1, < 2.0)
|
||||||
rainbow (2.1.0)
|
rainbow (2.2.1)
|
||||||
rake (11.2.2)
|
rake (12.0.0)
|
||||||
rb-fsevent (0.9.7)
|
rb-fsevent (0.9.8)
|
||||||
rb-inotify (0.9.7)
|
rb-inotify (0.9.7)
|
||||||
ffi (>= 0.5.0)
|
ffi (>= 0.5.0)
|
||||||
rest-client (2.0.0)
|
rest-client (2.0.0)
|
||||||
@ -237,8 +252,8 @@ GEM
|
|||||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||||
ruby-graphviz (1.2.2)
|
ruby-graphviz (1.2.2)
|
||||||
ruby-progressbar (1.8.1)
|
ruby-progressbar (1.8.1)
|
||||||
ruby_dep (1.4.0)
|
ruby_dep (1.5.0)
|
||||||
sass (3.4.22)
|
sass (3.4.23)
|
||||||
sass-rails (5.0.6)
|
sass-rails (5.0.6)
|
||||||
railties (>= 4.0.0, < 6)
|
railties (>= 4.0.0, < 6)
|
||||||
sass (~> 3.1)
|
sass (~> 3.1)
|
||||||
@ -253,18 +268,19 @@ GEM
|
|||||||
simplecov-html (~> 0.10.0)
|
simplecov-html (~> 0.10.0)
|
||||||
simplecov-html (0.10.0)
|
simplecov-html (0.10.0)
|
||||||
slop (3.6.0)
|
slop (3.6.0)
|
||||||
spring (1.7.2)
|
spring (2.0.0)
|
||||||
spring-watcher-listen (2.0.0)
|
activesupport (>= 4.2)
|
||||||
|
spring-watcher-listen (2.0.1)
|
||||||
listen (>= 2.7, < 4.0)
|
listen (>= 2.7, < 4.0)
|
||||||
spring (~> 1.2)
|
spring (>= 1.2, < 3.0)
|
||||||
sprockets (3.7.0)
|
sprockets (3.7.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (> 1, < 3)
|
rack (> 1, < 3)
|
||||||
sprockets-rails (3.1.1)
|
sprockets-rails (3.2.0)
|
||||||
actionpack (>= 4.0)
|
actionpack (>= 4.0)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
thor (0.19.1)
|
thor (0.19.4)
|
||||||
thread_safe (0.3.5)
|
thread_safe (0.3.5)
|
||||||
tilt (2.0.5)
|
tilt (2.0.5)
|
||||||
turbolinks (5.0.1)
|
turbolinks (5.0.1)
|
||||||
@ -272,13 +288,13 @@ GEM
|
|||||||
turbolinks-source (5.0.0)
|
turbolinks-source (5.0.0)
|
||||||
tzinfo (1.2.2)
|
tzinfo (1.2.2)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
uglifier (3.0.2)
|
uglifier (3.0.4)
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.7.2)
|
unf_ext (0.0.7.2)
|
||||||
unicode-display_width (1.1.1)
|
unicode-display_width (1.1.2)
|
||||||
web-console (3.3.1)
|
web-console (3.4.0)
|
||||||
actionview (>= 5.0)
|
actionview (>= 5.0)
|
||||||
activemodel (>= 5.0)
|
activemodel (>= 5.0)
|
||||||
debug_inspector
|
debug_inspector
|
||||||
@ -298,6 +314,7 @@ DEPENDENCIES
|
|||||||
bourbon
|
bourbon
|
||||||
brakeman
|
brakeman
|
||||||
byebug
|
byebug
|
||||||
|
faker
|
||||||
figaro (~> 1.1.1)
|
figaro (~> 1.1.1)
|
||||||
guard
|
guard
|
||||||
guard-brakeman
|
guard-brakeman
|
||||||
@ -309,6 +326,7 @@ DEPENDENCIES
|
|||||||
jbuilder (~> 2.6)
|
jbuilder (~> 2.6)
|
||||||
jquery-rails
|
jquery-rails
|
||||||
json (~> 2.0.2)
|
json (~> 2.0.2)
|
||||||
|
kaminari
|
||||||
listen
|
listen
|
||||||
mailjet (~> 1.3.8)
|
mailjet (~> 1.3.8)
|
||||||
minitest-reporters
|
minitest-reporters
|
||||||
@ -321,7 +339,7 @@ DEPENDENCIES
|
|||||||
puma (~> 3.0)
|
puma (~> 3.0)
|
||||||
pundit
|
pundit
|
||||||
rack-livereload
|
rack-livereload
|
||||||
rails (~> 5.0, >= 5.0.0.1)
|
rails (~> 5.0, >= 5.0.1)
|
||||||
rails-controller-testing
|
rails-controller-testing
|
||||||
rails-erd
|
rails-erd
|
||||||
rubocop (~> 0.42.0)
|
rubocop (~> 0.42.0)
|
||||||
@ -336,4 +354,4 @@ DEPENDENCIES
|
|||||||
web-console
|
web-console
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.12.5
|
1.13.3
|
||||||
|
@ -79,7 +79,7 @@ guard :shell, all_on_start: true do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
guard :rubocop, cli: %w(-D -S) do
|
guard :rubocop, cli: %w(-D -S -a) do
|
||||||
watch(/.rubocop.yml/)
|
watch(/.rubocop.yml/)
|
||||||
watch(/.+\.rb$/)
|
watch(/.+\.rb$/)
|
||||||
watch(/Rakefile/)
|
watch(/Rakefile/)
|
||||||
|
BIN
app/assets/images/ic_arrow_drop_down_black_24dp_1x.png
Normal file
BIN
app/assets/images/ic_arrow_drop_down_black_24dp_1x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 B |
BIN
app/assets/images/ic_arrow_drop_down_black_24dp_2x.png
Normal file
BIN
app/assets/images/ic_arrow_drop_down_black_24dp_2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 119 B |
BIN
app/assets/images/ic_arrow_drop_up_black_24dp_1x.png
Normal file
BIN
app/assets/images/ic_arrow_drop_up_black_24dp_1x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 95 B |
BIN
app/assets/images/ic_arrow_drop_up_black_24dp_2x.png
Normal file
BIN
app/assets/images/ic_arrow_drop_up_black_24dp_2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 119 B |
BIN
app/assets/images/ic_sort_black_24dp_1x.png
Normal file
BIN
app/assets/images/ic_sort_black_24dp_1x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 89 B |
BIN
app/assets/images/ic_sort_black_24dp_2x.png
Normal file
BIN
app/assets/images/ic_sort_black_24dp_2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 103 B |
@ -18,11 +18,6 @@ function updateVotes(data){
|
|||||||
$("[data-id=my-vote]").html(data.myVote);
|
$("[data-id=my-vote]").html(data.myVote);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateVeto(data){
|
|
||||||
$("[data-id=interview-request]").html(data.requestCopy);
|
|
||||||
$("[data-id=interview-decline]").html(data.declineCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('[data-id=ajax-action]').each(function(){ handleAjaxResponse($(this)); });
|
$('[data-id=ajax-action]').each(function(){ handleAjaxResponse($(this)); });
|
||||||
});
|
});
|
||||||
@ -30,7 +25,3 @@ $(document).ready(function() {
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('[data-id=vote-count]').each(function(){ handleAjaxResponse($(this), updateVotes); });
|
$('[data-id=vote-count]').each(function(){ handleAjaxResponse($(this), updateVotes); });
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
$('[data-id=veto-status]').each(function(){ handleAjaxResponse($(this), updateVeto); });
|
|
||||||
});
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
.admin-review {
|
.admin-review {
|
||||||
counter-reset: question;
|
counter-reset: question;
|
||||||
|
float: left;
|
||||||
|
width: 66%;
|
||||||
|
|
||||||
form {
|
form {
|
||||||
margin-left: 2.3em;
|
margin-left: 2.3em;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: counter(question) ") ";
|
content: counter(question) ') ';
|
||||||
counter-increment: question;
|
counter-increment: question;
|
||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
left: -1.8em;
|
left: -1.8em;
|
||||||
@ -17,6 +19,67 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.review-comments {
|
||||||
|
float: right;
|
||||||
|
padding-left: 30px;
|
||||||
|
width: 33%;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-message {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-author {
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '- ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-edit-stamp {
|
||||||
|
color: rgba($gray-dark, 0.65);
|
||||||
|
font-size: 0.75em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-edit-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 2px 5px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $gray-base;
|
||||||
|
color: $gray-lighter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-edit-form {
|
||||||
|
display: none;
|
||||||
|
margin: 30px 0;
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="checkbox"] {
|
||||||
|
&:checked + .comment-edit-form {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.review_meta {
|
.review_meta {
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
@ -24,8 +87,32 @@
|
|||||||
& > div { flex: 1 1 auto; }
|
& > div { flex: 1 1 auto; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.review_meta__votes,
|
.review_meta__votes {
|
||||||
.review_meta__vetos {
|
margin-bottom: 15px;
|
||||||
a { padding: 5px; }
|
a { padding: 5px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.review_meta__vetos {
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-status-comments {
|
||||||
|
opacity: 0;
|
||||||
|
padding: 15px 0;
|
||||||
|
height: 0;
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.500s;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked ~ .review-status-comments {
|
||||||
|
opacity: 1;
|
||||||
|
height: auto;
|
||||||
|
max-height: 9999px;
|
||||||
|
overflow: auto;
|
||||||
|
transition: all 0.7500s;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,50 @@ th {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
padding: $small-spacing 0;
|
padding: $small-spacing 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 18px;
|
||||||
|
padding-right: 5px;
|
||||||
|
position: relative;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background-image: asset_data_url("ic_sort_black_24dp_2x.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 18px;
|
||||||
|
left: 100%;
|
||||||
|
opacity: 0.5;
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
width: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.asc {
|
||||||
|
&::after {
|
||||||
|
background-image: asset_data_url("ic_arrow_drop_up_black_24dp_2x.png");
|
||||||
|
height: 25px;
|
||||||
|
left: calc(100% - 5px);
|
||||||
|
opacity: 1;
|
||||||
|
top: 1px;
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.desc {
|
||||||
|
&::after {
|
||||||
|
background-image: asset_data_url("ic_arrow_drop_down_black_24dp_2x.png");
|
||||||
|
height: 25px;
|
||||||
|
left: calc(100% - 5px);
|
||||||
|
opacity: 1;
|
||||||
|
top: 1px;
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
|
@ -15,7 +15,7 @@ module Admin
|
|||||||
|
|
||||||
if user && user.authenticate(auth_params[:password])
|
if user && user.authenticate(auth_params[:password])
|
||||||
session[:user] = user.to_i
|
session[:user] = user.to_i
|
||||||
redirect_to admin_path
|
redirect_to session[:request] || admin_path
|
||||||
else
|
else
|
||||||
redirect_to admin_login_path,
|
redirect_to admin_login_path,
|
||||||
flash: { error: "Sorry, incorrect email or password. Please try again." }
|
flash: { error: "Sorry, incorrect email or password. Please try again." }
|
||||||
|
@ -4,7 +4,8 @@ module Admin
|
|||||||
before_action :collect_quizzes, except: [:login, :auth]
|
before_action :collect_quizzes, except: [:login, :auth]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@candidates = policy_scope Candidate.order(:name)
|
@candidates = policy_scope Candidate.order("#{sort_column} #{sort_direction}")
|
||||||
|
.page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@ -49,13 +50,14 @@ module Admin
|
|||||||
authorize Candidate
|
authorize Candidate
|
||||||
candidate = Candidate.find_by(id: params[:id])
|
candidate = Candidate.find_by(id: params[:id])
|
||||||
CandidateMailer.welcome(candidate).deliver_later
|
CandidateMailer.welcome(candidate).deliver_later
|
||||||
render json: { message: "Email queued!" }.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def candidate_params
|
def candidate_params
|
||||||
params.require(:candidate).permit(:name, :email, :experience, :quiz_id)
|
params.require(:candidate).permit(
|
||||||
|
:name, :email, :experience, :quiz_id, :project, :position, :skill_needs
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def collect_quizzes
|
def collect_quizzes
|
||||||
@ -66,5 +68,9 @@ module Admin
|
|||||||
CandidateMailer.welcome(candidate).deliver_later
|
CandidateMailer.welcome(candidate).deliver_later
|
||||||
RecruiterMailer.candidate_created(candidate).deliver_later
|
RecruiterMailer.candidate_created(candidate).deliver_later
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sort_column
|
||||||
|
Candidate.column_names.include?(params[:sort]) ? params[:sort] : 'name'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
37
app/controllers/admin/comment_controller.rb
Normal file
37
app/controllers/admin/comment_controller.rb
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
module Admin
|
||||||
|
class CommentController < AdminController
|
||||||
|
def update
|
||||||
|
comment = QuizComment.find_by(id: params[:id], test_hash: params[:test_hash])
|
||||||
|
authorize comment
|
||||||
|
|
||||||
|
comment.update(comment_params)
|
||||||
|
flash_message = if comment.save
|
||||||
|
{ success: "Sucessfully updated comment" }
|
||||||
|
else
|
||||||
|
{ error: "Failed to update comment" }
|
||||||
|
end
|
||||||
|
redirect_to admin_result_path(params[:test_hash]), flash: flash_message
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
comment = QuizComment.new(comment_params.merge(user_id: current_user.id, test_hash: params[:test_hash]))
|
||||||
|
authorize comment
|
||||||
|
|
||||||
|
flash_message = if comment.save
|
||||||
|
{ success: "Sucessfully created comment" }
|
||||||
|
else
|
||||||
|
{ error: "Failed to save comment" }
|
||||||
|
end
|
||||||
|
|
||||||
|
ReviewerMailer.new_comment(comment).deliver_later if comment.persisted?
|
||||||
|
redirect_to admin_result_path(params[:test_hash]), flash: flash_message
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def comment_params
|
||||||
|
params.require(:quiz_comment).permit(:message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,7 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
module Admin
|
module Admin
|
||||||
class ResultController < AdminController
|
class ResultController < AdminController
|
||||||
#
|
|
||||||
# TODO: change context from Candidate to Quiz
|
# TODO: change context from Candidate to Quiz
|
||||||
# bypass pundit lockdowns until completed
|
# bypass pundit lockdowns until completed
|
||||||
after_action :skip_policy_scope
|
after_action :skip_policy_scope
|
||||||
@ -10,13 +9,30 @@ module Admin
|
|||||||
|
|
||||||
# TODO: Limit results to the quizzes current_user has access to
|
# TODO: Limit results to the quizzes current_user has access to
|
||||||
def index
|
def index
|
||||||
@candidates = Candidate.where(completed: true).includes(:recruiter)
|
sort_case = "(case when review_status = 0 then '' else name end)"
|
||||||
|
sort_with_case = sort_column == 'name' ? sort_case : sort_column
|
||||||
|
@candidates = Candidate.where(completed: true)
|
||||||
|
.includes(:recruiter)
|
||||||
|
.order("#{sort_with_case} #{sort_direction}")
|
||||||
|
.page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def view
|
def view
|
||||||
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
||||||
@quiz = @candidate.my_quiz
|
@quiz = @candidate.my_quiz
|
||||||
@status = QuizStatus.new(@candidate)
|
@status = QuizStatus.new(@candidate)
|
||||||
|
@comments = QuizComment.includes(:user).where(test_hash: @candidate.test_hash).order(:created_at)
|
||||||
|
@comment = QuizComment.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def sort_column
|
||||||
|
@sort_col ||= Candidate.column_names.include?(params[:sort]) ? params[:sort] : 'completed_at'
|
||||||
|
end
|
||||||
|
|
||||||
|
def sort_direction
|
||||||
|
%w(asc desc).include?(params[:direction]) ? params[:direction] : 'desc'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
module Admin
|
module Admin
|
||||||
class UserController < AdminController
|
class UserController < AdminController
|
||||||
def index
|
def index
|
||||||
@users = policy_scope User.order(:name)
|
@users = policy_scope User.order("#{sort_column} #{sort_direction}")
|
||||||
|
.page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@ -52,5 +53,9 @@ module Admin
|
|||||||
def user_params
|
def user_params
|
||||||
params.require(:user).permit(policy(User).permitted_attributes)
|
params.require(:user).permit(policy(User).permitted_attributes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sort_column
|
||||||
|
User.column_names.include?(params[:sort]) ? params[:sort] : 'name'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,54 +5,41 @@ module Admin
|
|||||||
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
||||||
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
||||||
current_user.cast_yea_on(@candidate)
|
current_user.cast_yea_on(@candidate)
|
||||||
|
|
||||||
results = {
|
|
||||||
message: "Vote Counted",
|
|
||||||
upCount: @candidate.votes.yea.count,
|
|
||||||
downCount: @candidate.votes.nay.count,
|
|
||||||
myVote: "yea"
|
|
||||||
}
|
|
||||||
render json: results.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
def down
|
||||||
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
||||||
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
||||||
current_user.cast_nay_on(@candidate)
|
current_user.cast_nay_on(@candidate)
|
||||||
|
|
||||||
results = {
|
|
||||||
message: "Vote Counted",
|
|
||||||
upCount: @candidate.votes.yea.count,
|
|
||||||
downCount: @candidate.votes.nay.count,
|
|
||||||
myVote: "nay"
|
|
||||||
}
|
|
||||||
render json: results.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def approve
|
def interview_request
|
||||||
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
||||||
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
||||||
current_user.approve_candidate(@candidate)
|
|
||||||
|
|
||||||
results = {
|
if interview_params[:review_comments].blank?
|
||||||
message: "Interview requested!",
|
refuse_interview_request
|
||||||
requestCopy: "Requested",
|
else
|
||||||
declineCopy: "Decline Interview"
|
send_interview_request
|
||||||
}
|
end
|
||||||
render json: results.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def decline
|
private
|
||||||
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
|
||||||
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
|
||||||
current_user.decline_candidate(@candidate)
|
|
||||||
|
|
||||||
results = {
|
def refuse_interview_request
|
||||||
message: "Interview declined.",
|
redirect_to admin_result_path(@candidate.test_hash),
|
||||||
requestCopy: "Request Interview",
|
flash: { error: "Must provide a comment" }
|
||||||
declineCopy: "Declined"
|
end
|
||||||
}
|
|
||||||
render json: results.to_json
|
def send_interview_request
|
||||||
end
|
current_user.review_candidate(@candidate, interview_params)
|
||||||
|
RecruiterMailer.candidate_reviewed(@candidate).deliver_later
|
||||||
|
redirect_to admin_result_path(@candidate.test_hash),
|
||||||
|
flash: { notice: "Quiz #{interview_params[:review_status]}" }
|
||||||
|
end
|
||||||
|
|
||||||
|
def interview_params
|
||||||
|
params.permit(:review_status, :review_comments)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -9,6 +9,9 @@ class AdminController < ApplicationController
|
|||||||
after_action :verify_authorized, except: :index
|
after_action :verify_authorized, except: :index
|
||||||
after_action :verify_policy_scoped, only: :index
|
after_action :verify_policy_scoped, only: :index
|
||||||
|
|
||||||
|
helper_method :sort_direction
|
||||||
|
helper_method :sort_column
|
||||||
|
|
||||||
def current_user
|
def current_user
|
||||||
@current_user ||= User.find_by(id: session[:user]) if session[:user]
|
@current_user ||= User.find_by(id: session[:user]) if session[:user]
|
||||||
end
|
end
|
||||||
@ -16,7 +19,16 @@ class AdminController < ApplicationController
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def sort_column
|
||||||
|
:completed_at
|
||||||
|
end
|
||||||
|
|
||||||
|
def sort_direction
|
||||||
|
%w(asc desc).include?(params[:direction]) ? params[:direction] : 'asc'
|
||||||
|
end
|
||||||
|
|
||||||
def authorize_user
|
def authorize_user
|
||||||
|
session[:request] = request.fullpath
|
||||||
redirect_to admin_login_path unless current_user
|
redirect_to admin_login_path unless current_user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class QuizController < ApplicationController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def complete_and_email
|
def complete_and_email
|
||||||
if current_candidate.update_attributes(completed: true)
|
if current_candidate.update_attributes(completed: true, completed_at: DateTime.current)
|
||||||
current_candidate.build_reviews
|
current_candidate.build_reviews
|
||||||
CandidateMailer.submitted(current_candidate).deliver_later
|
CandidateMailer.submitted(current_candidate).deliver_later
|
||||||
RecruiterMailer.candidate_submitted(current_candidate).deliver_later
|
RecruiterMailer.candidate_submitted(current_candidate).deliver_later
|
||||||
|
@ -44,4 +44,11 @@ module ApplicationHelper
|
|||||||
@js_blocks << code_label
|
@js_blocks << code_label
|
||||||
content_for :custom_javascipt, &block
|
content_for :custom_javascipt, &block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sortable(column, title = nil)
|
||||||
|
title ||= column.titleize
|
||||||
|
css_class = column == sort_column ? sort_direction.to_s : nil
|
||||||
|
direction = column == sort_column && sort_direction == "desc" ? "asc" : "desc"
|
||||||
|
link_to title, { sort: column, direction: direction }, class: css_class
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class RecruiterMailer < ApplicationMailer
|
class RecruiterMailer < ApplicationMailer
|
||||||
def candidate_created candidate
|
def candidate_created candidate
|
||||||
@candidate = candidate
|
@candidate = Candidate.find_by(id: candidate.to_i)
|
||||||
|
|
||||||
mail to: @candidate.recruiter.email, subject: "Skills Assessment Test - #{candidate.name}"
|
mail to: @candidate.recruiter.email,
|
||||||
|
subject: "Skills Assessment Test - #{candidate.name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def candidate_submitted candidate
|
def candidate_submitted candidate
|
||||||
@candidate = candidate
|
@candidate = Candidate.find_by(id: candidate.to_i)
|
||||||
|
|
||||||
mail to: @candidate.recruiter.email, subject: "Skills Assessment Test - #{candidate.name}"
|
mail to: @candidate.recruiter.email,
|
||||||
|
subject: "Skills Assessment Test - #{candidate.name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def candidate_reviewed candidate
|
||||||
|
@candidate = Candidate.find_by(id: candidate.to_i)
|
||||||
|
|
||||||
|
mail to: @candidate.recruiter.email,
|
||||||
|
subject: "Skills Assesment - Review Complete for #{candidate.name}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,4 +6,24 @@ class ReviewerMailer < ApplicationMailer
|
|||||||
|
|
||||||
mail to: recipients, subject: "Skills Assessment Results - #{@candidate.test_hash}"
|
mail to: recipients, subject: "Skills Assessment Results - #{@candidate.test_hash}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reminder reminder
|
||||||
|
@reminder = reminder
|
||||||
|
|
||||||
|
mail to: reminder.email, subject: "Review Reminder"
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_manager candidate_id
|
||||||
|
@candidate = Candidate.find_by(id: candidate_id)
|
||||||
|
@manager = @candidate.manager
|
||||||
|
|
||||||
|
mail to: @manager.email, subject: "Voting Complete"
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_comment comment
|
||||||
|
@comment = comment
|
||||||
|
recipients = comment.candidate.reviewers.map(&:email)
|
||||||
|
|
||||||
|
mail to: recipients, subject: "Skills Assessment Review Comment - #{@comment.test_hash}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,6 +6,7 @@ class Candidate < ApplicationRecord
|
|||||||
belongs_to :recruiter, class_name: "User"
|
belongs_to :recruiter, class_name: "User"
|
||||||
has_many :votes, class_name: "ReviewerVote"
|
has_many :votes, class_name: "ReviewerVote"
|
||||||
has_many :reviewers, through: :quiz
|
has_many :reviewers, through: :quiz
|
||||||
|
has_many :quiz_comments, foreign_key: :test_hash, primary_key: :test_hash
|
||||||
|
|
||||||
serialize :email, CryptSerializer
|
serialize :email, CryptSerializer
|
||||||
|
|
||||||
@ -14,6 +15,8 @@ class Candidate < ApplicationRecord
|
|||||||
validates :recruiter_id, presence: true
|
validates :recruiter_id, presence: true
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
validates :experience, presence: true
|
validates :experience, presence: true
|
||||||
|
validates :project, presence: true
|
||||||
|
validates :position, presence: true
|
||||||
validates :email, uniqueness: true, presence: true, email_format: true
|
validates :email, uniqueness: true, presence: true, email_format: true
|
||||||
validates :test_hash, uniqueness: true, presence: true
|
validates :test_hash, uniqueness: true, presence: true
|
||||||
|
|
||||||
@ -23,12 +26,24 @@ class Candidate < ApplicationRecord
|
|||||||
declined: 2
|
declined: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def interview?
|
||||||
|
return 'yes' if approved?
|
||||||
|
'no' if declined?
|
||||||
|
end
|
||||||
|
|
||||||
def build_reviews
|
def build_reviews
|
||||||
reviewers.each do |reviewer|
|
reviewers.each do |reviewer|
|
||||||
votes.find_or_create_by(user_id: reviewer.id)
|
votes.find_or_create_by(user_id: reviewer.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def manager
|
||||||
|
manager_votes = votes.joins(:user).where("users.role = 'manager'")
|
||||||
|
return User.new(name: "No Manager") if manager_votes.empty?
|
||||||
|
|
||||||
|
manager_votes.first.user
|
||||||
|
end
|
||||||
|
|
||||||
def submitted_answers
|
def submitted_answers
|
||||||
answers.where(submitted: true)
|
answers.where(submitted: true)
|
||||||
end
|
end
|
||||||
|
11
app/models/quiz_comment.rb
Normal file
11
app/models/quiz_comment.rb
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class QuizComment < ApplicationRecord
|
||||||
|
belongs_to :user
|
||||||
|
belongs_to :candidate, foreign_key: :test_hash, primary_key: :test_hash
|
||||||
|
|
||||||
|
validates :message, presence: true
|
||||||
|
|
||||||
|
def edits?
|
||||||
|
updated_at > (created_at + 5.seconds)
|
||||||
|
end
|
||||||
|
end
|
@ -5,6 +5,8 @@ class ReviewerVote < ApplicationRecord
|
|||||||
|
|
||||||
validates :user_id, uniqueness: { scope: :candidate_id }
|
validates :user_id, uniqueness: { scope: :candidate_id }
|
||||||
|
|
||||||
|
after_save :notify_manager
|
||||||
|
|
||||||
enum vote: {
|
enum vote: {
|
||||||
undecided: 0,
|
undecided: 0,
|
||||||
yea: 1,
|
yea: 1,
|
||||||
@ -13,6 +15,40 @@ class ReviewerVote < ApplicationRecord
|
|||||||
|
|
||||||
enum veto: {
|
enum veto: {
|
||||||
approved: 1,
|
approved: 1,
|
||||||
rejected: 2
|
declined: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def notify_manager
|
||||||
|
ReviewerMailer.notify_manager(candidate_id).deliver_now if all_reviewers_voted?
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_reviewers_voted? # rubocop:disable Metrics/MethodLength
|
||||||
|
sql = " select distinct rev.candidate_id
|
||||||
|
, full_votes.voters, full_votes.vote_count
|
||||||
|
, c.test_hash
|
||||||
|
from reviewer_votes rev
|
||||||
|
inner join users u on u.id = rev.user_id
|
||||||
|
inner join candidates c on c.id = rev.candidate_id
|
||||||
|
left join (
|
||||||
|
select candidate_id
|
||||||
|
from reviewer_votes
|
||||||
|
where veto > 0 or veto is null
|
||||||
|
) as vetos on vetos.candidate_id = rev.candidate_id
|
||||||
|
left join (
|
||||||
|
select candidate_id
|
||||||
|
, count(vote) voters
|
||||||
|
, sum(case when vote != 0 then 1 else 0 end) vote_count
|
||||||
|
from reviewer_votes
|
||||||
|
left join users on users.id = reviewer_votes.user_id
|
||||||
|
where users.role != 'manager'
|
||||||
|
group by candidate_id
|
||||||
|
) as full_votes on full_votes.candidate_id = rev.candidate_id
|
||||||
|
where vetos.candidate_id is null
|
||||||
|
and full_votes.voters = full_votes.vote_count
|
||||||
|
and rev.candidate_id = #{candidate_id};"
|
||||||
|
result = ActiveRecord::Base.connection.exec_query(sql)
|
||||||
|
result.count == 1
|
||||||
|
end # rubocop:enable Metrics/MethodLength
|
||||||
end
|
end
|
||||||
|
@ -5,6 +5,7 @@ class User < ApplicationRecord
|
|||||||
has_many :reviewer_to_quizzes
|
has_many :reviewer_to_quizzes
|
||||||
has_many :quizzes, through: :reviewer_to_quizzes
|
has_many :quizzes, through: :reviewer_to_quizzes
|
||||||
has_many :votes, class_name: 'ReviewerVote'
|
has_many :votes, class_name: 'ReviewerVote'
|
||||||
|
has_many :quiz_comments
|
||||||
|
|
||||||
has_many :reviewees, through: :quizzes, source: :candidates
|
has_many :reviewees, through: :quizzes, source: :candidates
|
||||||
|
|
||||||
@ -18,7 +19,13 @@ class User < ApplicationRecord
|
|||||||
save
|
save
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def commented_on? test_hash
|
||||||
|
quiz_comments.where(test_hash: test_hash).count.positive?
|
||||||
|
end
|
||||||
|
|
||||||
# Voting
|
# Voting
|
||||||
|
# TODO: Refactor this out of User, belongs on ReviewerVote
|
||||||
|
# ie: cast_yea(candidate, user)
|
||||||
def cast_yea_on candidate
|
def cast_yea_on candidate
|
||||||
vote = votes.find_by(candidate_id: candidate.to_i)
|
vote = votes.find_by(candidate_id: candidate.to_i)
|
||||||
vote.vote = :yea
|
vote.vote = :yea
|
||||||
@ -31,20 +38,16 @@ class User < ApplicationRecord
|
|||||||
vote.save
|
vote.save
|
||||||
end
|
end
|
||||||
|
|
||||||
def approve_candidate candidate
|
def review_candidate candidate, parms_hash
|
||||||
candidate = Candidate.find(candidate.to_i)
|
candidate = Candidate.find(candidate.to_i)
|
||||||
|
|
||||||
vote = votes.find_by(candidate_id: candidate.to_i)
|
vote = votes.find_by(candidate_id: candidate.to_i)
|
||||||
vote.veto = :approved
|
vote.veto = parms_hash[:review_status]
|
||||||
candidate.update_attribute(:review_status, :approved) if vote.save
|
if vote.save
|
||||||
end
|
# skipping validations on candidate because that's not the managers responsibility
|
||||||
|
candidate.review_comments = parms_hash[:review_comments]
|
||||||
def decline_candidate candidate
|
candidate.update_attribute(:review_status, parms_hash[:review_status])
|
||||||
candidate = Candidate.find(candidate.to_i)
|
end
|
||||||
|
|
||||||
vote = votes.find_by(candidate_id: candidate.to_i)
|
|
||||||
vote.veto = :rejected
|
|
||||||
candidate.update_attribute(:review_status, :declined) if vote.save
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def my_vote candidate
|
def my_vote candidate
|
||||||
|
22
app/policies/quiz_comment_policy.rb
Normal file
22
app/policies/quiz_comment_policy.rb
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class QuizCommentPolicy < ApplicationPolicy
|
||||||
|
# Quiz Comment Policy
|
||||||
|
#
|
||||||
|
# Anyone who can vote on results, can comment
|
||||||
|
# Only comment owner can edit her comment
|
||||||
|
|
||||||
|
def new?
|
||||||
|
user.acts_as_reviewer?
|
||||||
|
end
|
||||||
|
|
||||||
|
def create?
|
||||||
|
return true if user.acts_as_admin?
|
||||||
|
|
||||||
|
user.acts_as_reviewer? &&
|
||||||
|
record.candidate.reviewers.where(id: user.id).count.positive?
|
||||||
|
end
|
||||||
|
|
||||||
|
def update?
|
||||||
|
user.acts_as_reviewer? && user.id == record.user_id
|
||||||
|
end
|
||||||
|
end
|
@ -2,30 +2,26 @@
|
|||||||
class ReviewerVotePolicy < ApplicationPolicy
|
class ReviewerVotePolicy < ApplicationPolicy
|
||||||
# Voting Policy
|
# Voting Policy
|
||||||
#
|
#
|
||||||
# Only Reviewers, Managers, and Admins, can cast a vote on a quiz result
|
# Only Reviewers and Managers can cast a vote on a quiz result
|
||||||
#
|
#
|
||||||
# Reviewers can vote any quiz they are linked to
|
# Reviewers can vote any quiz they are linked to
|
||||||
# Only Managers, and Admins, can veto a quiz result
|
# Only Managers, and Admins, can veto a quiz result
|
||||||
|
|
||||||
def up?
|
def up?
|
||||||
return true if user.acts_as_admin?
|
return false unless user.commented_on?(record.candidate.test_hash)
|
||||||
return false unless record.candidate.reviewers.include? user
|
return false unless record.candidate.reviewers.include? user
|
||||||
|
return false if user.admin?
|
||||||
user.acts_as_reviewer?
|
user.acts_as_reviewer?
|
||||||
end
|
end
|
||||||
|
|
||||||
def down?
|
def down?
|
||||||
return true if user.acts_as_admin?
|
return false unless user.commented_on?(record.candidate.test_hash)
|
||||||
return false unless record.candidate.reviewers.include? user
|
return false unless record.candidate.reviewers.include? user
|
||||||
|
return false if user.admin?
|
||||||
user.acts_as_reviewer?
|
user.acts_as_reviewer?
|
||||||
end
|
end
|
||||||
|
|
||||||
def approve?
|
def interview_request?
|
||||||
return true if user.acts_as_admin?
|
|
||||||
return false unless record.candidate.reviewers.include? user
|
|
||||||
user.acts_as_manager?
|
|
||||||
end
|
|
||||||
|
|
||||||
def decline?
|
|
||||||
return true if user.acts_as_admin?
|
return true if user.acts_as_admin?
|
||||||
return false unless record.candidate.reviewers.include? user
|
return false unless record.candidate.reviewers.include? user
|
||||||
user.acts_as_manager?
|
user.acts_as_manager?
|
||||||
|
@ -16,6 +16,24 @@
|
|||||||
<%= form.select :experience, experience_options(candidate.experience), include_blank: false %>
|
<%= form.select :experience, experience_options(candidate.experience), include_blank: false %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<%= form.label :project, "Client or project" %>
|
||||||
|
<%= form.text_field :project %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<%= form.radio_button :position, 'full-time' %>
|
||||||
|
<%= form.label "position_full-time", "Full-time" %>
|
||||||
|
|
||||||
|
<%= form.radio_button :position, 'contract' %>
|
||||||
|
<%= form.label :position_contract, "Contract" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<%= form.label :skill_needs, "Specific skill needs" %>
|
||||||
|
<%= form.text_field :skill_needs %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<%= form.label :quiz_id, "Quiz" %>
|
<%= form.label :quiz_id, "Quiz" %>
|
||||||
<%= form.select :quiz_id, quiz_options(quizzes, candidate.quiz_id), include_blank: (quizzes.size > 1) %>
|
<%= form.select :quiz_id, quiz_options(quizzes, candidate.quiz_id), include_blank: (quizzes.size > 1) %>
|
||||||
|
@ -9,14 +9,14 @@
|
|||||||
|
|
||||||
<table cellspacing="0" cellpadding="0">
|
<table cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Candidate</th>
|
<th><%= sortable "name", "Candidate" %></th>
|
||||||
<th>Test ID</th>
|
<th><%= sortable "test_hash", "Test ID" %></th>
|
||||||
<th>Email</th>
|
<th><%= sortable "email" %></th>
|
||||||
<th>Experience</th>
|
<th><%= sortable "experience" %></th>
|
||||||
<th>Progress</th>
|
<th>Progress</th>
|
||||||
<th>Completed</th>
|
<th><%= sortable "completed_at", "Completed" %></th>
|
||||||
<th>Reminded</th>
|
<th><%= sortable "reminded" %></th>
|
||||||
<th>Interview Request</th>
|
<th>Interview?</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<% @candidates.each do |candidate| %>
|
<% @candidates.each do |candidate| %>
|
||||||
@ -32,8 +32,9 @@
|
|||||||
<td><%= candidate.status %></td>
|
<td><%= candidate.status %></td>
|
||||||
<td><%= candidate.completed ? link_to("Submitted", admin_result_path(candidate.test_hash)) : "" %></td>
|
<td><%= candidate.completed ? link_to("Submitted", admin_result_path(candidate.test_hash)) : "" %></td>
|
||||||
<td><%= candidate.reminded ? "Yes" : "" %></td>
|
<td><%= candidate.reminded ? "Yes" : "" %></td>
|
||||||
<td><%= candidate.review_status unless candidate.pending? %></td>
|
<td><%= candidate.interview? %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</table>
|
</table>
|
||||||
|
<%= paginate @candidates %>
|
||||||
</main>
|
</main>
|
||||||
|
1
app/views/admin/candidate/resend_welcome.json.erb
Normal file
1
app/views/admin/candidate/resend_welcome.json.erb
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ "message" : "Email queued!" }
|
23
app/views/admin/result/_comment.html.erb
Normal file
23
app/views/admin/result/_comment.html.erb
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<div class="comment-message">
|
||||||
|
<%= comment.message %>
|
||||||
|
|
||||||
|
<% if policy(comment).update? %>
|
||||||
|
<label class="comment-edit-btn" for="comment-<%= comment.id %>">edit</label>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if comment.edits? %>
|
||||||
|
<div class="comment-edit-stamp">Updated <%= time_ago_in_words(comment.updated_at) %> ago</div>
|
||||||
|
<% else %>
|
||||||
|
<div class="comment-edit-stamp"><%= time_ago_in_words(comment.created_at) %> ago</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="comment-author"><%= comment.user.name %></div>
|
||||||
|
|
||||||
|
|
||||||
|
<% if policy(comment).update? %>
|
||||||
|
<input type="checkbox" id="comment-<%= comment.id %>">
|
||||||
|
<div class="comment-edit-form">
|
||||||
|
<%= render partial: 'comment_form', locals: {comment: comment, test_hash: comment.test_hash } %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
23
app/views/admin/result/_comment_form.html.erb
Normal file
23
app/views/admin/result/_comment_form.html.erb
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<% if comment.id.nil? %>
|
||||||
|
|
||||||
|
<%= form_for comment, url: admin_create_comment_path(test_hash: test_hash), method: :post do |form| %>
|
||||||
|
<div class="form-group">
|
||||||
|
<%= form.label :message, "New Comment" %>
|
||||||
|
<%= form.text_area :message %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= submit_tag "Save Comment" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% else %>
|
||||||
|
|
||||||
|
<%= form_for comment, url: admin_update_comment_path(test_hash: test_hash, id: comment.id), method: :post do |form| %>
|
||||||
|
<div class="form-group">
|
||||||
|
<%= form.label :message, "Update Comment" %>
|
||||||
|
<%= form.text_area :message %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= submit_tag "Update" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% end %>
|
@ -1,34 +1,54 @@
|
|||||||
<% # TODO: This needs to be extracted into a decorator, or something. It is only a quick hack solution. %>
|
<% # TODO: This should be extracted into a decorator, or something. It is only a quick hack solution. %>
|
||||||
|
|
||||||
<% if current_user.acts_as_reviewer? %>
|
<% if current_user.acts_as_reviewer? %>
|
||||||
<div class="review_meta__votes" data-id="vote-count">
|
<div class="review_meta__votes" data-id="vote-count">
|
||||||
<strong>Votes: </strong>
|
<strong>Votes: </strong>
|
||||||
<%= link_to admin_up_vote_path(test_hash: @candidate.test_hash), remote: true do %>
|
|
||||||
Yea (<span data-id="up-votes"><%= @candidate.votes.yea.count %></span>)
|
<% if @candidate.pending? && current_user.commented_on?(@candidate.test_hash) %>
|
||||||
<% end %>
|
<%= link_to admin_up_vote_path(test_hash: @candidate.test_hash), remote: true do %>
|
||||||
<%= link_to admin_down_vote_path(test_hash: @candidate.test_hash), remote: true do %>
|
Yea (<span data-id="up-votes"><%= @candidate.votes.yea.count %></span>)
|
||||||
Nay (<span data-id="down-votes"><%= @candidate.votes.nay.count %></span>)
|
<% end %>
|
||||||
|
|
||||||
|
<%= link_to admin_down_vote_path(test_hash: @candidate.test_hash), remote: true do %>
|
||||||
|
Nay (<span data-id="down-votes"><%= @candidate.votes.nay.count %></span>)
|
||||||
|
<% end %>
|
||||||
|
<% elsif @candidate.pending? %>
|
||||||
|
<div>You must comment before you can vote</div>
|
||||||
|
<span>Yea (<span data-id="up-votes"><%= @candidate.votes.yea.count %></span>)</span>
|
||||||
|
<span>Nay (<span data-id="down-votes"><%= @candidate.votes.nay.count %></span>)</span>
|
||||||
|
<% else %>
|
||||||
|
Voting closed -
|
||||||
|
Yea (<%= @candidate.votes.yea.count %>) -
|
||||||
|
Nay (<%= @candidate.votes.nay.count %>)
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<small>(Your vote: <span data-id="my-vote"><%= current_user.my_vote(@candidate) %></span>)</small>
|
<small>(Your vote: <span data-id="my-vote"><%= current_user.my_vote(@candidate) %></span>)</small>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if current_user.acts_as_manager? %>
|
<% if current_user.acts_as_manager? %>
|
||||||
<div class="review_meta__vetos" data-id="veto-status">
|
<div class="review_meta__vetos">
|
||||||
<strong>Manager Vetos: </strong>
|
<%= form_tag admin_interview_path(test_hash: @candidate.test_hash) do %>
|
||||||
<%= link_to admin_approve_vote_path(test_hash: @candidate.test_hash), remote: true do %>
|
<strong>Interview: </strong>
|
||||||
<span data-id="interview-request">
|
|
||||||
<%= @candidate.approved? ? "Requested" : "Request Interview" %>
|
<%= radio_button_tag :review_status, :approved, checked = @candidate.approved? %>
|
||||||
</span>
|
<%= label_tag :review_status_approved, "Yes" %>
|
||||||
<% end %>
|
|
||||||
<%= link_to admin_decline_vote_path(test_hash: @candidate.test_hash), remote: true do %>
|
<%= radio_button_tag :review_status, :declined, checked = @candidate.declined? %>
|
||||||
<span data-id="interview-decline">
|
<%= label_tag :review_status_declined, "No" %>
|
||||||
<%= @candidate.declined? ? "Declined" : "Decline Interview" %>
|
|
||||||
</span>
|
<div class="review-status-comments">
|
||||||
|
<span>Review comments for recruiter</span>
|
||||||
|
<%= text_area_tag :review_comments, @candidate.review_comments %>
|
||||||
|
<%= submit_tag 'Send Request' %>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% else %>
|
<% else %>
|
||||||
|
|
||||||
<strong>Candidate Interview Status: </strong><%= @candidate.review_status %>
|
<strong>Candidate Interview Status: </strong><%= @candidate.review_status %>
|
||||||
|
<% unless @candidate.review_status.blank? %>
|
||||||
|
<div>Review Status Comments:</div>
|
||||||
|
<div><%= @candidate.review_comments %></div>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -5,19 +5,24 @@
|
|||||||
<main class="summary_tpl">
|
<main class="summary_tpl">
|
||||||
<table cellspacing="0" cellpadding="0">
|
<table cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Test ID</th>
|
<th><%= sortable "test_hash", "Test ID" %></th>
|
||||||
<th>Experience</th>
|
<th><%= sortable "name" %></th>
|
||||||
|
<th><%= sortable "project", "Client/Project" %></th>
|
||||||
<th>Recruiter</th>
|
<th>Recruiter</th>
|
||||||
<th>Interview Request</th>
|
<th><%= sortable "completed_at", "Submitted on" %></th>
|
||||||
|
<th>Interview?</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<% @candidates.each do |candidate| %>
|
<% @candidates.each do |candidate| %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= link_to candidate.test_hash, admin_result_path(candidate.test_hash) %></td>
|
<td><%= link_to candidate.test_hash, admin_result_path(candidate.test_hash) %></td>
|
||||||
<td><%= candidate.experience %> years</td>
|
<td><%= candidate.name if !candidate.pending? %></td>
|
||||||
|
<td><%= candidate.project %></td>
|
||||||
<td><%= mail_to(candidate.recruiter.email) %></td>
|
<td><%= mail_to(candidate.recruiter.email) %></td>
|
||||||
<td><%= candidate.review_status unless candidate.pending? %></td>
|
<td><%= candidate.completed_at.strftime('%D') unless candidate.completed_at.nil? %></td>
|
||||||
|
<td><%= candidate.interview? %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</table>
|
</table>
|
||||||
|
<%= paginate @candidates %>
|
||||||
</main>
|
</main>
|
||||||
|
@ -2,41 +2,65 @@
|
|||||||
content_for :title, "Quiz Review - Skills Assessment Admin"
|
content_for :title, "Quiz Review - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<main class="summary_tpl admin-review">
|
<div class="summary_tpl">
|
||||||
<h2 class="prft-heading">Quiz Review</h2>
|
<div class="admin-review">
|
||||||
|
<h2 class="prft-heading">Quiz Review</h2>
|
||||||
|
|
||||||
<div class="review_meta">
|
<div class="review_meta">
|
||||||
<div>
|
<div>
|
||||||
<strong>Test ID:</strong> <%= @candidate.test_hash %><br />
|
<% unless @candidate.pending? %>
|
||||||
<strong>Years of Experience:</strong> <%= @candidate.experience %><br />
|
<strong>Name:</strong> <%= @candidate.name %><br />
|
||||||
<strong>Recruiter Email:</strong> <%= mail_to @candidate.recruiter.name, @candidate.recruiter.email %><br />
|
<% end %>
|
||||||
|
<strong>Test ID:</strong> <%= @candidate.test_hash %><br />
|
||||||
|
<strong>Years of Experience:</strong> <%= @candidate.experience %><br />
|
||||||
|
<strong>Client/Project:</strong> <%= @candidate.project %><br />
|
||||||
|
<strong>Position Type:</strong> <%= @candidate.position %><br />
|
||||||
|
<strong>Skill Needs:</strong> <%= @candidate.skill_needs %><br />
|
||||||
|
<strong>Recruiter Email:</strong> <%= mail_to @candidate.recruiter.name, @candidate.recruiter.email %><br />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div><%= render partial: 'voting' %></div>
|
<% @quiz.each do |question| %>
|
||||||
|
<%= form_for(:answer, url: '#never-post', html:{id: 'summary-form'}) do |form| %>
|
||||||
|
<article class="answer-sec <%= question.input_type %>-type" data-qid="<%= question.question_id %>">
|
||||||
|
<div class="question-heading">
|
||||||
|
<div class="question-title">
|
||||||
|
<h3><%= question.question %></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="answer-container">
|
||||||
|
<% if question.attachment.present? %>
|
||||||
|
<%= image_tag question.attachment %>
|
||||||
|
<% end %>
|
||||||
|
<fieldset disabled class="answer-block">
|
||||||
|
<%= render partial: "quiz/#{question.input_type}", locals: {question: question, answer: question.answer, form: form} %>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<% end #form_tag %>
|
||||||
|
<% end #questions loop %>
|
||||||
|
|
||||||
|
<%= link_to(admin_results_path, { class: 'secondary-btn' }) do %>
|
||||||
|
<button>Back to list</button>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% @quiz.each do |question| %>
|
<div class="review-comments">
|
||||||
<%= form_for(:answer, url: '#never-post', html:{id: 'summary-form'}) do |form| %>
|
<div>
|
||||||
<article class="answer-sec <%= question.input_type %>-type" data-qid="<%= question.question_id %>">
|
<h2 class="prft-heading">Voting</h2>
|
||||||
<div class="question-heading">
|
<div class="review_meta">
|
||||||
<div class="question-title">
|
<div><%= render partial: 'voting' %></div>
|
||||||
<h3><%= question.question %></h3>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="answer-container">
|
<div>
|
||||||
<% if question.attachment.present? %>
|
<h2 id="comment-header" class="prft-heading">Comments</h2>
|
||||||
<%= image_tag question.attachment %>
|
<% if policy(QuizComment).new? %>
|
||||||
<% end %>
|
<%= render partial: 'comment_form', locals: {comment: @comment, test_hash: @candidate.test_hash } %>
|
||||||
<fieldset disabled class="answer-block">
|
<% end %>
|
||||||
<%= render partial: "quiz/#{question.input_type}", locals: {question: question, answer: question.answer, form: form} %>
|
<%= render partial: 'comment', collection: @comments, locals: { test_hash: @candidate.test_hash } %>
|
||||||
</fieldset>
|
<a href="#comment-header">Back to top</a>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</div>
|
||||||
<% end #form_tag %>
|
</div>
|
||||||
<% end #questions loop %>
|
|
||||||
|
|
||||||
<%= link_to(admin_results_path, { class: 'secondary-btn' }) do %>
|
|
||||||
<button>Back to list</button>
|
|
||||||
<% end %>
|
|
||||||
</main>
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<table cellspacing="0" cellpadding="0">
|
<table cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<th>User</th>
|
<th><%= sortable "name", "User" %></th>
|
||||||
<th>Email</th>
|
<th><%= sortable "email" %></th>
|
||||||
<th>Role</th>
|
<th><%= sortable "role" %></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
6
app/views/admin/vote/down.json.erb
Normal file
6
app/views/admin/vote/down.json.erb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"message" : "Vote Counted",
|
||||||
|
"upCount" : <%= @candidate.votes.yea.count %>,
|
||||||
|
"downCount" : <%= @candidate.votes.nay.count %>,
|
||||||
|
"myVote" : "nay"
|
||||||
|
}
|
6
app/views/admin/vote/up.json.erb
Normal file
6
app/views/admin/vote/up.json.erb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"message" : "Vote Counted",
|
||||||
|
"upCount" : <%= @candidate.votes.yea.count %>,
|
||||||
|
"downCount" : <%= @candidate.votes.nay.count %>,
|
||||||
|
"myVote" : "yea"
|
||||||
|
}
|
@ -23,7 +23,7 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="email-copyright">
|
<td class="email-copyright">
|
||||||
©2016 All Rights Reserved - Perficient Digital
|
©2016-<%= Time.now.year %> All Rights Reserved - Perficient Digital
|
||||||
</td>
|
</td>
|
||||||
<td class="email-logo">
|
<td class="email-logo">
|
||||||
<%= image_tag(attachments["perficientdigital-logo.jpg"].url, alt:"Perficient Digital") %>
|
<%= image_tag(attachments["perficientdigital-logo.jpg"].url, alt:"Perficient Digital") %>
|
||||||
|
@ -7,8 +7,9 @@
|
|||||||
<strong>Candidate email:</strong> <%= @candidate.email %><br />
|
<strong>Candidate email:</strong> <%= @candidate.email %><br />
|
||||||
<strong>Candidate ID:</strong> <%= @candidate.test_hash %><br />
|
<strong>Candidate ID:</strong> <%= @candidate.test_hash %><br />
|
||||||
<strong>Years of experience:</strong> <%= @candidate.experience %> Years<br />
|
<strong>Years of experience:</strong> <%= @candidate.experience %> Years<br />
|
||||||
|
<strong>Client/Project:</strong> <%= @candidate.project %><br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>You will be notified when the candidate has finished taking the test.</p>
|
<p>You will be notified when the candidate has finished taking the test.</p>
|
||||||
</columns>
|
</columns>
|
||||||
</row>
|
</row>
|
||||||
|
@ -6,5 +6,6 @@ Candidate name: <%= @candidate.name %>
|
|||||||
Candidate email: <%= @candidate.email %>
|
Candidate email: <%= @candidate.email %>
|
||||||
Candidate ID: <%= @candidate.test_hash %>
|
Candidate ID: <%= @candidate.test_hash %>
|
||||||
Years of experience: <%= @candidate.experience %> Years
|
Years of experience: <%= @candidate.experience %> Years
|
||||||
|
Client/Project: <%= @candidate.project %>
|
||||||
|
|
||||||
You will be notified when the candidate has finished taking the test.
|
You will be notified when the candidate has finished taking the test.
|
||||||
|
9
app/views/recruiter_mailer/candidate_reviewed.html.inky
Normal file
9
app/views/recruiter_mailer/candidate_reviewed.html.inky
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<row>
|
||||||
|
<columns class="email-body">
|
||||||
|
<p>The team has <%= @candidate.review_status %> an interview with <strong><%= @candidate.name %></strong> with the following comments:</p>
|
||||||
|
|
||||||
|
<p><%= @candidate.review_comments %></p>
|
||||||
|
|
||||||
|
<p>Thank you</p>
|
||||||
|
</columns>
|
||||||
|
</row>
|
7
app/views/recruiter_mailer/candidate_reviewed.text.erb
Normal file
7
app/views/recruiter_mailer/candidate_reviewed.text.erb
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
PERFICIENT/digital - Skills Assessment Test
|
||||||
|
|
||||||
|
The team has <%= @candidate.review_status %> an interview with <%= @candidate.name %> with the following comments:
|
||||||
|
|
||||||
|
<%= @candidate.review_comments %>
|
||||||
|
|
||||||
|
Thank you.
|
@ -1,6 +1,6 @@
|
|||||||
<row>
|
<row>
|
||||||
<columns class="email-body">
|
<columns class="email-body">
|
||||||
<p><strong><%= @candidate.name %></strong> has completed the Skills Assessment Test.</p>
|
<p><strong><%= @candidate.name %></strong> has completed the Skills Assessment Test.</p>
|
||||||
<p><strong>Martin Ridgway</strong> will let you know if we would like to interview this candidate.</p>
|
<p><strong><%= @candidate.manager.name %></strong> will let you know if we would like to interview this candidate.</p>
|
||||||
</columns>
|
</columns>
|
||||||
</row>
|
</row>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
PERFICIENT/digital - Skills Assessment Test
|
PERFICIENT/digital - Skills Assessment Test
|
||||||
|
|
||||||
<%= @candidate.name %> has completed the Skills Assessment Test.
|
<%= @candidate.name %> has completed the Skills Assessment Test.
|
||||||
Martin Ridgway will let you know if we would like to interview this candidate.
|
<%= @candidate.manager.name %> will let you know if we would like to interview this candidate.
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<row>
|
<row>
|
||||||
<columns class="email-body">
|
<columns class="email-body">
|
||||||
<p>Candidate <strong><%= @candidate.test_hash %></strong> has completed the Skills Assessment Test.</p>
|
<p>
|
||||||
|
Candidate <strong><%= @candidate.test_hash %></strong> has completed the
|
||||||
|
Skills Assessment Test for client/project <%= @candidate.project %>.
|
||||||
|
</p>
|
||||||
<p>You can view the results here: <%= link_to nil, admin_result_url(@candidate.test_hash) %>.</p>
|
<p>You can view the results here: <%= link_to nil, admin_result_url(@candidate.test_hash) %>.</p>
|
||||||
</columns>
|
</columns>
|
||||||
</row>
|
</row>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
PERFICIENT/digital SKILLS ASSESSMENT RESULTS
|
PERFICIENT/digital SKILLS ASSESSMENT RESULTS
|
||||||
|
|
||||||
Candidate <%= @candidate.test_hash %> has completed the Skills Assessment Test.
|
Candidate <%= @candidate.test_hash %> has completed the Skills Assessment Test for client/project <%= @candidate.project %>.
|
||||||
|
|
||||||
You can view the results here: <%= admin_result_url(@candidate.test_hash) %>.
|
You can view the results here: <%= admin_result_url(@candidate.test_hash) %>.
|
||||||
|
13
app/views/reviewer_mailer/new_comment.html.inky
Normal file
13
app/views/reviewer_mailer/new_comment.html.inky
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<row>
|
||||||
|
<columns class="email-body">
|
||||||
|
<p>
|
||||||
|
<%= @comment.user.name %> wrote a comment for quiz <%= @comment.test_hash %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style="border-top: 1px solid; border-bottom: 1px solid;">
|
||||||
|
<%= @comment.message %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>You can view and reply here: <%= link_to nil, admin_result_url(@comment.test_hash) %>.</p>
|
||||||
|
</columns>
|
||||||
|
</row>
|
9
app/views/reviewer_mailer/new_comment.text.erb
Normal file
9
app/views/reviewer_mailer/new_comment.text.erb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
SKILLS ASSESSMENT RESULT COMMENT
|
||||||
|
|
||||||
|
<%= @comment.user.name %> wrote a comment for quiz <%= @comment.test_hash %>
|
||||||
|
|
||||||
|
--- --- --- ---
|
||||||
|
<%= @comment.message %>
|
||||||
|
--- --- --- ---
|
||||||
|
|
||||||
|
You can view and reply here: <%= admin_result_url(@comment.test_hash) %>.
|
10
app/views/reviewer_mailer/notify_manager.html.inky
Normal file
10
app/views/reviewer_mailer/notify_manager.html.inky
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<row>
|
||||||
|
<columns class="email-body">
|
||||||
|
<p>Hello <%= @manager.name %>,</p>
|
||||||
|
<p>
|
||||||
|
Everyone has voted and you need to request or decline an interview for
|
||||||
|
<%= link_to nil, admin_result_url(@candidate.test_hash) %>
|
||||||
|
</p>
|
||||||
|
</columns>
|
||||||
|
</row>
|
||||||
|
|
6
app/views/reviewer_mailer/notify_manager.text.erb
Normal file
6
app/views/reviewer_mailer/notify_manager.text.erb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
PERFICIENT/digital SKILLS ASSESSMENT RESULTS
|
||||||
|
|
||||||
|
Hello <%= @manager.name %>,
|
||||||
|
|
||||||
|
Everyone has voted and you need to request or decline an interview for
|
||||||
|
<%= admin_result_url(@candidate.test_hash) %>
|
9
app/views/reviewer_mailer/reminder.html.inky
Normal file
9
app/views/reviewer_mailer/reminder.html.inky
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<row>
|
||||||
|
<columns class="email-body">
|
||||||
|
<p>Hello <%= @reminder.name %>,</p>
|
||||||
|
<p>
|
||||||
|
Please review and vote on the results for <%= link_to nil, admin_result_url(@reminder.test_hash) %>
|
||||||
|
</p>
|
||||||
|
</columns>
|
||||||
|
</row>
|
||||||
|
|
5
app/views/reviewer_mailer/reminder.text.erb
Normal file
5
app/views/reviewer_mailer/reminder.text.erb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
PERFICIENT/digital SKILLS ASSESSMENT RESULTS
|
||||||
|
|
||||||
|
Hello <%= @reminder.name %>,
|
||||||
|
|
||||||
|
Please review and vote on the results for <%= admin_result_url(@reminder.test_hash) %>.
|
@ -1,5 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Reminder
|
class CandidateReminder
|
||||||
def initialize
|
def initialize
|
||||||
@collection = reminder_collection
|
@collection = reminder_collection
|
||||||
end
|
end
|
33
app/workers/reviewer_reminder.rb
Normal file
33
app/workers/reviewer_reminder.rb
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class ReviewerReminder
|
||||||
|
def initialize
|
||||||
|
@collection = reminder_collection
|
||||||
|
end
|
||||||
|
|
||||||
|
def count
|
||||||
|
@collection.count
|
||||||
|
end
|
||||||
|
alias size count
|
||||||
|
|
||||||
|
def reminders
|
||||||
|
@reminders ||= @collection.to_hash.map { |r| OpenStruct.new(r) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_all
|
||||||
|
reminders.each do |reminder|
|
||||||
|
ReviewerMailer.reminder(reminder).deliver_now
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def reminder_collection
|
||||||
|
sql = "select u.name, u.email, c.test_hash, c.project
|
||||||
|
from reviewer_votes rev
|
||||||
|
inner join users u on u.id = rev.user_id
|
||||||
|
inner join candidates c on c.id = rev.candidate_id
|
||||||
|
where rev.vote = 0 and rev.veto = 0
|
||||||
|
and u.role != 'manager' and u.active is not false;"
|
||||||
|
ActiveRecord::Base.connection.exec_query(sql)
|
||||||
|
end
|
||||||
|
end
|
@ -30,5 +30,8 @@ module SkillAssessmentApp
|
|||||||
g.helper false
|
g.helper false
|
||||||
g.routes false
|
g.routes false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# let's use lib/utils for dev only utility classes & helpers
|
||||||
|
config.autoload_paths << "#{Rails.root}/lib/utils"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,10 +1,81 @@
|
|||||||
{
|
{
|
||||||
"ignored_warnings": [
|
"ignored_warnings": [
|
||||||
{
|
{
|
||||||
|
"warning_type": "SQL Injection",
|
||||||
|
"warning_code": 0,
|
||||||
|
"fingerprint": "6f3216446dca0fa79e96267eb0323d50cc59e7bc1e1529fd160cd5beb185e2f2",
|
||||||
|
"message": "Possible SQL injection",
|
||||||
|
"file": "app/controllers/admin/candidate_controller.rb",
|
||||||
|
"line": 7,
|
||||||
|
"link": "http://brakemanscanner.org/docs/warning_types/sql_injection/",
|
||||||
|
"code": "Candidate.order(\"#{sort_column} #{sort_direction}\")",
|
||||||
|
"render_path": null,
|
||||||
|
"location": {
|
||||||
|
"type": "method",
|
||||||
|
"class": "Admin::CandidateController",
|
||||||
|
"method": "index"
|
||||||
|
},
|
||||||
|
"user_input": "sort_column",
|
||||||
|
"confidence": "Medium",
|
||||||
|
"note": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"warning_type": "SQL Injection",
|
||||||
|
"warning_code": 0,
|
||||||
|
"fingerprint": "9e802ac2067c1ee551f97c1f37816b585451abc42b6a2c7903c6d97aa042da61",
|
||||||
|
"message": "Possible SQL injection",
|
||||||
|
"file": "app/controllers/admin/user_controller.rb",
|
||||||
|
"line": 5,
|
||||||
|
"link": "http://brakemanscanner.org/docs/warning_types/sql_injection/",
|
||||||
|
"code": "User.order(\"#{sort_column} #{sort_direction}\")",
|
||||||
|
"render_path": null,
|
||||||
|
"location": {
|
||||||
|
"type": "method",
|
||||||
|
"class": "Admin::UserController",
|
||||||
|
"method": "index"
|
||||||
|
},
|
||||||
|
"user_input": "sort_column",
|
||||||
|
"confidence": "Medium",
|
||||||
|
"note": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"warning_type": "SQL Injection",
|
||||||
|
"warning_code": 0,
|
||||||
|
"fingerprint": "a65b53d63f1d43cb831947d0693d2d1b8819e21aec5ee18bf09b577ade02a0ee",
|
||||||
|
"message": "Possible SQL injection",
|
||||||
|
"file": "app/controllers/admin/result_controller.rb",
|
||||||
|
"line": 16,
|
||||||
|
"link": "http://brakemanscanner.org/docs/warning_types/sql_injection/",
|
||||||
|
"code": "Candidate.where(:completed => true).includes(:recruiter).order(\"#{(\"(case when review_status = 0 then '' else name end)\" or sort_column)} #{sort_direction}\")",
|
||||||
|
"render_path": null,
|
||||||
|
"location": {
|
||||||
|
"type": "method",
|
||||||
|
"class": "Admin::ResultController",
|
||||||
|
"method": "index"
|
||||||
|
},
|
||||||
|
"user_input": "sort_column",
|
||||||
|
"confidence": "Medium",
|
||||||
|
"note": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"warning_type": "Dynamic Render Path",
|
||||||
|
"warning_code": 15,
|
||||||
"fingerprint": "da17225c940987e6239cc4ecfe27bcb1e5da2db1134435dc3e1025d97927e0ba",
|
"fingerprint": "da17225c940987e6239cc4ecfe27bcb1e5da2db1134435dc3e1025d97927e0ba",
|
||||||
|
"message": "Render path contains parameter value",
|
||||||
|
"file": "app/views/admin/question/options.html.erb",
|
||||||
|
"line": 3,
|
||||||
|
"link": "http://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
|
||||||
|
"code": "render(partial => \"admin/question/#{params[:input_type]}\", { :locals => ({ :question => ((Question.find(params[:question_id]) or Question.new)) }) })",
|
||||||
|
"render_path": [{"type":"controller","class":"Admin::QuestionController","method":"options","line":58,"file":"app/controllers/admin/question_controller.rb"}],
|
||||||
|
"location": {
|
||||||
|
"type": "template",
|
||||||
|
"template": "admin/question/options"
|
||||||
|
},
|
||||||
|
"user_input": "params[:input_type]",
|
||||||
|
"confidence": "Medium",
|
||||||
"note": "false positive"
|
"note": "false positive"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"updated": "2016-09-19 09:06:25 -0500",
|
"updated": "2017-03-06 12:16:23 -0600",
|
||||||
"brakeman_version": "3.4.0"
|
"brakeman_version": "3.4.1"
|
||||||
}
|
}
|
||||||
|
12
config/initializers/kaminari_config.rb
Normal file
12
config/initializers/kaminari_config.rb
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
Kaminari.configure do |config|
|
||||||
|
config.default_per_page = 10
|
||||||
|
# config.max_per_page = nil
|
||||||
|
# config.window = 4
|
||||||
|
# config.outer_window = 0
|
||||||
|
# config.left = 0
|
||||||
|
# config.right = 0
|
||||||
|
# config.page_method_name = :page
|
||||||
|
# config.param_name = :page
|
||||||
|
# config.params_on_first_page = false
|
||||||
|
end
|
@ -1,4 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
# Neat routing visualizer, in irb:
|
||||||
|
# File.open('routing.html', 'w+'){|f| f.write Rails.application.routes.router.visualizer }
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
get "/styleguide", to: "application#styleguide", as: :styleguide
|
get "/styleguide", to: "application#styleguide", as: :styleguide
|
||||||
get "/admin/styleguide", to: "application#styleguide"
|
get "/admin/styleguide", to: "application#styleguide"
|
||||||
@ -45,15 +47,17 @@ Rails.application.routes.draw do
|
|||||||
post "/admin/candidate/new", to: "admin/candidate#create", as: :admin_create_candidate
|
post "/admin/candidate/new", to: "admin/candidate#create", as: :admin_create_candidate
|
||||||
get "/admin/candidate/:id", to: "admin/candidate#edit", as: :admin_edit_candidate
|
get "/admin/candidate/:id", to: "admin/candidate#edit", as: :admin_edit_candidate
|
||||||
post "/admin/candidate/:id", to: "admin/candidate#update", as: :admin_update_candidate
|
post "/admin/candidate/:id", to: "admin/candidate#update", as: :admin_update_candidate
|
||||||
get "/admin/candidate/:id/resend", to: "admin/candidate#resend_welcome", as: :admin_resend_welcome
|
get "/admin/candidate/:id/resend", to: "admin/candidate#resend_welcome", as: :admin_resend_welcome, defaults: { format: 'json' }
|
||||||
|
|
||||||
get "/admin/results", to: "admin/result#index", as: :admin_results
|
get "/admin/results", to: "admin/result#index", as: :admin_results
|
||||||
get "/admin/result/:test_hash", to: "admin/result#view", as: :admin_result
|
get "/admin/result/:test_hash", to: "admin/result#view", as: :admin_result
|
||||||
|
|
||||||
get "admin/vote/:test_hash/up", to: "admin/vote#up", as: :admin_up_vote
|
post "/admin/comment/:test_hash/:id", to: "admin/comment#update", as: :admin_update_comment
|
||||||
get "admin/vote/:test_hash/down", to: "admin/vote#down", as: :admin_down_vote
|
post "/admin/comment/:test_hash", to: "admin/comment#create", as: :admin_create_comment
|
||||||
get "admin/vote/:test_hash/approve", to: "admin/vote#approve", as: :admin_approve_vote
|
|
||||||
get "admin/vote/:test_hash/decline", to: "admin/vote#decline", as: :admin_decline_vote
|
get "admin/vote/:test_hash/up", to: "admin/vote#up", as: :admin_up_vote, defaults: { format: 'json' }
|
||||||
|
get "admin/vote/:test_hash/down", to: "admin/vote#down", as: :admin_down_vote, defaults: { format: 'json' }
|
||||||
|
post "admin/vote/:test_hash", to: "admin/vote#interview_request", as: :admin_interview
|
||||||
|
|
||||||
get "/admin", to: "admin/dashboard#show", as: :admin
|
get "/admin", to: "admin/dashboard#show", as: :admin
|
||||||
|
|
||||||
|
6
db/migrate/20161204213828_remove_vote_locking.rb
Normal file
6
db/migrate/20161204213828_remove_vote_locking.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class RemoveVoteLocking < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
remove_column :reviewer_votes, :locked, :boolean, default: false, null: false
|
||||||
|
end
|
||||||
|
end
|
12
db/migrate/20161204220527_add_completed_at.rb
Normal file
12
db/migrate/20161204220527_add_completed_at.rb
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class AddCompletedAt < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
add_column :candidates, :completed_at, :datetime, after: :completed
|
||||||
|
|
||||||
|
Candidate.reset_column_information
|
||||||
|
Candidate.where(completed: true).each do |candidate|
|
||||||
|
candidate.completed_at = candidate.updated_at
|
||||||
|
candidate.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
6
db/migrate/20170208212526_add_project_to_client.rb
Normal file
6
db/migrate/20170208212526_add_project_to_client.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class AddProjectToClient < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
add_column :candidates, :project, :string, after: :experience
|
||||||
|
end
|
||||||
|
end
|
12
db/migrate/20170210165110_create_quiz_comments.rb
Normal file
12
db/migrate/20170210165110_create_quiz_comments.rb
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class CreateQuizComments < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
create_table :quiz_comments do |t|
|
||||||
|
t.integer :user_id
|
||||||
|
t.string :test_hash
|
||||||
|
t.text :message
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
7
db/migrate/20170227154554_add_attributes_to_candidate.rb
Normal file
7
db/migrate/20170227154554_add_attributes_to_candidate.rb
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class AddAttributesToCandidate < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
add_column :candidates, :skill_needs, :string, after: :project
|
||||||
|
add_column :candidates, :position, :string, after: :project
|
||||||
|
end
|
||||||
|
end
|
6
db/migrate/20170228161543_add_comments_to_candidate.rb
Normal file
6
db/migrate/20170228161543_add_comments_to_candidate.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class AddCommentsToCandidate < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
add_column :candidates, :review_comments, :text, after: :review_status
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,6 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class RemoveLastRemindedFromReviewerVotes < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
remove_column :reviewer_votes, :last_reminded, :datetime
|
||||||
|
end
|
||||||
|
end
|
31
db/schema.rb
31
db/schema.rb
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20161120175737) do
|
ActiveRecord::Schema.define(version: 20170228161729) do
|
||||||
|
|
||||||
create_table "answers", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
create_table "answers", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
||||||
t.integer "candidate_id"
|
t.integer "candidate_id"
|
||||||
@ -30,13 +30,18 @@ ActiveRecord::Schema.define(version: 20161120175737) do
|
|||||||
t.string "name"
|
t.string "name"
|
||||||
t.string "email"
|
t.string "email"
|
||||||
t.string "experience"
|
t.string "experience"
|
||||||
|
t.string "project"
|
||||||
|
t.string "position"
|
||||||
|
t.string "skill_needs"
|
||||||
t.integer "recruiter_id"
|
t.integer "recruiter_id"
|
||||||
t.boolean "completed"
|
t.boolean "completed"
|
||||||
|
t.datetime "completed_at"
|
||||||
t.boolean "reminded"
|
t.boolean "reminded"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.integer "quiz_id"
|
t.integer "quiz_id"
|
||||||
t.integer "review_status", default: 0, null: false
|
t.integer "review_status", default: 0, null: false
|
||||||
|
t.text "review_comments", limit: 65535
|
||||||
t.index ["quiz_id"], name: "index_candidates_on_quiz_id", using: :btree
|
t.index ["quiz_id"], name: "index_candidates_on_quiz_id", using: :btree
|
||||||
t.index ["recruiter_id"], name: "index_candidates_on_recruiter_id", using: :btree
|
t.index ["recruiter_id"], name: "index_candidates_on_recruiter_id", using: :btree
|
||||||
t.index ["test_hash"], name: "index_candidates_on_test_hash", unique: true, using: :btree
|
t.index ["test_hash"], name: "index_candidates_on_test_hash", unique: true, using: :btree
|
||||||
@ -58,6 +63,14 @@ ActiveRecord::Schema.define(version: 20161120175737) do
|
|||||||
t.index ["sort"], name: "index_questions_on_sort", using: :btree
|
t.index ["sort"], name: "index_questions_on_sort", using: :btree
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "quiz_comments", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
||||||
|
t.integer "user_id"
|
||||||
|
t.string "test_hash"
|
||||||
|
t.text "message", limit: 65535
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
end
|
||||||
|
|
||||||
create_table "quizzes", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
create_table "quizzes", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
||||||
t.string "unit"
|
t.string "unit"
|
||||||
t.string "dept"
|
t.string "dept"
|
||||||
@ -77,12 +90,10 @@ ActiveRecord::Schema.define(version: 20161120175737) do
|
|||||||
create_table "reviewer_votes", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
create_table "reviewer_votes", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
||||||
t.integer "candidate_id"
|
t.integer "candidate_id"
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.integer "vote", default: 0, null: false
|
t.integer "vote", default: 0, null: false
|
||||||
t.integer "veto", default: 0, null: false
|
t.integer "veto", default: 0, null: false
|
||||||
t.datetime "last_reminded"
|
t.datetime "created_at", null: false
|
||||||
t.boolean "locked", default: false, null: false
|
t.datetime "updated_at", null: false
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.datetime "updated_at", null: false
|
|
||||||
t.index ["candidate_id", "user_id"], name: "index_reviewer_votes_on_candidate_id_and_user_id", unique: true, using: :btree
|
t.index ["candidate_id", "user_id"], name: "index_reviewer_votes_on_candidate_id_and_user_id", unique: true, using: :btree
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2,7 +2,13 @@
|
|||||||
namespace :reminders do
|
namespace :reminders do
|
||||||
desc "send reminders to stagnate quizes"
|
desc "send reminders to stagnate quizes"
|
||||||
task send_all: :environment do
|
task send_all: :environment do
|
||||||
reminders = Reminder.new
|
reminders = CandidateReminder.new
|
||||||
|
reminders.send_all
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "send reminders to reviewers"
|
||||||
|
task reviewers: :environment do
|
||||||
|
reminders = ReviewerReminder.new
|
||||||
reminders.send_all
|
reminders.send_all
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
69
lib/utils/fake_quiz.rb
Normal file
69
lib/utils/fake_quiz.rb
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class FakeQuiz
|
||||||
|
def create_completed_quizzes num = 10
|
||||||
|
num.times do
|
||||||
|
candidate = create_candidate Faker::Name.name
|
||||||
|
answer_questions candidate
|
||||||
|
candidate.update_attributes(completed: true, completed_at: Time.zone.now - rand(0..112).days)
|
||||||
|
candidate.build_reviews
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_candidate name
|
||||||
|
Candidate.create(name: name,
|
||||||
|
email: "#{Faker::Internet.user_name(name)}@mailinator.com",
|
||||||
|
experience: rando_experience,
|
||||||
|
project: Faker::Company.name,
|
||||||
|
recruiter_id: recruiter_id,
|
||||||
|
quiz_id: fed_quiz_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def answer_questions candidate
|
||||||
|
candidate.quiz.questions.each do |question|
|
||||||
|
candidate.answers.create(question_id: question.id,
|
||||||
|
answer: generate_answer(question),
|
||||||
|
submitted: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fed_quiz_id
|
||||||
|
Quiz.find_by(dept: 'fed').id
|
||||||
|
end
|
||||||
|
|
||||||
|
def recruiter_id
|
||||||
|
User.find_by(name: 'Sam Recruiter').id
|
||||||
|
end
|
||||||
|
|
||||||
|
def rando_experience
|
||||||
|
%w(0-3 4-6 7-9 10-14 15+)[rand(0..4)]
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_answer question # rubocop:disable Metrics/MethodLength
|
||||||
|
case question.input_type
|
||||||
|
when "checkbox"
|
||||||
|
question.input_options
|
||||||
|
when "checkbox_other"
|
||||||
|
{
|
||||||
|
other: Faker::TwinPeaks.quote,
|
||||||
|
options: question.input_options
|
||||||
|
}
|
||||||
|
when "radio"
|
||||||
|
question.input_options.sample
|
||||||
|
when "radio_other"
|
||||||
|
{
|
||||||
|
other: Faker::TwinPeaks.quote,
|
||||||
|
options: question.input_options.sample
|
||||||
|
}
|
||||||
|
when "live_code"
|
||||||
|
{
|
||||||
|
html: "<p>#{Faker::TwinPeaks.quote}</p>",
|
||||||
|
css: "body {color: #{Faker::Color.hex_color}}",
|
||||||
|
text: Faker::TwinPeaks.quote
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Faker::TwinPeaks.quote
|
||||||
|
end
|
||||||
|
end # rubocop:enable Metrics/MethodLength
|
||||||
|
end
|
@ -26,6 +26,18 @@ module Admin
|
|||||||
assert_redirected_to admin_url
|
assert_redirected_to admin_url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "should auth to original request" do
|
||||||
|
elsie = candidates(:elsie)
|
||||||
|
|
||||||
|
get admin_result_url(test_hash: elsie.test_hash)
|
||||||
|
|
||||||
|
assert_redirected_to admin_login_url
|
||||||
|
post admin_auth_url, params: { auth:
|
||||||
|
{ email: 'fed.reviewer@mailinator.com', password: 'password' } }
|
||||||
|
|
||||||
|
assert_redirected_to admin_result_url(test_hash: elsie.test_hash)
|
||||||
|
end
|
||||||
|
|
||||||
test "should FAIL auth" do
|
test "should FAIL auth" do
|
||||||
post admin_auth_url, params: { auth:
|
post admin_auth_url, params: { auth:
|
||||||
{ email: 'alan.admin@mailinator.com', password: 'b@d9a$$werD' } }
|
{ email: 'alan.admin@mailinator.com', password: 'b@d9a$$werD' } }
|
||||||
|
@ -23,8 +23,14 @@ module Admin
|
|||||||
|
|
||||||
assert_enqueued_jobs 2 do
|
assert_enqueued_jobs 2 do
|
||||||
assert_difference("Candidate.count") do
|
assert_difference("Candidate.count") do
|
||||||
post admin_create_candidate_path, params: { candidate:
|
post admin_create_candidate_path, params: { candidate: {
|
||||||
{ name: 'new name', email: 'test@mailinator.com', experience: '0-3', quiz_id: quizzes(:fed).id } }
|
name: 'new name',
|
||||||
|
email: 'test@mailinator.com',
|
||||||
|
experience: '0-3',
|
||||||
|
position: 'full-time',
|
||||||
|
project: 'client project',
|
||||||
|
quiz_id: quizzes(:fed).id
|
||||||
|
} }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert_redirected_to admin_candidates_path
|
assert_redirected_to admin_candidates_path
|
||||||
|
103
test/controllers/admin/comment_controller_test.rb
Normal file
103
test/controllers/admin/comment_controller_test.rb
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class CommentControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
|
test "should post update" do
|
||||||
|
auth_manager
|
||||||
|
comment = quiz_comments(:com5)
|
||||||
|
post admin_update_comment_url(test_hash: comment.test_hash, id: comment.id),
|
||||||
|
params: { quiz_comment: { message: 'updated comment' } }
|
||||||
|
|
||||||
|
assert_redirected_to admin_result_url(test_hash: comment.test_hash)
|
||||||
|
assert flash[:success]
|
||||||
|
refute_equal comment.message, QuizComment.find_by(id: comment.id).message
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should require message to update" do
|
||||||
|
auth_manager
|
||||||
|
comment = quiz_comments(:com5)
|
||||||
|
post admin_update_comment_url(test_hash: comment.test_hash, id: comment.id),
|
||||||
|
params: { quiz_comment: { message: '' } }
|
||||||
|
|
||||||
|
assert_redirected_to admin_result_url(test_hash: comment.test_hash)
|
||||||
|
assert flash[:error]
|
||||||
|
assert_equal comment.message, QuizComment.find_by(id: comment.id).message
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should post create" do
|
||||||
|
auth_reviewer
|
||||||
|
candidate = candidates(:stacy)
|
||||||
|
|
||||||
|
assert_difference("QuizComment.count") do
|
||||||
|
post admin_create_comment_url(test_hash: candidate.test_hash),
|
||||||
|
params: { quiz_comment: { message: 'this is a test comment' } }
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_redirected_to admin_result_url(test_hash: candidate.test_hash)
|
||||||
|
assert flash[:success]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "admin can post comment" do
|
||||||
|
auth_admin
|
||||||
|
candidate = candidates(:stacy)
|
||||||
|
|
||||||
|
assert_difference("QuizComment.count") do
|
||||||
|
post admin_create_comment_url(test_hash: candidate.test_hash),
|
||||||
|
params: { quiz_comment: { message: 'this is an admin comment' } }
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_redirected_to admin_result_url(test_hash: candidate.test_hash)
|
||||||
|
assert flash[:success]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should queue emails on create" do
|
||||||
|
auth_reviewer
|
||||||
|
candidate = candidates(:stacy)
|
||||||
|
|
||||||
|
assert_enqueued_jobs 1 do
|
||||||
|
assert_difference("QuizComment.count", 1) do
|
||||||
|
post admin_create_comment_url(test_hash: candidate.test_hash),
|
||||||
|
params: { quiz_comment: { message: 'this is a test comment' } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should require comment to create" do
|
||||||
|
auth_reviewer
|
||||||
|
candidate = candidates(:stacy)
|
||||||
|
|
||||||
|
assert_difference("QuizComment.count", 0) do
|
||||||
|
post admin_create_comment_url(test_hash: candidate.test_hash),
|
||||||
|
params: { quiz_comment: { message: '' } }
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_redirected_to admin_result_url(test_hash: candidate.test_hash)
|
||||||
|
assert flash[:error]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should not edit others comments" do
|
||||||
|
auth_reviewer
|
||||||
|
comment = quiz_comments(:com5)
|
||||||
|
post admin_update_comment_url(test_hash: comment.test_hash, id: comment.id),
|
||||||
|
params: { quiz_comment: { message: 'updated comment' } }
|
||||||
|
|
||||||
|
assert_redirected_to admin_login_url
|
||||||
|
assert_equal comment.message, QuizComment.find_by(id: comment.id).message
|
||||||
|
end
|
||||||
|
|
||||||
|
test "can not comment on Gustov" do
|
||||||
|
auth_reviewer
|
||||||
|
candidate = candidates(:gustov)
|
||||||
|
|
||||||
|
assert_difference("QuizComment.count", 0) do
|
||||||
|
post admin_create_comment_url(test_hash: candidate.test_hash),
|
||||||
|
params: { quiz_comment: { message: 'this is a test comment' } }
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_redirected_to admin_login_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -3,10 +3,30 @@ require 'test_helper'
|
|||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class VoteControllerTest < ActionDispatch::IntegrationTest
|
class VoteControllerTest < ActionDispatch::IntegrationTest
|
||||||
test "reviewer can up vote henry" do
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
|
test "reviewer can only vote after commenting" do
|
||||||
auth_user users(:reviewer)
|
auth_user users(:reviewer)
|
||||||
henry = candidates(:henry)
|
henry = candidates(:henry)
|
||||||
|
|
||||||
|
assert_difference("Candidate.find(#{henry.id}).votes.yea.count", 0) do
|
||||||
|
get admin_up_vote_url(henry.test_hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
post admin_create_comment_url(test_hash: henry.test_hash),
|
||||||
|
params: { quiz_comment: { message: 'this is a comment to vote' } }
|
||||||
|
|
||||||
|
assert_difference("Candidate.find(#{henry.id}).votes.yea.count", 1) do
|
||||||
|
get admin_up_vote_url(henry.test_hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "reviewer can up vote henry" do
|
||||||
|
auth_user users(:reviewer2)
|
||||||
|
henry = candidates(:henry)
|
||||||
|
|
||||||
assert_difference("Candidate.find(#{henry.id}).votes.yea.count", 1) do
|
assert_difference("Candidate.find(#{henry.id}).votes.yea.count", 1) do
|
||||||
get admin_up_vote_url(henry.test_hash)
|
get admin_up_vote_url(henry.test_hash)
|
||||||
end
|
end
|
||||||
@ -14,7 +34,7 @@ module Admin
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "reviewer can down vote henry" do
|
test "reviewer can down vote henry" do
|
||||||
auth_user users(:reviewer)
|
auth_user users(:reviewer2)
|
||||||
henry = candidates(:henry)
|
henry = candidates(:henry)
|
||||||
|
|
||||||
assert_difference("Candidate.find(#{henry.id}).votes.nay.count", 1) do
|
assert_difference("Candidate.find(#{henry.id}).votes.nay.count", 1) do
|
||||||
@ -24,7 +44,7 @@ module Admin
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "reviewer can change vote on henry" do
|
test "reviewer can change vote on henry" do
|
||||||
auth_user users(:reviewer)
|
auth_user users(:reviewer2)
|
||||||
henry = candidates(:henry)
|
henry = candidates(:henry)
|
||||||
get admin_up_vote_url(henry.test_hash)
|
get admin_up_vote_url(henry.test_hash)
|
||||||
|
|
||||||
@ -36,24 +56,30 @@ module Admin
|
|||||||
assert_response :success
|
assert_response :success
|
||||||
end
|
end
|
||||||
|
|
||||||
test "manager can approve henry" do
|
test "manager can approve henry and notify recruiter" do
|
||||||
auth_user users(:manager)
|
auth_user users(:manager)
|
||||||
henry = candidates(:henry)
|
henry = candidates(:henry)
|
||||||
get admin_approve_vote_url(henry.test_hash)
|
|
||||||
|
|
||||||
assert_equal 1, henry.votes.approved.count
|
assert_enqueued_jobs 1 do
|
||||||
|
post admin_interview_url(henry.test_hash), params: {
|
||||||
|
review_status: 'approved',
|
||||||
|
review_comments: 'ipsum'
|
||||||
|
}
|
||||||
|
end
|
||||||
assert_equal 'approved', Candidate.find(henry.to_i).review_status
|
assert_equal 'approved', Candidate.find(henry.to_i).review_status
|
||||||
assert_response :success
|
assert_equal 'ipsum', Candidate.find(henry.to_i).review_comments
|
||||||
|
assert_redirected_to admin_result_url(henry.test_hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "manager can decline henry" do
|
test "approve fails without comment" do
|
||||||
auth_user users(:manager)
|
auth_user users(:manager)
|
||||||
henry = candidates(:henry)
|
henry = candidates(:henry)
|
||||||
get admin_decline_vote_url(henry.test_hash)
|
|
||||||
|
|
||||||
assert_equal 1, henry.votes.rejected.count
|
assert_enqueued_jobs 0 do
|
||||||
assert_equal 'declined', Candidate.find(henry.to_i).review_status
|
post admin_interview_url(henry.test_hash), params: { review_status: 'approved' }
|
||||||
assert_response :success
|
end
|
||||||
|
assert_match 'comment', flash[:error]
|
||||||
|
assert_redirected_to admin_result_url(henry.test_hash)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -9,4 +9,11 @@ class AdminControllerTest < ActionDispatch::IntegrationTest
|
|||||||
assert_redirected_to admin_login_path
|
assert_redirected_to admin_login_path
|
||||||
assert_match 'not authorized', flash[:error]
|
assert_match 'not authorized', flash[:error]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'sort_column present' do
|
||||||
|
# a stupid coverage report thing.
|
||||||
|
admin_controller = AdminController.new
|
||||||
|
|
||||||
|
assert_equal :completed_at, admin_controller.send(:sort_column)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
194
test/fixtures/answers.yml
vendored
194
test/fixtures/answers.yml
vendored
@ -696,3 +696,197 @@ wade10:
|
|||||||
created_at: <%= DateTime.now() - 36.hours - 40.minutes %>
|
created_at: <%= DateTime.now() - 36.hours - 40.minutes %>
|
||||||
updated_at: <%= DateTime.now() - 36.hours - 20.minutes %>
|
updated_at: <%= DateTime.now() - 36.hours - 20.minutes %>
|
||||||
|
|
||||||
|
jorge1:
|
||||||
|
candidate: jorge
|
||||||
|
question: Cras justo odio, dapibus ac facilisis in, egestas eget quam. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit.
|
||||||
|
answer: option 3
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 22.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 22.minutes %>
|
||||||
|
|
||||||
|
jorge2:
|
||||||
|
candidate: jorge
|
||||||
|
question: fed2
|
||||||
|
answer: 'indexOf()'
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 24.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 4.minutes %>
|
||||||
|
|
||||||
|
jorge3:
|
||||||
|
candidate: jorge
|
||||||
|
question: fed3
|
||||||
|
answer: {html: '<h1>Salmon</h1>', css: 'h1 {color: salmon;}', js: '', text: 'Gotta lotta GOOD things on sale, strangah.'}
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 26.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 6.minutes %>
|
||||||
|
|
||||||
|
jorge4:
|
||||||
|
candidate: jorge
|
||||||
|
question: fed4
|
||||||
|
answer: Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 28.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 28.minutes %>
|
||||||
|
|
||||||
|
jorge5:
|
||||||
|
candidate: jorge
|
||||||
|
question: fed5
|
||||||
|
answer: 'Dynamic listeners'
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 30.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 30.minutes %>
|
||||||
|
|
||||||
|
jorge6:
|
||||||
|
candidate: jorge
|
||||||
|
question: fed6
|
||||||
|
answer: Integer posuere erat a ante venenatis dapibus posuere velit aliquet.
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 32.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 12.minutes %>
|
||||||
|
|
||||||
|
jorge7:
|
||||||
|
candidate: jorge
|
||||||
|
question: fed7
|
||||||
|
answer: {html: '<p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'}
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 34.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 14.minutes %>
|
||||||
|
|
||||||
|
jorge8:
|
||||||
|
candidate: jorge
|
||||||
|
question: fed8
|
||||||
|
answer:
|
||||||
|
other: Some generic user input
|
||||||
|
options:
|
||||||
|
- other
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 36.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 16.minutes %>
|
||||||
|
|
||||||
|
jorge9:
|
||||||
|
candidate: jorge
|
||||||
|
question: fed9
|
||||||
|
answer:
|
||||||
|
other: Brunch
|
||||||
|
options:
|
||||||
|
- Neither
|
||||||
|
- other
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 38.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 18.minutes %>
|
||||||
|
|
||||||
|
jorge10:
|
||||||
|
candidate: jorge
|
||||||
|
question: fed10
|
||||||
|
answer: ["Live long and prosper", "Who you calling Scruffy?"]
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 40.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 20.minutes %>
|
||||||
|
|
||||||
|
elsie1:
|
||||||
|
candidate: elsie
|
||||||
|
question: Cras justo odio, dapibus ac facilisis in, egestas eget quam. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit.
|
||||||
|
answer: option 3
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 22.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 22.minutes %>
|
||||||
|
|
||||||
|
elsie2:
|
||||||
|
candidate: elsie
|
||||||
|
question: fed2
|
||||||
|
answer: 'indexOf()'
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 24.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 4.minutes %>
|
||||||
|
|
||||||
|
elsie3:
|
||||||
|
candidate: elsie
|
||||||
|
question: fed3
|
||||||
|
answer: {html: '<h1>Salmon</h1>', css: 'h1 {color: salmon;}', js: '', text: 'Gotta lotta GOOD things on sale, strangah.'}
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 26.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 6.minutes %>
|
||||||
|
|
||||||
|
elsie4:
|
||||||
|
candidate: elsie
|
||||||
|
question: fed4
|
||||||
|
answer: Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 28.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 28.minutes %>
|
||||||
|
|
||||||
|
elsie5:
|
||||||
|
candidate: elsie
|
||||||
|
question: fed5
|
||||||
|
answer: 'Dynamic listeners'
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 30.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 30.minutes %>
|
||||||
|
|
||||||
|
elsie6:
|
||||||
|
candidate: elsie
|
||||||
|
question: fed6
|
||||||
|
answer: Integer posuere erat a ante venenatis dapibus posuere velit aliquet.
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 32.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 12.minutes %>
|
||||||
|
|
||||||
|
elsie7:
|
||||||
|
candidate: elsie
|
||||||
|
question: fed7
|
||||||
|
answer: {html: '<p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'}
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 34.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 14.minutes %>
|
||||||
|
|
||||||
|
elsie8:
|
||||||
|
candidate: elsie
|
||||||
|
question: fed8
|
||||||
|
answer:
|
||||||
|
other: Some generic user input
|
||||||
|
options:
|
||||||
|
- other
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 36.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 16.minutes %>
|
||||||
|
|
||||||
|
elsie9:
|
||||||
|
candidate: elsie
|
||||||
|
question: fed9
|
||||||
|
answer:
|
||||||
|
other: Brunch
|
||||||
|
options:
|
||||||
|
- Neither
|
||||||
|
- other
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 38.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 18.minutes %>
|
||||||
|
|
||||||
|
elsie10:
|
||||||
|
candidate: elsie
|
||||||
|
question: fed10
|
||||||
|
answer: ["Live long and prosper", "Who you calling Scruffy?"]
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 40.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 20.minutes %>
|
||||||
|
|
||||||
|
67
test/fixtures/candidates.yml
vendored
67
test/fixtures/candidates.yml
vendored
@ -4,6 +4,9 @@ roy: # Roy should have started, and is ready for a reminder
|
|||||||
name: Roy Cruz
|
name: Roy Cruz
|
||||||
email: <%= CryptSerializer.dump 'roy.cruz@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'roy.cruz@mailinator.com' %>
|
||||||
experience: 0-3
|
experience: 0-3
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'css/html'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: false
|
completed: false
|
||||||
@ -14,6 +17,9 @@ gillian: # Gillian has not begun the test
|
|||||||
name: Gillian Anderson
|
name: Gillian Anderson
|
||||||
email: <%= CryptSerializer.dump 'gillian.anderson@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'gillian.anderson@mailinator.com' %>
|
||||||
experience: 4-6
|
experience: 4-6
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'css/html'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: false
|
completed: false
|
||||||
@ -24,6 +30,9 @@ martha: # Martha has not begun the test
|
|||||||
name: Martha Watts
|
name: Martha Watts
|
||||||
email: <%= CryptSerializer.dump 'martha.watts@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'martha.watts@mailinator.com' %>
|
||||||
experience: 4-6
|
experience: 4-6
|
||||||
|
project: Client/Project
|
||||||
|
position: 'contract'
|
||||||
|
skill_needs: 'angular'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: false
|
completed: false
|
||||||
@ -34,6 +43,9 @@ dawn: # Dawn has completed, and been reminded, but not submitted the test
|
|||||||
name: Dawn Hopkins
|
name: Dawn Hopkins
|
||||||
email: <%= CryptSerializer.dump 'dawn.hopkins@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'dawn.hopkins@mailinator.com' %>
|
||||||
experience: 0-2
|
experience: 0-2
|
||||||
|
project: Client/Project
|
||||||
|
position: 'contract'
|
||||||
|
skill_needs: 'javascript'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: false
|
completed: false
|
||||||
@ -44,6 +56,9 @@ peggy: # Peggy has completed, and been reminded, but not submitted the test
|
|||||||
name: Peggy Blisters
|
name: Peggy Blisters
|
||||||
email: <%= CryptSerializer.dump 'peggy.blisters@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'peggy.blisters@mailinator.com' %>
|
||||||
experience: 0-2
|
experience: 0-2
|
||||||
|
project: Client/Project
|
||||||
|
position: 'contract'
|
||||||
|
skill_needs: 'css/html'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: false
|
completed: false
|
||||||
@ -54,17 +69,25 @@ richard: # Richard has completed AND submitted the test
|
|||||||
name: Richard Burns
|
name: Richard Burns
|
||||||
email: <%= CryptSerializer.dump 'richard.burns@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'richard.burns@mailinator.com' %>
|
||||||
experience: 15+
|
experience: 15+
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'css/html'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: true
|
completed: true
|
||||||
|
completed_at: <%= DateTime.current - 20.days %>
|
||||||
reminded: false
|
reminded: false
|
||||||
test_hash: 6NjnourLE6Y
|
test_hash: 6NjnourLE6Y
|
||||||
review_status: 1
|
review_status: 1
|
||||||
|
review_comments: "Some reasons why or why not, but here they are."
|
||||||
|
|
||||||
juan: # Juan has chosen "finish later" for live coders
|
juan: # Juan has chosen "finish later" for live coders
|
||||||
name: Juan Campbell
|
name: Juan Campbell
|
||||||
email: <%= CryptSerializer.dump 'juan.campbell@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'juan.campbell@mailinator.com' %>
|
||||||
experience: 15+
|
experience: 15+
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'css/html'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: false
|
completed: false
|
||||||
@ -75,20 +98,29 @@ stacy: # Stacy has completed AND submitted the test
|
|||||||
name: Stacy Scott
|
name: Stacy Scott
|
||||||
email: <%= CryptSerializer.dump 'stacy.scott@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'stacy.scott@mailinator.com' %>
|
||||||
experience: 7-9
|
experience: 7-9
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'css/html'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: true
|
completed: true
|
||||||
|
completed_at: <%= DateTime.current - 13.hours %>
|
||||||
reminded: false
|
reminded: false
|
||||||
test_hash: s6oFExZliYYFx
|
test_hash: s6oFExZliYYFx
|
||||||
review_status: 2
|
review_status: 2
|
||||||
|
review_comments: "Some reasons why or why not, but here they are."
|
||||||
|
|
||||||
henry: # Henry has completed AND submitted the test
|
henry: # Henry has completed AND submitted the test
|
||||||
name: Henry Butler
|
name: Henry Butler
|
||||||
email: <%= CryptSerializer.dump 'henry.butler@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'henry.butler@mailinator.com' %>
|
||||||
experience: 4-6
|
experience: 4-6
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'css/html'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: true
|
completed: true
|
||||||
|
completed_at: <%= DateTime.current - 3.days %>
|
||||||
reminded: false
|
reminded: false
|
||||||
test_hash: egPomAuVDeCEp
|
test_hash: egPomAuVDeCEp
|
||||||
|
|
||||||
@ -96,16 +128,51 @@ wade: # Wade has completed AND submitted the test
|
|||||||
name: Wade Armstrong
|
name: Wade Armstrong
|
||||||
email: <%= CryptSerializer.dump 'wade.armstrong@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'wade.armstrong@mailinator.com' %>
|
||||||
experience: 0-3
|
experience: 0-3
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'css/html'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: fed
|
quiz: fed
|
||||||
completed: true
|
completed: true
|
||||||
|
completed_at: <%= DateTime.current - 8.days %>
|
||||||
reminded: false
|
reminded: false
|
||||||
test_hash: BkSkpapJnkz2N
|
test_hash: BkSkpapJnkz2N
|
||||||
|
|
||||||
|
jorge: # Jorge has completed AND submitted the test
|
||||||
|
name: Jorge Holmes
|
||||||
|
email: <%= CryptSerializer.dump 'jorge.holmes@mailinator.com' %>
|
||||||
|
experience: 0-3
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'css/html'
|
||||||
|
recruiter: recruiter
|
||||||
|
quiz: fed
|
||||||
|
completed: true
|
||||||
|
completed_at: <%= DateTime.current - 12.days + 3.hours %>
|
||||||
|
reminded: false
|
||||||
|
test_hash: iC5FdWJxcyySBmpOpU
|
||||||
|
|
||||||
|
elsie: # Elsie has completed AND submitted the test
|
||||||
|
name: Elsie Lowe
|
||||||
|
email: <%= CryptSerializer.dump 'elsie.lowe@mailinator.com' %>
|
||||||
|
experience: 0-3
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'css/html'
|
||||||
|
recruiter: recruiter
|
||||||
|
quiz: fed
|
||||||
|
completed: true
|
||||||
|
completed_at: <%= DateTime.current - 45.days + 6.hours %>
|
||||||
|
reminded: false
|
||||||
|
test_hash: rLSoizA3ATMNSCx
|
||||||
|
|
||||||
gustov: # Gustov is NOT for FED
|
gustov: # Gustov is NOT for FED
|
||||||
name: Gustov
|
name: Gustov
|
||||||
email: <%= CryptSerializer.dump 'gustov@mailinator.com' %>
|
email: <%= CryptSerializer.dump 'gustov@mailinator.com' %>
|
||||||
experience: 0-3
|
experience: 0-3
|
||||||
|
project: Client/Project
|
||||||
|
position: 'full-time'
|
||||||
|
skill_needs: 'C#, SQL'
|
||||||
recruiter: recruiter
|
recruiter: recruiter
|
||||||
quiz: admin
|
quiz: admin
|
||||||
completed: false
|
completed: false
|
||||||
|
87
test/fixtures/quiz_comments.yml
vendored
Normal file
87
test/fixtures/quiz_comments.yml
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
com1:
|
||||||
|
test_hash: BkSkpapJnkz2N #wade
|
||||||
|
user: reviewer
|
||||||
|
message: Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Etiam porta sem malesuada magna mollis euismod. Aenean lacinia bibendum nulla sed consectetur. Maecenas faucibus mollis interdum.
|
||||||
|
|
||||||
|
com2:
|
||||||
|
test_hash: BkSkpapJnkz2N #wade
|
||||||
|
user: reviewer
|
||||||
|
message: Donec sed odio dui. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Curabitur blandit tempus porttitor. Nullam quis risus eget urna mollis ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
|
||||||
|
com3:
|
||||||
|
test_hash: BkSkpapJnkz2N #wade
|
||||||
|
user: reviewer2
|
||||||
|
message: Cras mattis consectetur purus sit amet fermentum. Donec sed odio dui. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.
|
||||||
|
|
||||||
|
com4:
|
||||||
|
test_hash: iC5FdWJxcyySBmpOpU #jorge
|
||||||
|
user: manager
|
||||||
|
message: Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed posuere consectetur est at lobortis.
|
||||||
|
|
||||||
|
com5:
|
||||||
|
test_hash: egPomAuVDeCEp #henry
|
||||||
|
user: manager
|
||||||
|
message: no.
|
||||||
|
|
||||||
|
com6:
|
||||||
|
test_hash: egPomAuVDeCEp #henry
|
||||||
|
user: reviewer2
|
||||||
|
message: fine.
|
||||||
|
|
||||||
|
com7:
|
||||||
|
test_hash: iC5FdWJxcyySBmpOpU #jorge
|
||||||
|
user: reviewer
|
||||||
|
message: Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.
|
||||||
|
|
||||||
|
com8:
|
||||||
|
test_hash: egPomAuVDeCEp #henry
|
||||||
|
user: manager
|
||||||
|
message: no.
|
||||||
|
|
||||||
|
com9:
|
||||||
|
test_hash: rLSoizA3ATMNSCx #elsie
|
||||||
|
user: reviewer
|
||||||
|
message: Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Donec ullamcorper nulla non metus auctor fringilla. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Etiam porta sem malesuada magna mollis euismod. Vestibulum id ligula porta felis euismod semper. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Maecenas faucibus mollis interdum.
|
||||||
|
|
||||||
|
com10:
|
||||||
|
test_hash: rLSoizA3ATMNSCx #elsie
|
||||||
|
user: reviewer2
|
||||||
|
message: Ornare Tellus Nullam Mattis
|
||||||
|
|
||||||
|
com11:
|
||||||
|
test_hash: rLSoizA3ATMNSCx #elsie
|
||||||
|
user: reviewer2
|
||||||
|
message: Nibh Ultricies Purus
|
||||||
|
|
||||||
|
com12:
|
||||||
|
test_hash: rLSoizA3ATMNSCx #elsie
|
||||||
|
user: reviewer
|
||||||
|
message: Donec id elit non mi porta gravida at eget metus.
|
||||||
|
|
||||||
|
com13:
|
||||||
|
test_hash: rLSoizA3ATMNSCx #elsie
|
||||||
|
user: manager
|
||||||
|
message: Donec id elit non mi porta gravida at eget metus.
|
||||||
|
|
||||||
|
com14:
|
||||||
|
test_hash: rLSoizA3ATMNSCx #elsie
|
||||||
|
user: reviewer2
|
||||||
|
message: Ultricies Vulputate Bibendum Parturient
|
||||||
|
|
||||||
|
com15:
|
||||||
|
test_hash: 6NjnourLE6Y #richard
|
||||||
|
user: manager
|
||||||
|
message: gibberish
|
||||||
|
|
||||||
|
com16:
|
||||||
|
test_hash: egPomAuVDeCEp #henry
|
||||||
|
user: admin
|
||||||
|
message: word.
|
||||||
|
|
||||||
|
com17:
|
||||||
|
test_hash: 6NjnourLE6Y #richard
|
||||||
|
user: reviewer
|
||||||
|
message: more gibberish
|
||||||
|
|
27
test/fixtures/reviewer_votes.yml
vendored
27
test/fixtures/reviewer_votes.yml
vendored
@ -66,3 +66,30 @@ reviewer2_wade:
|
|||||||
candidate: wade
|
candidate: wade
|
||||||
user: reviewer2
|
user: reviewer2
|
||||||
|
|
||||||
|
|
||||||
|
manager_jorge:
|
||||||
|
candidate: jorge
|
||||||
|
user: manager
|
||||||
|
|
||||||
|
reviewer_jorge:
|
||||||
|
candidate: jorge
|
||||||
|
user: reviewer
|
||||||
|
vote: 1
|
||||||
|
|
||||||
|
reviewer2_jorge:
|
||||||
|
candidate: jorge
|
||||||
|
user: reviewer2
|
||||||
|
|
||||||
|
|
||||||
|
manager_elsie:
|
||||||
|
candidate: elsie
|
||||||
|
user: manager
|
||||||
|
|
||||||
|
reviewer_elsie:
|
||||||
|
candidate: elsie
|
||||||
|
user: reviewer
|
||||||
|
|
||||||
|
reviewer2_elsie:
|
||||||
|
candidate: elsie
|
||||||
|
user: reviewer2
|
||||||
|
|
||||||
|
@ -8,4 +8,8 @@ class RecruiterMailerPreview < ActionMailer::Preview
|
|||||||
def candidate_submitted
|
def candidate_submitted
|
||||||
RecruiterMailer.candidate_submitted Candidate.find_by(test_hash: 'OvP0ZqGKwJ0') # Dawn
|
RecruiterMailer.candidate_submitted Candidate.find_by(test_hash: 'OvP0ZqGKwJ0') # Dawn
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def candidate_reviewed
|
||||||
|
RecruiterMailer.candidate_reviewed Candidate.find_by(test_hash: 's6oFExZliYYFx') # Stacy
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -4,4 +4,18 @@ class ReviewerMailerPreview < ActionMailer::Preview
|
|||||||
def candidate_submission
|
def candidate_submission
|
||||||
ReviewerMailer.candidate_submission Candidate.find_by(test_hash: 'OvP0ZqGKwJ0') # Dawn
|
ReviewerMailer.candidate_submission Candidate.find_by(test_hash: 'OvP0ZqGKwJ0') # Dawn
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reminder
|
||||||
|
reminders = ReviewerReminder.new
|
||||||
|
reminder = reminders.reminders.first
|
||||||
|
ReviewerMailer.reminder reminder
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_manager
|
||||||
|
ReviewerMailer.notify_manager Candidate.find_by(test_hash: 'OvP0ZqGKwJ0').id # Dawn
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_comment
|
||||||
|
ReviewerMailer.new_comment QuizComment.first
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
class UserMailerPreview < ActionMailer::Preview
|
class UserMailerPreview < ActionMailer::Preview
|
||||||
def password_reset
|
def password_reset
|
||||||
user = User.find_by(email: 'alan.admin@mailinator.com')
|
user = User.find_by(email: 'alan.admin@mailinator.com')
|
||||||
|
user.send(:gen_reset_token)
|
||||||
UserMailer.password_reset user
|
UserMailer.password_reset user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -12,11 +12,24 @@ class RecruiterMailerTest < ActionMailer::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "candidate_submitted" do
|
test "candidate_submitted" do
|
||||||
candidate = candidates :dawn
|
candidate = candidates :elsie
|
||||||
|
manager = users :manager
|
||||||
mail = RecruiterMailer.candidate_submitted candidate
|
mail = RecruiterMailer.candidate_submitted candidate
|
||||||
assert_match candidate.name, mail.subject
|
assert_match candidate.name, mail.subject
|
||||||
assert_equal [candidate.recruiter.email], mail.to
|
assert_equal [candidate.recruiter.email], mail.to
|
||||||
assert_equal [ENV["default_mail_from"]], mail.from
|
assert_equal [ENV["default_mail_from"]], mail.from
|
||||||
assert_match candidate.name, mail.body.encoded
|
assert_match candidate.name, mail.body.encoded
|
||||||
|
assert_match manager.name, mail.body.encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
test "candidate_reviewed" do
|
||||||
|
candidate = candidates :stacy
|
||||||
|
mail = RecruiterMailer.candidate_reviewed candidate
|
||||||
|
assert_match candidate.name, mail.subject
|
||||||
|
assert_equal [candidate.recruiter.email], mail.to
|
||||||
|
assert_equal [ENV["default_mail_from"]], mail.from
|
||||||
|
assert_match candidate.review_status, mail.body.encoded
|
||||||
|
assert_match candidate.name, mail.body.encoded
|
||||||
|
assert_match candidate.review_comments, mail.body.encoded
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -11,4 +11,33 @@ class ReviewerMailerTest < ActionMailer::TestCase
|
|||||||
assert_equal [ENV["default_mail_from"]], mail.from
|
assert_equal [ENV["default_mail_from"]], mail.from
|
||||||
assert_match candidate.test_hash, mail.body.encoded
|
assert_match candidate.test_hash, mail.body.encoded
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "reminder" do
|
||||||
|
reminders = ReviewerReminder.new
|
||||||
|
reminder = reminders.reminders.first
|
||||||
|
mail = ReviewerMailer.reminder reminder
|
||||||
|
assert_match "Review Reminder", mail.subject
|
||||||
|
assert_equal [reminder.email], mail.to
|
||||||
|
assert_equal [ENV["default_mail_from"]], mail.from
|
||||||
|
assert_match reminder.test_hash, mail.body.encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
test "notify_manager" do
|
||||||
|
candidate = candidates(:richard)
|
||||||
|
mail = ReviewerMailer.notify_manager candidate.id
|
||||||
|
assert_match "Voting Complete", mail.subject
|
||||||
|
assert_equal [candidate.manager.email], mail.to
|
||||||
|
assert_equal [ENV["default_mail_from"]], mail.from
|
||||||
|
assert_match candidate.test_hash, mail.body.encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
test "comment notification" do
|
||||||
|
comment = quiz_comments(:com5)
|
||||||
|
mail = ReviewerMailer.new_comment comment
|
||||||
|
assert_match "Comment", mail.subject
|
||||||
|
assert_match comment.test_hash, mail.subject
|
||||||
|
assert_equal comment.candidate.reviewers.map(&:email), mail.to
|
||||||
|
assert_equal [ENV["default_mail_from"]], mail.from
|
||||||
|
assert_match comment.test_hash, mail.body.encoded
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,6 +6,7 @@ class CandidateTest < ActiveSupport::TestCase
|
|||||||
candidate = Candidate.create(name: 'new name',
|
candidate = Candidate.create(name: 'new name',
|
||||||
email: 'test@mailinator.com',
|
email: 'test@mailinator.com',
|
||||||
experience: '0-3',
|
experience: '0-3',
|
||||||
|
project: 'Client',
|
||||||
quiz_id: quizzes(:fed).id)
|
quiz_id: quizzes(:fed).id)
|
||||||
|
|
||||||
assert candidate.test_hash.present?
|
assert candidate.test_hash.present?
|
||||||
@ -16,6 +17,8 @@ class CandidateTest < ActiveSupport::TestCase
|
|||||||
candidate = Candidate.create(name: 'new name',
|
candidate = Candidate.create(name: 'new name',
|
||||||
email: email,
|
email: email,
|
||||||
experience: '0-3',
|
experience: '0-3',
|
||||||
|
project: 'Client',
|
||||||
|
position: 'full-time',
|
||||||
recruiter_id: users(:recruiter).id,
|
recruiter_id: users(:recruiter).id,
|
||||||
quiz_id: quizzes(:fed).id)
|
quiz_id: quizzes(:fed).id)
|
||||||
|
|
||||||
|
27
test/models/quiz_comment_test.rb
Normal file
27
test/models/quiz_comment_test.rb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class QuizCommentTest < ActiveSupport::TestCase
|
||||||
|
test "the truth" do
|
||||||
|
assert QuizComment
|
||||||
|
end
|
||||||
|
|
||||||
|
test "user to comments association" do
|
||||||
|
manager = users(:manager)
|
||||||
|
|
||||||
|
assert_equal 5, manager.quiz_comments.size
|
||||||
|
end
|
||||||
|
|
||||||
|
test "candidate to comments association" do
|
||||||
|
candidate = candidates(:elsie)
|
||||||
|
|
||||||
|
assert_equal 6, candidate.quiz_comments.size
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'comment to user' do
|
||||||
|
comment = quiz_comments(:com1)
|
||||||
|
|
||||||
|
assert_match 'Wade', comment.candidate.name
|
||||||
|
assert_match 'Tina', comment.user.name
|
||||||
|
end
|
||||||
|
end
|
@ -3,7 +3,7 @@ require 'test_helper'
|
|||||||
|
|
||||||
class ReviewerVoteTest < ActiveSupport::TestCase
|
class ReviewerVoteTest < ActiveSupport::TestCase
|
||||||
test "the truth" do
|
test "the truth" do
|
||||||
assert ReviewerVoteTest
|
assert ReviewerVote
|
||||||
end
|
end
|
||||||
|
|
||||||
test "richard has 3 votes" do
|
test "richard has 3 votes" do
|
||||||
@ -12,10 +12,11 @@ class ReviewerVoteTest < ActiveSupport::TestCase
|
|||||||
assert_equal 3, richard.votes.size
|
assert_equal 3, richard.votes.size
|
||||||
end
|
end
|
||||||
|
|
||||||
test "manager has 4 votes" do
|
test "manager has a vote for every completed quiz" do
|
||||||
manager = users(:manager)
|
manager = users(:manager)
|
||||||
|
completed_count = Candidate.where(completed: true).count
|
||||||
|
|
||||||
assert_equal 4, manager.votes.size
|
assert_equal completed_count, manager.votes.size
|
||||||
end
|
end
|
||||||
|
|
||||||
test "richard has been approved" do
|
test "richard has been approved" do
|
||||||
@ -31,4 +32,22 @@ class ReviewerVoteTest < ActiveSupport::TestCase
|
|||||||
assert stacy.declined?
|
assert stacy.declined?
|
||||||
refute stacy.approved?
|
refute stacy.approved?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "mailer is queued on last vote" do
|
||||||
|
reviewer = users(:reviewer2)
|
||||||
|
candidate = candidates(:jorge)
|
||||||
|
|
||||||
|
assert_difference("ActionMailer::Base.deliveries.size", 1) do
|
||||||
|
reviewer.cast_yea_on(candidate)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "mailer is NOT queued on first vote" do
|
||||||
|
reviewer = users(:reviewer2)
|
||||||
|
candidate = candidates(:elsie)
|
||||||
|
|
||||||
|
assert_difference("ActionMailer::Base.deliveries.size", 0) do
|
||||||
|
reviewer.cast_yea_on(candidate)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
30
test/policies/quiz_comment_policy_test.rb
Normal file
30
test/policies/quiz_comment_policy_test.rb
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class QuizCommentPolicyTest < PolicyAssertions::Test
|
||||||
|
test 'should require current_user' do
|
||||||
|
assert_raise Pundit::NotAuthorizedError do
|
||||||
|
QuizCommentPolicy.new(nil, User.first).create?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_create
|
||||||
|
candidate = candidates(:stacy)
|
||||||
|
comment = QuizComment.new(test_hash: candidate.test_hash)
|
||||||
|
|
||||||
|
assert_permit users(:manager), comment
|
||||||
|
assert_permit users(:reviewer), comment
|
||||||
|
assert_permit users(:admin), comment
|
||||||
|
|
||||||
|
refute_permit users(:recruiter), comment
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_update
|
||||||
|
assert_permit users(:reviewer2), quiz_comments(:com6)
|
||||||
|
|
||||||
|
refute_permit users(:reviewer), quiz_comments(:com6)
|
||||||
|
refute_permit users(:manager), quiz_comments(:com6)
|
||||||
|
refute_permit users(:admin), quiz_comments(:com6)
|
||||||
|
refute_permit users(:recruiter), quiz_comments(:com6)
|
||||||
|
end
|
||||||
|
end
|
@ -31,8 +31,8 @@ class ReviewerVotePolicyTest < PolicyAssertions::Test
|
|||||||
def test_up
|
def test_up
|
||||||
assert_permit users(:manager), reviewer_votes(:manager_richard)
|
assert_permit users(:manager), reviewer_votes(:manager_richard)
|
||||||
assert_permit users(:reviewer), reviewer_votes(:reviewer_richard)
|
assert_permit users(:reviewer), reviewer_votes(:reviewer_richard)
|
||||||
assert_permit users(:admin), reviewer_votes(:manager_henry)
|
|
||||||
|
|
||||||
|
refute_permit users(:admin), reviewer_votes(:manager_henry)
|
||||||
refute_permit users(:recruiter), reviewer_votes(:manager_henry)
|
refute_permit users(:recruiter), reviewer_votes(:manager_henry)
|
||||||
refute_permit users(:reviewer), reviewer_votes(:gustov)
|
refute_permit users(:reviewer), reviewer_votes(:gustov)
|
||||||
refute_permit users(:manager), reviewer_votes(:gustov)
|
refute_permit users(:manager), reviewer_votes(:gustov)
|
||||||
@ -41,22 +41,14 @@ class ReviewerVotePolicyTest < PolicyAssertions::Test
|
|||||||
def test_down
|
def test_down
|
||||||
assert_permit users(:manager), reviewer_votes(:manager_richard)
|
assert_permit users(:manager), reviewer_votes(:manager_richard)
|
||||||
assert_permit users(:reviewer), reviewer_votes(:reviewer_richard)
|
assert_permit users(:reviewer), reviewer_votes(:reviewer_richard)
|
||||||
assert_permit users(:admin), reviewer_votes(:manager_henry)
|
|
||||||
|
|
||||||
|
refute_permit users(:admin), reviewer_votes(:manager_henry)
|
||||||
refute_permit users(:recruiter), reviewer_votes(:manager_henry)
|
refute_permit users(:recruiter), reviewer_votes(:manager_henry)
|
||||||
refute_permit users(:reviewer), reviewer_votes(:gustov)
|
refute_permit users(:reviewer), reviewer_votes(:gustov)
|
||||||
refute_permit users(:manager), reviewer_votes(:gustov)
|
refute_permit users(:manager), reviewer_votes(:gustov)
|
||||||
end
|
end
|
||||||
|
|
||||||
def approve
|
def interview_request
|
||||||
assert_permit users(:manager), reviewer_votes(:manager_richard)
|
|
||||||
assert_permit users(:admin), reviewer_votes(:manager_henry)
|
|
||||||
|
|
||||||
refute_permit users(:recruiter), reviewer_votes(:manager_henry)
|
|
||||||
refute_permit users(:reviewer), reviewer_votes(:reviewer_richard)
|
|
||||||
end
|
|
||||||
|
|
||||||
def decline
|
|
||||||
assert_permit users(:manager), reviewer_votes(:manager_richard)
|
assert_permit users(:manager), reviewer_votes(:manager_richard)
|
||||||
assert_permit users(:admin), reviewer_votes(:manager_henry)
|
assert_permit users(:admin), reviewer_votes(:manager_henry)
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ SimpleCov.start 'rails' do
|
|||||||
add_group 'Services & Workers', %w(app/workers app/services)
|
add_group 'Services & Workers', %w(app/workers app/services)
|
||||||
add_group "Jobs", 'app/jobs'
|
add_group "Jobs", 'app/jobs'
|
||||||
add_group "Policies", 'app/policies'
|
add_group "Policies", 'app/policies'
|
||||||
|
|
||||||
|
add_filter "/lib/utils/" # no need to test dev only utility classes
|
||||||
end
|
end
|
||||||
|
|
||||||
require File.expand_path('../../config/environment', __FILE__)
|
require File.expand_path('../../config/environment', __FILE__)
|
||||||
@ -19,6 +21,7 @@ Dir[Rails.root.join("test/test_helpers/**/*.rb")].each { |f| require f }
|
|||||||
Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(color: true)]
|
Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(color: true)]
|
||||||
|
|
||||||
class ActiveSupport::TestCase
|
class ActiveSupport::TestCase
|
||||||
|
ActiveRecord::Migration.maintain_test_schema!
|
||||||
ActiveRecord::Migration.check_pending!
|
ActiveRecord::Migration.check_pending!
|
||||||
|
|
||||||
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class ReminderTest < ActiveSupport::TestCase
|
class CandidateReminderTest < ActiveSupport::TestCase
|
||||||
test "collection is created with one result" do
|
test "collection is created with one result" do
|
||||||
reminders = Reminder.new
|
reminders = CandidateReminder.new
|
||||||
assert_equal 1, reminders.size
|
assert_equal 1, reminders.size
|
||||||
end
|
end
|
||||||
|
|
||||||
test "each candidate has needed attributes" do
|
test "each candidate has needed attributes" do
|
||||||
reminders = Reminder.new
|
reminders = CandidateReminder.new
|
||||||
|
|
||||||
assert_instance_of String, reminders.candidates.first.name
|
assert_instance_of String, reminders.candidates.first.name
|
||||||
assert_instance_of String, reminders.candidates.first.test_hash
|
assert_instance_of String, reminders.candidates.first.test_hash
|
||||||
@ -16,7 +16,7 @@ class ReminderTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "send reminders sends email, and flags reminded" do
|
test "send reminders sends email, and flags reminded" do
|
||||||
reminders = Reminder.new
|
reminders = CandidateReminder.new
|
||||||
pre_reminded = Candidate.find(reminders.candidates.first.id).reminded
|
pre_reminded = Candidate.find(reminders.candidates.first.id).reminded
|
||||||
|
|
||||||
assert_difference("ActionMailer::Base.deliveries.size", reminders.count) do
|
assert_difference("ActionMailer::Base.deliveries.size", reminders.count) do
|
@ -97,7 +97,7 @@ class QuizStatusTest < ActiveSupport::TestCase
|
|||||||
dawn = candidates :dawn
|
dawn = candidates :dawn
|
||||||
status = QuizStatus.new dawn
|
status = QuizStatus.new dawn
|
||||||
|
|
||||||
assert_equal nil, status.current_question_id
|
assert_nil status.current_question_id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "richard has no_finish_laters" do
|
test "richard has no_finish_laters" do
|
||||||
|
25
test/workers/reviewer_reminder_test.rb
Normal file
25
test/workers/reviewer_reminder_test.rb
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class ReviewerReminderTest < ActiveSupport::TestCase
|
||||||
|
test "collection is created with results" do
|
||||||
|
reminders = ReviewerReminder.new
|
||||||
|
assert_equal 6, reminders.size
|
||||||
|
end
|
||||||
|
|
||||||
|
test "each reminder has needed attributes" do
|
||||||
|
collection = ReviewerReminder.new
|
||||||
|
|
||||||
|
assert_instance_of String, collection.reminders.first.name
|
||||||
|
assert_instance_of String, collection.reminders.first.email
|
||||||
|
assert_instance_of String, collection.reminders.first.test_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
test "send_all sends emails for each reviewer and test" do
|
||||||
|
collection = ReviewerReminder.new
|
||||||
|
|
||||||
|
assert_difference("ActionMailer::Base.deliveries.size", collection.count) do
|
||||||
|
collection.send_all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user