Introduced candidate voting and various tweaks
Merge branch 'develop'
This commit is contained in:
commit
439c0bf553
@ -75,7 +75,7 @@ guard :shell, all_on_start: true do
|
|||||||
# TODO: Annoyingly, all files are linted twice on start/full runs. Why?
|
# TODO: Annoyingly, all files are linted twice on start/full runs. Why?
|
||||||
watch %r{app/assets/javascripts/*/.*} do |file|
|
watch %r{app/assets/javascripts/*/.*} do |file|
|
||||||
system %(echo "ESLint:\033[32m #{file[0]}\033[0m")
|
system %(echo "ESLint:\033[32m #{file[0]}\033[0m")
|
||||||
system %(eslint #{file[0]})
|
system %(./node_modules/eslint/bin/eslint.js #{file[0]})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -77,10 +77,14 @@ There are some convenience scripts included to make starting the container and r
|
|||||||
* `./start-server.sh`
|
* `./start-server.sh`
|
||||||
- starts up just rails server for viewing application
|
- starts up just rails server for viewing application
|
||||||
|
|
||||||
|
## Deploying a new version
|
||||||
|
* ssh into server
|
||||||
|
* cd into app root
|
||||||
|
* run deploy.sh
|
||||||
|
|
||||||
## TODOs and notes
|
## TODOs and notes
|
||||||
|
|
||||||
* Question attachment path: http://dev.perficientxd.com/skill_assets/
|
* Question attachment path: https://dev.perficientdigital.com/skills-app-images/
|
||||||
* clean code
|
* clean code
|
||||||
* [Confident Ruby](http://www.confidentruby.com/)
|
* [Confident Ruby](http://www.confidentruby.com/)
|
||||||
* [POODR](http://www.poodr.com/)
|
* [POODR](http://www.poodr.com/)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
function handleAjaxResponse($el) {
|
function handleAjaxResponse($el, callback) {
|
||||||
var $header = $('header');
|
var $header = $('header');
|
||||||
$el.on("ajax:success", function(e, data){
|
$el.on("ajax:success", function(e, data){
|
||||||
$header.after('<div class="success">' + data.message + '</div>');
|
$header.after('<div class="success">' + data.message + '</div>');
|
||||||
|
callback(data);
|
||||||
}).on("ajax:error", function(e, xhr) {
|
}).on("ajax:error", function(e, xhr) {
|
||||||
if (xhr.status === 400){
|
if (xhr.status === 400){
|
||||||
$header.after('<div class="error">' + xhr.responseJSON.join('<br>') + '</div>');
|
$header.after('<div class="error">' + xhr.responseJSON.join('<br>') + '</div>');
|
||||||
@ -11,6 +12,25 @@ function handleAjaxResponse($el) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateVotes(data){
|
||||||
|
$("[data-id=up-votes]").html(data.upCount);
|
||||||
|
$("[data-id=down-votes]").html(data.downCount);
|
||||||
|
$("[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)); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('[data-id=vote-count]').each(function(){ handleAjaxResponse($(this), updateVotes); });
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('[data-id=veto-status]').each(function(){ handleAjaxResponse($(this), updateVeto); });
|
||||||
|
});
|
||||||
|
57
app/assets/stylesheets/molecules/_accordions.scss
Normal file
57
app/assets/stylesheets/molecules/_accordions.scss
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
.accordion {
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
|
||||||
|
[type="checkbox"]:checked + label,
|
||||||
|
[type="checkbox"]:checked ~ label:after,
|
||||||
|
[type="checkbox"]:not(:checked) + label,
|
||||||
|
[type="checkbox"]:not(:checked) ~ label:after,
|
||||||
|
[type="radio"]:checked + label,
|
||||||
|
[type="radio"]:checked ~ label:after,
|
||||||
|
[type="radio"]:not(:checked) + label,
|
||||||
|
[type="radio"]:not(:checked) ~ label:after {
|
||||||
|
content: "";
|
||||||
|
border: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="checkbox"]:hover:not(:disabled) + label:before,
|
||||||
|
[type="radio"]:hover:not(:disabled) + label:before,
|
||||||
|
[type="radio"]:not(:checked) ~ label:before,
|
||||||
|
[type="checkbox"]:not(:checked) ~ label:before,
|
||||||
|
[type="radio"]:checked ~ label:before,
|
||||||
|
[type="checkbox"]:checked ~ label:before {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0;
|
||||||
|
content: "+";
|
||||||
|
font-size: 1.3em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="checkbox"]:hover:checked + label:before,
|
||||||
|
[type="radio"]:hover:checked + label:before,
|
||||||
|
[type="radio"]:checked ~ label:before,
|
||||||
|
[type="checkbox"]:checked ~ label:before {
|
||||||
|
background-color: transparent;
|
||||||
|
content: "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="radio"]:not(:checked) + label,
|
||||||
|
[type="checkbox"]:not(:checked) + label,
|
||||||
|
[type="radio"]:checked + label,
|
||||||
|
[type="checkbox"]:checked + label {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion__copy {
|
||||||
|
display: none;
|
||||||
|
margin-top: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion__toggle:checked ~ .accordion__copy {
|
||||||
|
background-color: $gray-lighter;
|
||||||
|
display: block;
|
||||||
|
padding: 2.5em 1.25em 1em 1.75em;
|
||||||
|
margin: -2.25em 0 2em -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
app/assets/stylesheets/molecules/_admin_review.scss
Normal file
31
app/assets/stylesheets/molecules/_admin_review.scss
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.admin-review {
|
||||||
|
counter-reset: question;
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin-left: 2.3em;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: counter(question) ") ";
|
||||||
|
counter-increment: question;
|
||||||
|
font-size: 1.25em;
|
||||||
|
left: -1.8em;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.review_meta {
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
display: flex;
|
||||||
|
& > div { flex: 1 1 auto; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.review_meta__votes,
|
||||||
|
.review_meta__vetos {
|
||||||
|
a { padding: 5px; }
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,10 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.code-results {
|
||||||
|
margin-bottom: -0.65em;
|
||||||
|
}
|
||||||
|
|
||||||
.results {
|
.results {
|
||||||
border: 1px solid $secondary-color;
|
border: 1px solid $secondary-color;
|
||||||
clear: both;
|
clear: both;
|
||||||
@ -31,8 +35,15 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset:disabled .results {
|
fieldset:disabled {
|
||||||
|
.results {
|
||||||
border-color: #bbb;
|
border-color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-results,
|
||||||
|
.code-input label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
|
@ -62,7 +62,7 @@ module Admin
|
|||||||
|
|
||||||
def question_params
|
def question_params
|
||||||
params.require(:question).permit(
|
params.require(:question).permit(
|
||||||
:quiz_id, :question, :category, :input_type, :sort, :active, :input_options,
|
:quiz_id, :question, :category, :attachment, :input_type, :sort, :active, :input_options,
|
||||||
multi_choice: [], live_code: [:later, :html, :css, :js, :text]
|
multi_choice: [], live_code: [:later, :html, :css, :js, :text]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
58
app/controllers/admin/vote_controller.rb
Normal file
58
app/controllers/admin/vote_controller.rb
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
module Admin
|
||||||
|
class VoteController < AdminController
|
||||||
|
def up
|
||||||
|
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
||||||
|
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
||||||
|
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
|
||||||
|
|
||||||
|
def down
|
||||||
|
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
||||||
|
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
||||||
|
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
|
||||||
|
|
||||||
|
def approve
|
||||||
|
@candidate = Candidate.find_by(test_hash: params[:test_hash])
|
||||||
|
authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id)
|
||||||
|
current_user.approve_candidate(@candidate)
|
||||||
|
|
||||||
|
results = {
|
||||||
|
message: "Interview requested!",
|
||||||
|
requestCopy: "Requested",
|
||||||
|
declineCopy: "Decline Interview"
|
||||||
|
}
|
||||||
|
render json: results.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def decline
|
||||||
|
@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 = {
|
||||||
|
message: "Interview declined.",
|
||||||
|
requestCopy: "Request Interview",
|
||||||
|
declineCopy: "Declined"
|
||||||
|
}
|
||||||
|
render json: results.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -36,6 +36,7 @@ class QuizController < ApplicationController
|
|||||||
|
|
||||||
def complete_and_email
|
def complete_and_email
|
||||||
if current_candidate.update_attributes(completed: true)
|
if current_candidate.update_attributes(completed: true)
|
||||||
|
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
|
||||||
ReviewerMailer.candidate_submission(current_candidate).deliver_later
|
ReviewerMailer.candidate_submission(current_candidate).deliver_later
|
||||||
|
@ -4,6 +4,6 @@ class ReviewerMailer < ApplicationMailer
|
|||||||
@candidate = candidate
|
@candidate = candidate
|
||||||
recipients = candidate.quiz.reviewers.map(&:email)
|
recipients = candidate.quiz.reviewers.map(&:email)
|
||||||
|
|
||||||
mail to: recipients, subject: "Skills Assessment Results"
|
mail to: recipients, subject: "Skills Assessment Results - #{@candidate.test_hash}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,8 @@ class Candidate < ApplicationRecord
|
|||||||
has_many :questions, -> { order("sort") }, through: :quiz
|
has_many :questions, -> { order("sort") }, through: :quiz
|
||||||
has_many :answers
|
has_many :answers
|
||||||
belongs_to :recruiter, class_name: "User"
|
belongs_to :recruiter, class_name: "User"
|
||||||
|
has_many :votes, class_name: "ReviewerVote"
|
||||||
|
has_many :reviewers, through: :quiz
|
||||||
|
|
||||||
serialize :email, CryptSerializer
|
serialize :email, CryptSerializer
|
||||||
|
|
||||||
@ -15,6 +17,18 @@ class Candidate < ApplicationRecord
|
|||||||
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
|
||||||
|
|
||||||
|
enum review_status: {
|
||||||
|
pending: 0,
|
||||||
|
approved: 1,
|
||||||
|
declined: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
def build_reviews
|
||||||
|
reviewers.each do |reviewer|
|
||||||
|
votes.find_or_create_by(user_id: reviewer.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def submitted_answers
|
def submitted_answers
|
||||||
answers.where(submitted: true)
|
answers.where(submitted: true)
|
||||||
end
|
end
|
||||||
|
18
app/models/reviewer_vote.rb
Normal file
18
app/models/reviewer_vote.rb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class ReviewerVote < ApplicationRecord
|
||||||
|
belongs_to :candidate
|
||||||
|
belongs_to :user
|
||||||
|
|
||||||
|
validates :user_id, uniqueness: { scope: :candidate_id }
|
||||||
|
|
||||||
|
enum vote: {
|
||||||
|
undecided: 0,
|
||||||
|
yea: 1,
|
||||||
|
nay: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
enum veto: {
|
||||||
|
approved: 1,
|
||||||
|
rejected: 2
|
||||||
|
}
|
||||||
|
end
|
@ -4,6 +4,9 @@ class User < ApplicationRecord
|
|||||||
has_many :candidates, foreign_key: :recruiter_id
|
has_many :candidates, foreign_key: :recruiter_id
|
||||||
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 :reviewees, through: :quizzes, source: :candidates
|
||||||
|
|
||||||
validates :email, presence: true, uniqueness: true
|
validates :email, presence: true, uniqueness: true
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
@ -15,6 +18,42 @@ class User < ApplicationRecord
|
|||||||
save
|
save
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Voting
|
||||||
|
def cast_yea_on candidate
|
||||||
|
vote = votes.find_by(candidate_id: candidate.to_i)
|
||||||
|
vote.vote = :yea
|
||||||
|
vote.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_nay_on candidate
|
||||||
|
vote = votes.find_by(candidate_id: candidate.to_i)
|
||||||
|
vote.vote = :nay
|
||||||
|
vote.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def approve_candidate candidate
|
||||||
|
candidate = Candidate.find(candidate.to_i)
|
||||||
|
|
||||||
|
vote = votes.find_by(candidate_id: candidate.to_i)
|
||||||
|
vote.veto = :approved
|
||||||
|
candidate.update_attribute(:review_status, :approved) if vote.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def decline_candidate candidate
|
||||||
|
candidate = Candidate.find(candidate.to_i)
|
||||||
|
|
||||||
|
vote = votes.find_by(candidate_id: candidate.to_i)
|
||||||
|
vote.veto = :rejected
|
||||||
|
candidate.update_attribute(:review_status, :declined) if vote.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def my_vote candidate
|
||||||
|
candidate = Candidate.find(candidate.to_i)
|
||||||
|
|
||||||
|
my_vote = votes.find_by(candidate_id: candidate.id)
|
||||||
|
my_vote.vote unless my_vote.nil?
|
||||||
|
end
|
||||||
|
|
||||||
# Roles
|
# Roles
|
||||||
def admin?
|
def admin?
|
||||||
'admin' == role
|
'admin' == role
|
||||||
@ -45,7 +84,7 @@ class User < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def acts_as_reviewer?
|
def acts_as_reviewer?
|
||||||
%w(admin reviewer).include? role
|
%w(admin manager reviewer).include? role
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
45
app/policies/reviewer_vote_policy.rb
Normal file
45
app/policies/reviewer_vote_policy.rb
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class ReviewerVotePolicy < ApplicationPolicy
|
||||||
|
# Voting Policy
|
||||||
|
#
|
||||||
|
# Only Reviewers, Managers, and Admins, can cast a vote on a quiz result
|
||||||
|
#
|
||||||
|
# Reviewers can vote any quiz they are linked to
|
||||||
|
# Only Managers, and Admins, can veto a quiz result
|
||||||
|
|
||||||
|
def up?
|
||||||
|
return true if user.acts_as_admin?
|
||||||
|
return false unless record.candidate.reviewers.include? user
|
||||||
|
user.acts_as_reviewer?
|
||||||
|
end
|
||||||
|
|
||||||
|
def down?
|
||||||
|
return true if user.acts_as_admin?
|
||||||
|
return false unless record.candidate.reviewers.include? user
|
||||||
|
user.acts_as_reviewer?
|
||||||
|
end
|
||||||
|
|
||||||
|
def approve?
|
||||||
|
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 false unless record.candidate.reviewers.include? user
|
||||||
|
user.acts_as_manager?
|
||||||
|
end
|
||||||
|
|
||||||
|
class Scope < Scope
|
||||||
|
def resolve
|
||||||
|
return ReviewerVote.none if user.recruiter?
|
||||||
|
|
||||||
|
if user.reviewer?
|
||||||
|
scope.where(user_id: user.id)
|
||||||
|
else
|
||||||
|
scope
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,11 +1,11 @@
|
|||||||
<%
|
<%
|
||||||
content_for :main_class, "intro_tpl"
|
content_for :main_class, "intro_tpl"
|
||||||
|
content_for :title, "Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<h1>Admin Login</h1>
|
<h1>Admin Login</h1>
|
||||||
|
|
||||||
<%= form_for :auth, url: admin_login_path do |form| %>
|
<%= form_for :auth, url: admin_login_path do |form| %>
|
||||||
|
|
||||||
<% if flash[:error].present? %>
|
<% if flash[:error].present? %>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
Need a <%= link_to "password reset", admin_reset_request_path %>?
|
Need a <%= link_to "password reset", admin_reset_request_path %>?
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :main_class, "intro_tpl"
|
content_for :main_class, "intro_tpl"
|
||||||
|
content_for :title, "Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<h1>Password Reset</h1>
|
<h1>Password Reset</h1>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :main_class, "intro_tpl"
|
content_for :main_class, "intro_tpl"
|
||||||
|
content_for :title, "Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<h1>Password Reset</h1>
|
<h1>Password Reset</h1>
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
<%
|
||||||
|
content_for :title, "Edit Candidate - Skills Assessment Admin"
|
||||||
|
%>
|
||||||
|
|
||||||
<main class="intro_tpl">
|
<main class="intro_tpl">
|
||||||
<h1>Edit: <%= @candidate.name %></h1>
|
<h1>Edit: <%= @candidate.name %></h1>
|
||||||
<p><strong>Test ID: </strong><%= @candidate.test_hash %></p>
|
<p><strong>Test ID: </strong><%= @candidate.test_hash %></p>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "Candidates"
|
content_for :section_title, "Candidates"
|
||||||
|
content_for :title, "Candidates - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
<main class="summary_tpl">
|
<main class="summary_tpl">
|
||||||
<%= link_to(admin_new_candidate_path, { class: 'secondary-btn' }) do %>
|
<%= link_to(admin_new_candidate_path, { class: 'secondary-btn' }) do %>
|
||||||
@ -15,6 +16,7 @@
|
|||||||
<th>Progress</th>
|
<th>Progress</th>
|
||||||
<th>Completed</th>
|
<th>Completed</th>
|
||||||
<th>Reminded</th>
|
<th>Reminded</th>
|
||||||
|
<th>Interview Request</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<% @candidates.each do |candidate| %>
|
<% @candidates.each do |candidate| %>
|
||||||
@ -28,8 +30,9 @@
|
|||||||
</td>
|
</td>
|
||||||
<td><%= candidate.experience %> years</td>
|
<td><%= candidate.experience %> years</td>
|
||||||
<td><%= candidate.status %></td>
|
<td><%= candidate.status %></td>
|
||||||
<td><%= candidate.completed ? "Submitted" : "" %></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>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</table>
|
</table>
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
<%
|
||||||
|
content_for :title, "New Candidate - Skills Assessment Admin"
|
||||||
|
%>
|
||||||
|
|
||||||
<main class="intro_tpl">
|
<main class="intro_tpl">
|
||||||
<h1>New Candidate</h1>
|
<h1>New Candidate</h1>
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "Edit: #{@user.name}"
|
content_for :section_title, "Edit: #{@user.name}"
|
||||||
|
content_for :title, "Profile - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<%= render partial: 'shared/form_model_errors', locals: {obj: @user} %>
|
<%= render partial: 'shared/form_model_errors', locals: {obj: @user} %>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "Profile"
|
content_for :section_title, "Profile"
|
||||||
|
content_for :title, "Profile - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<p>Name: <%= current_user.name %></p>
|
<p>Name: <%= current_user.name %></p>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "Questions"
|
content_for :section_title, "Questions"
|
||||||
|
content_for :title, "Edit Question - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<h1><%= @question.quiz.name %></h1>
|
<h1><%= @question.quiz.name %></h1>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "Questions"
|
content_for :section_title, "Questions"
|
||||||
|
content_for :title, "Questions - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<% quizzes = @questions.group_by{ |q| q.quiz.name } %>
|
<% quizzes = @questions.group_by{ |q| q.quiz.name } %>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "New Question"
|
content_for :section_title, "New Question"
|
||||||
|
content_for :title, "New Question - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<%= render partial: 'form', locals: {question: @question, action: admin_create_question_path } %>
|
<%= render partial: 'form', locals: {question: @question, action: admin_create_question_path } %>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "Question for #{@question.quiz.name}"
|
content_for :section_title, "Question for #{@question.quiz.name}"
|
||||||
|
content_for :title, "Question - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<table cellspacing="0" cellpadding="0">
|
<table cellspacing="0" cellpadding="0">
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "Quizzes"
|
content_for :section_title, "Quizzes"
|
||||||
|
content_for :title, "Quizzes - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<%= render partial: 'admin/quiz/table_list', locals: { quizzes: @quizzes } %>
|
<%= render partial: 'admin/quiz/table_list', locals: { quizzes: @quizzes } %>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "New Quiz"
|
content_for :section_title, "New Quiz"
|
||||||
|
content_for :title, "New Quiz - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<%= render partial: 'form', locals: { quiz: @quiz, action: admin_create_quiz_path } %>
|
<%= render partial: 'form', locals: { quiz: @quiz, action: admin_create_quiz_path } %>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "#{@quiz.name}"
|
content_for :section_title, "#{@quiz.name}"
|
||||||
|
content_for :title, "Quiz - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<p><%= @quiz.name %></p>
|
<p><%= @quiz.name %></p>
|
||||||
|
34
app/views/admin/result/_voting.html.erb
Normal file
34
app/views/admin/result/_voting.html.erb
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<% # TODO: This needs to be extracted into a decorator, or something. It is only a quick hack solution. %>
|
||||||
|
|
||||||
|
<% if current_user.acts_as_reviewer? %>
|
||||||
|
<div class="review_meta__votes" data-id="vote-count">
|
||||||
|
<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>)
|
||||||
|
<% 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 %>
|
||||||
|
<small>(Your vote: <span data-id="my-vote"><%= current_user.my_vote(@candidate) %></span>)</small>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if current_user.acts_as_manager? %>
|
||||||
|
<div class="review_meta__vetos" data-id="veto-status">
|
||||||
|
<strong>Manager Vetos: </strong>
|
||||||
|
<%= link_to admin_approve_vote_path(test_hash: @candidate.test_hash), remote: true do %>
|
||||||
|
<span data-id="interview-request">
|
||||||
|
<%= @candidate.approved? ? "Requested" : "Request Interview" %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
<%= link_to admin_decline_vote_path(test_hash: @candidate.test_hash), remote: true do %>
|
||||||
|
<span data-id="interview-decline">
|
||||||
|
<%= @candidate.declined? ? "Declined" : "Decline Interview" %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% else %>
|
||||||
|
|
||||||
|
<strong>Candidate Interview Status: </strong><%= @candidate.review_status %>
|
||||||
|
<% end %>
|
@ -1,5 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
content_for :section_title, "Completed Tests"
|
content_for :section_title, "Completed Tests"
|
||||||
|
content_for :title, "Quiz Results - Skills Assessment Admin"
|
||||||
%>
|
%>
|
||||||
<main class="summary_tpl">
|
<main class="summary_tpl">
|
||||||
<table cellspacing="0" cellpadding="0">
|
<table cellspacing="0" cellpadding="0">
|
||||||
@ -7,6 +8,7 @@
|
|||||||
<th>Test ID</th>
|
<th>Test ID</th>
|
||||||
<th>Experience</th>
|
<th>Experience</th>
|
||||||
<th>Recruiter</th>
|
<th>Recruiter</th>
|
||||||
|
<th>Interview Request</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<% @candidates.each do |candidate| %>
|
<% @candidates.each do |candidate| %>
|
||||||
@ -14,6 +16,7 @@
|
|||||||
<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.experience %> years</td>
|
||||||
<td><%= mail_to(candidate.recruiter.email) %></td>
|
<td><%= mail_to(candidate.recruiter.email) %></td>
|
||||||
|
<td><%= candidate.review_status unless candidate.pending? %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</table>
|
</table>
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
<main class="summary_tpl">
|
<%
|
||||||
|
content_for :title, "Quiz Review - Skills Assessment Admin"
|
||||||
|
%>
|
||||||
|
|
||||||
|
<main class="summary_tpl admin-review">
|
||||||
<h2 class="prft-heading">Quiz Review</h2>
|
<h2 class="prft-heading">Quiz Review</h2>
|
||||||
<p>
|
|
||||||
|
<div class="review_meta">
|
||||||
|
<div>
|
||||||
<strong>Test ID:</strong> <%= @candidate.test_hash %><br />
|
<strong>Test ID:</strong> <%= @candidate.test_hash %><br />
|
||||||
<strong>Years of Experience:</strong> <%= @candidate.experience %><br />
|
<strong>Years of Experience:</strong> <%= @candidate.experience %><br />
|
||||||
<strong>Recruiter Email:</strong> <%= mail_to @candidate.recruiter.name, @candidate.recruiter.email %><br />
|
<strong>Recruiter Email:</strong> <%= mail_to @candidate.recruiter.name, @candidate.recruiter.email %><br />
|
||||||
</p>
|
</div>
|
||||||
|
|
||||||
|
<div><%= render partial: 'voting' %></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<% @quiz.each do |question| %>
|
<% @quiz.each do |question| %>
|
||||||
<%= form_for(:answer, url: '#never-post', html:{id: 'summary-form'}) do |form| %>
|
<%= form_for(:answer, url: '#never-post', html:{id: 'summary-form'}) do |form| %>
|
||||||
|
@ -15,12 +15,20 @@
|
|||||||
<%= form.select :role, admin_role_options(user.role), include_blank: false %>
|
<%= form.select :role, admin_role_options(user.role), include_blank: false %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div><strong>Quiz Review List</strong></div>
|
||||||
|
<p>
|
||||||
|
Quizzes this user should be reviewing the results of.<br />
|
||||||
|
Admins and Recruiters should not have any checked, unless they are expected
|
||||||
|
to participate in the technical review for that quiz.
|
||||||
|
</p>
|
||||||
<%= form.collection_check_boxes(:quiz_ids, Quiz.all, :id, :name, {}, {class: 'checkbox'}) do | quiz | %>
|
<%= form.collection_check_boxes(:quiz_ids, Quiz.all, :id, :name, {}, {class: 'checkbox'}) do | quiz | %>
|
||||||
<div class="form-group-multiples">
|
<div class="form-group-multiples">
|
||||||
<%= quiz.check_box( checked: user.quizzes.include?(quiz.object)) %>
|
<%= quiz.check_box( checked: user.quizzes.include?(quiz.object)) %>
|
||||||
<%= quiz.label %>
|
<%= quiz.label %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%= form.submit %>
|
<%= form.submit %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
<% content_for :title, "Skills Assessment" %>
|
||||||
|
|
||||||
<main class="intro_tpl">
|
<main class="intro_tpl">
|
||||||
<h1 class="prft-heading">Oops!</h1>
|
<h1 class="prft-heading">Oops!</h1>
|
||||||
<p>
|
<p>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<% content_for :title, "Saved! - Skills Assessment" %>
|
||||||
<main class="styleguide_tpl">
|
<main class="styleguide_tpl">
|
||||||
<p>
|
<p>
|
||||||
Your test results have been saved. You can visit again later with your Test ID to complete
|
Your test results have been saved. You can visit again later with your Test ID to complete
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<% content_for :title, "Thank You - Skills Assessment" %>
|
||||||
<main class="styleguide_tpl">
|
<main class="styleguide_tpl">
|
||||||
<h1>Thank you!</h1>
|
<h1>Thank you!</h1>
|
||||||
<p>
|
<p>
|
||||||
|
@ -45,6 +45,17 @@
|
|||||||
</noscript>
|
</noscript>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion" id="accordion<%= question.question_id %>" style="display: none;">
|
||||||
|
<input type="checkbox" class="accordion__toggle" id="accordion-toggle-live-coder" />
|
||||||
|
<label class="accordion__label" for="accordion-toggle-live-coder">How to use the live coder</label>
|
||||||
|
<p class="accordion__copy">
|
||||||
|
This is our own nifty creation, and it works similarly to CodePen. To use: type any HTML, CSS,
|
||||||
|
or JS inside their corresponding boxes, and watch the Results window below the boxes update
|
||||||
|
with your changes. Once you’re happy with your code and how it renders in the Results window,
|
||||||
|
move on to the next question!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="answer<%= question.question_id %>" data-id="live-coder-answer" style="display: none;">
|
<div id="answer<%= question.question_id %>" data-id="live-coder-answer" style="display: none;">
|
||||||
<label for="answer_answer_hash_text">Enter answer here</label>
|
<label for="answer_answer_hash_text">Enter answer here</label>
|
||||||
<%= text_area_tag 'answer[answer_hash][text]', value_text, { disabled: true, data: {last: answers['text']}} %>
|
<%= text_area_tag 'answer[answer_hash][text]', value_text, { disabled: true, data: {last: answers['text']}} %>
|
||||||
@ -64,6 +75,7 @@
|
|||||||
<%= text_area_tag 'answer[answer_hash][js]', value_js, { disabled: true, data: {id: 'code-js', last: answers['js']}, class: 'code-answer code-js' } %>
|
<%= text_area_tag 'answer[answer_hash][js]', value_js, { disabled: true, data: {id: 'code-js', last: answers['js']}, class: 'code-answer code-js' } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<label class="code-results">Results</label>
|
||||||
<div class="results" data-id="results"></div>
|
<div class="results" data-id="results"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -71,6 +83,7 @@
|
|||||||
<% # removes the no-js message %>
|
<% # removes the no-js message %>
|
||||||
document.getElementById("nojs<%= question.question_id %>").style.display = "none";
|
document.getElementById("nojs<%= question.question_id %>").style.display = "none";
|
||||||
document.getElementById("answer<%= question.question_id %>").style.display = "";
|
document.getElementById("answer<%= question.question_id %>").style.display = "";
|
||||||
|
document.getElementById("accordion<%= question.question_id %>").style.display = "";
|
||||||
|
|
||||||
<% # we want the coders disabled until JS is confirmed, so form post is easier to validate %>
|
<% # we want the coders disabled until JS is confirmed, so form post is easier to validate %>
|
||||||
var coders = document.querySelectorAll("[data-id=live-coder-answer] textarea");
|
var coders = document.querySelectorAll("[data-id=live-coder-answer] textarea");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<%
|
<%
|
||||||
content_for :title, "Skills Assessment"
|
content_for :title, "Summary - Skills Assessment"
|
||||||
content_for :footer_title, "Skills Assessment"
|
content_for :footer_title, "Skills Assessment"
|
||||||
content_for :progress, @status.progress.to_s
|
content_for :progress, @status.progress.to_s
|
||||||
content_for_javascript_once 'summary-edit' do
|
content_for_javascript_once 'summary-edit' do
|
||||||
|
@ -50,6 +50,11 @@ Rails.application.routes.draw do
|
|||||||
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
|
||||||
|
get "admin/vote/:test_hash/down", to: "admin/vote#down", as: :admin_down_vote
|
||||||
|
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", to: "admin/dashboard#show", as: :admin
|
get "/admin", to: "admin/dashboard#show", as: :admin
|
||||||
|
|
||||||
#########################################################################################
|
#########################################################################################
|
||||||
|
18
db/migrate/20161118023249_candidate_review_system.rb
Normal file
18
db/migrate/20161118023249_candidate_review_system.rb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class CandidateReviewSystem < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
create_table :reviewer_votes do |t|
|
||||||
|
t.integer :candidate_id
|
||||||
|
t.integer :user_id
|
||||||
|
t.integer :vote, default: 0, null: false
|
||||||
|
t.integer :veto, default: 0, null: false
|
||||||
|
t.datetime :last_reminded
|
||||||
|
t.boolean :locked, default: false, null: false
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
add_index :reviewer_votes, [:candidate_id, :user_id], unique: true
|
||||||
|
|
||||||
|
add_column :candidates, :review_status, :integer, default: 0, null: false
|
||||||
|
end
|
||||||
|
end
|
6
db/migrate/20161120175737_init_reviewer_votes.rb
Normal file
6
db/migrate/20161120175737_init_reviewer_votes.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class InitReviewerVotes < ActiveRecord::Migration[5.0]
|
||||||
|
def up
|
||||||
|
Candidate.where(completed: true).each(&:build_reviews)
|
||||||
|
end
|
||||||
|
end
|
15
db/schema.rb
15
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: 20160915164450) do
|
ActiveRecord::Schema.define(version: 20161120175737) 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"
|
||||||
@ -36,6 +36,7 @@ ActiveRecord::Schema.define(version: 20160915164450) do
|
|||||||
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.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
|
||||||
@ -73,6 +74,18 @@ ActiveRecord::Schema.define(version: 20160915164450) do
|
|||||||
t.index ["quiz_id"], name: "index_reviewer_to_quizzes_on_quiz_id", using: :btree
|
t.index ["quiz_id"], name: "index_reviewer_to_quizzes_on_quiz_id", using: :btree
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "reviewer_votes", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
||||||
|
t.integer "candidate_id"
|
||||||
|
t.integer "user_id"
|
||||||
|
t.integer "vote", default: 0, null: false
|
||||||
|
t.integer "veto", default: 0, null: false
|
||||||
|
t.datetime "last_reminded"
|
||||||
|
t.boolean "locked", default: false, 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
|
||||||
|
end
|
||||||
|
|
||||||
create_table "users", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
create_table "users", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.string "email"
|
t.string "email"
|
||||||
|
@ -67,6 +67,18 @@ module Admin
|
|||||||
assert_select 'p', 'foo bar baz'
|
assert_select 'p', 'foo bar baz'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "should post attachment" do
|
||||||
|
question = questions(:fed1)
|
||||||
|
post admin_update_question_url(question.to_i), params: { question:
|
||||||
|
{ quiz_id: quizzes(:fed).to_i, attachment: 'https://dev.perficientdigital.com/logo.png' } }
|
||||||
|
assert_redirected_to admin_question_path(question.to_i)
|
||||||
|
|
||||||
|
get admin_question_path question.to_i
|
||||||
|
assert_select 'img' do
|
||||||
|
assert_select "[src=?]", "https://dev.perficientdigital.com/logo.png"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "should fail to update question" do
|
test "should fail to update question" do
|
||||||
question = questions(:fed9)
|
question = questions(:fed9)
|
||||||
post admin_update_question_url(question.to_i), params: { question: { question: nil } }
|
post admin_update_question_url(question.to_i), params: { question: { question: nil } }
|
||||||
|
@ -19,5 +19,19 @@ module Admin
|
|||||||
assert assigns(:quiz), "@quiz not present"
|
assert assigns(:quiz), "@quiz not present"
|
||||||
assert assigns(:status), "@status not present"
|
assert assigns(:status), "@status not present"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "reviewer can view result for henry" do
|
||||||
|
auth_reviewer
|
||||||
|
|
||||||
|
get admin_result_url(candidates(:henry).test_hash)
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "recruiter can view result for henry" do
|
||||||
|
auth_user users(:recruiter)
|
||||||
|
|
||||||
|
get admin_result_url(candidates(:henry).test_hash)
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true()
|
||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
|
59
test/controllers/admin/vote_controller_test.rb
Normal file
59
test/controllers/admin/vote_controller_test.rb
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class VoteControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
test "reviewer can up vote henry" do
|
||||||
|
auth_user users(:reviewer)
|
||||||
|
henry = candidates(:henry)
|
||||||
|
|
||||||
|
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 down vote henry" do
|
||||||
|
auth_user users(:reviewer)
|
||||||
|
henry = candidates(:henry)
|
||||||
|
|
||||||
|
assert_difference("Candidate.find(#{henry.id}).votes.nay.count", 1) do
|
||||||
|
get admin_down_vote_url(henry.test_hash)
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "reviewer can change vote on henry" do
|
||||||
|
auth_user users(:reviewer)
|
||||||
|
henry = candidates(:henry)
|
||||||
|
get admin_up_vote_url(henry.test_hash)
|
||||||
|
|
||||||
|
assert_difference("Candidate.find(#{henry.id}).votes.yea.count", -1) do
|
||||||
|
assert_difference("Candidate.find(#{henry.id}).votes.nay.count", 1) do
|
||||||
|
get admin_down_vote_url(henry.test_hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "manager can approve henry" do
|
||||||
|
auth_user users(:manager)
|
||||||
|
henry = candidates(:henry)
|
||||||
|
get admin_approve_vote_url(henry.test_hash)
|
||||||
|
|
||||||
|
assert_equal 1, henry.votes.approved.count
|
||||||
|
assert_equal 'approved', Candidate.find(henry.to_i).review_status
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "manager can decline henry" do
|
||||||
|
auth_user users(:manager)
|
||||||
|
henry = candidates(:henry)
|
||||||
|
get admin_decline_vote_url(henry.test_hash)
|
||||||
|
|
||||||
|
assert_equal 1, henry.votes.rejected.count
|
||||||
|
assert_equal 'declined', Candidate.find(henry.to_i).review_status
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
292
test/fixtures/answers.yml
vendored
292
test/fixtures/answers.yml
vendored
@ -404,3 +404,295 @@ juan10:
|
|||||||
created_at: <%= DateTime.now() - 38.hours - 40.minutes %>
|
created_at: <%= DateTime.now() - 38.hours - 40.minutes %>
|
||||||
updated_at: <%= DateTime.now() - 38.hours - 20.minutes %>
|
updated_at: <%= DateTime.now() - 38.hours - 20.minutes %>
|
||||||
|
|
||||||
|
stacy1:
|
||||||
|
candidate: stacy
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
stacy2:
|
||||||
|
candidate: stacy
|
||||||
|
question: fed2
|
||||||
|
answer: 'indexOf()'
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 24.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 4.minutes %>
|
||||||
|
|
||||||
|
stacy3:
|
||||||
|
candidate: stacy
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
stacy4:
|
||||||
|
candidate: stacy
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
stacy5:
|
||||||
|
candidate: stacy
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
stacy6:
|
||||||
|
candidate: stacy
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
stacy7:
|
||||||
|
candidate: stacy
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
stacy8:
|
||||||
|
candidate: stacy
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
stacy9:
|
||||||
|
candidate: stacy
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
stacy10:
|
||||||
|
candidate: stacy
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
|
||||||
|
henry1:
|
||||||
|
candidate: henry
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
henry2:
|
||||||
|
candidate: henry
|
||||||
|
question: fed2
|
||||||
|
answer: 'indexOf()'
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 24.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 4.minutes %>
|
||||||
|
|
||||||
|
henry3:
|
||||||
|
candidate: henry
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
henry4:
|
||||||
|
candidate: henry
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
henry5:
|
||||||
|
candidate: henry
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
henry6:
|
||||||
|
candidate: henry
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
henry7:
|
||||||
|
candidate: henry
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
henry8:
|
||||||
|
candidate: henry
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
henry9:
|
||||||
|
candidate: henry
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
henry10:
|
||||||
|
candidate: henry
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
wade1:
|
||||||
|
candidate: wade
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
wade2:
|
||||||
|
candidate: wade
|
||||||
|
question: fed2
|
||||||
|
answer: 'indexOf()'
|
||||||
|
saved: 0
|
||||||
|
submitted: true
|
||||||
|
created_at: <%= DateTime.now() - 36.hours - 24.minutes %>
|
||||||
|
updated_at: <%= DateTime.now() - 36.hours - 4.minutes %>
|
||||||
|
|
||||||
|
wade3:
|
||||||
|
candidate: wade
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
wade4:
|
||||||
|
candidate: wade
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
wade5:
|
||||||
|
candidate: wade
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
wade6:
|
||||||
|
candidate: wade
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
wade7:
|
||||||
|
candidate: wade
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
wade8:
|
||||||
|
candidate: wade
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
wade9:
|
||||||
|
candidate: wade
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
wade10:
|
||||||
|
candidate: wade
|
||||||
|
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 %>
|
||||||
|
|
||||||
|
45
test/fixtures/candidates.yml
vendored
45
test/fixtures/candidates.yml
vendored
@ -59,6 +59,7 @@ richard: # Richard has completed AND submitted the test
|
|||||||
completed: true
|
completed: true
|
||||||
reminded: false
|
reminded: false
|
||||||
test_hash: 6NjnourLE6Y
|
test_hash: 6NjnourLE6Y
|
||||||
|
review_status: 1
|
||||||
|
|
||||||
juan: # Juan has chosen "finish later" for live coders
|
juan: # Juan has chosen "finish later" for live coders
|
||||||
name: Juan Campbell
|
name: Juan Campbell
|
||||||
@ -68,4 +69,46 @@ juan: # Juan has chosen "finish later" for live coders
|
|||||||
quiz: fed
|
quiz: fed
|
||||||
completed: false
|
completed: false
|
||||||
reminded: true
|
reminded: true
|
||||||
test_hash: <%= CryptSerializer.dump 'qKQo0l4dyol
|
test_hash: qKQo0l4dyol
|
||||||
|
|
||||||
|
stacy: # Stacy has completed AND submitted the test
|
||||||
|
name: Stacy Scott
|
||||||
|
email: <%= CryptSerializer.dump 'stacy.scott@mailinator.com' %>
|
||||||
|
experience: 7-9
|
||||||
|
recruiter: recruiter
|
||||||
|
quiz: fed
|
||||||
|
completed: true
|
||||||
|
reminded: false
|
||||||
|
test_hash: s6oFExZliYYFx
|
||||||
|
review_status: 2
|
||||||
|
|
||||||
|
henry: # Henry has completed AND submitted the test
|
||||||
|
name: Henry Butler
|
||||||
|
email: <%= CryptSerializer.dump 'henry.butler@mailinator.com' %>
|
||||||
|
experience: 4-6
|
||||||
|
recruiter: recruiter
|
||||||
|
quiz: fed
|
||||||
|
completed: true
|
||||||
|
reminded: false
|
||||||
|
test_hash: egPomAuVDeCEp
|
||||||
|
|
||||||
|
wade: # Wade has completed AND submitted the test
|
||||||
|
name: Wade Armstrong
|
||||||
|
email: <%= CryptSerializer.dump 'wade.armstrong@mailinator.com' %>
|
||||||
|
experience: 0-3
|
||||||
|
recruiter: recruiter
|
||||||
|
quiz: fed
|
||||||
|
completed: true
|
||||||
|
reminded: false
|
||||||
|
test_hash: BkSkpapJnkz2N
|
||||||
|
|
||||||
|
gustov: # Gustov is NOT for FED
|
||||||
|
name: Gustov
|
||||||
|
email: <%= CryptSerializer.dump 'gustov@mailinator.com' %>
|
||||||
|
experience: 0-3
|
||||||
|
recruiter: recruiter
|
||||||
|
quiz: admin
|
||||||
|
completed: false
|
||||||
|
reminded: false
|
||||||
|
test_hash: kp6tfghjyapJnkz2N
|
||||||
|
|
||||||
|
2
test/fixtures/questions.yml
vendored
2
test/fixtures/questions.yml
vendored
@ -55,7 +55,7 @@ fed5:
|
|||||||
fed6:
|
fed6:
|
||||||
quiz: fed
|
quiz: fed
|
||||||
question: Comment on how realistic the following image is.
|
question: Comment on how realistic the following image is.
|
||||||
attachment: "http://dev.perficientxd.com/skill_assets/commets_css.jpg"
|
attachment: "https://dev.perficientdigital.com/skills-app-images/commets_css.jpg"
|
||||||
category: CSS
|
category: CSS
|
||||||
input_type: text
|
input_type: text
|
||||||
input_options:
|
input_options:
|
||||||
|
4
test/fixtures/reviewer_to_quizzes.yml
vendored
4
test/fixtures/reviewer_to_quizzes.yml
vendored
@ -7,3 +7,7 @@ one:
|
|||||||
two:
|
two:
|
||||||
user: reviewer2
|
user: reviewer2
|
||||||
quiz: fed
|
quiz: fed
|
||||||
|
|
||||||
|
three:
|
||||||
|
user: manager
|
||||||
|
quiz: fed
|
||||||
|
68
test/fixtures/reviewer_votes.yml
vendored
Normal file
68
test/fixtures/reviewer_votes.yml
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
gustov:
|
||||||
|
candidate: gustov
|
||||||
|
user_id: 12341234
|
||||||
|
|
||||||
|
|
||||||
|
manager_richard:
|
||||||
|
candidate: richard
|
||||||
|
user: manager
|
||||||
|
vote: 1
|
||||||
|
|
||||||
|
reviewer_richard:
|
||||||
|
candidate: richard
|
||||||
|
user: reviewer
|
||||||
|
vote: 1
|
||||||
|
|
||||||
|
reviewer2_richard:
|
||||||
|
candidate: richard
|
||||||
|
user: reviewer2
|
||||||
|
vote: 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
manager_stacy:
|
||||||
|
candidate: stacy
|
||||||
|
user: manager
|
||||||
|
vote: 2
|
||||||
|
|
||||||
|
reviewer_stacy:
|
||||||
|
candidate: stacy
|
||||||
|
user: reviewer
|
||||||
|
vote: 2
|
||||||
|
|
||||||
|
reviewer2_stacy:
|
||||||
|
candidate: stacy
|
||||||
|
user: reviewer2
|
||||||
|
vote: 2
|
||||||
|
|
||||||
|
|
||||||
|
manager_henry:
|
||||||
|
candidate: henry
|
||||||
|
user: manager
|
||||||
|
vote: 0
|
||||||
|
veto: 2
|
||||||
|
|
||||||
|
reviewer_henry:
|
||||||
|
candidate: henry
|
||||||
|
user: reviewer
|
||||||
|
|
||||||
|
reviewer2_henry:
|
||||||
|
candidate: henry
|
||||||
|
user: reviewer2
|
||||||
|
|
||||||
|
|
||||||
|
manager_wade:
|
||||||
|
candidate: wade
|
||||||
|
user: manager
|
||||||
|
vote: 2
|
||||||
|
veto: 2
|
||||||
|
|
||||||
|
reviewer_wade:
|
||||||
|
candidate: wade
|
||||||
|
user: reviewer
|
||||||
|
vote: 2
|
||||||
|
|
||||||
|
reviewer2_wade:
|
||||||
|
candidate: wade
|
||||||
|
user: reviewer2
|
||||||
|
|
@ -6,6 +6,7 @@ class ReviewerMailerTest < ActionMailer::TestCase
|
|||||||
candidate = candidates :dawn
|
candidate = candidates :dawn
|
||||||
mail = ReviewerMailer.candidate_submission candidate
|
mail = ReviewerMailer.candidate_submission candidate
|
||||||
assert_match "Results", mail.subject
|
assert_match "Results", mail.subject
|
||||||
|
assert_match candidate.test_hash, mail.subject
|
||||||
assert_equal candidate.quiz.reviewers.map(&:email), mail.to
|
assert_equal candidate.quiz.reviewers.map(&:email), mail.to
|
||||||
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
|
||||||
|
@ -24,4 +24,11 @@ class CandidateTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
refute_equal email, enc_email
|
refute_equal email, enc_email
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "can build reviewer records" do
|
||||||
|
candidate = candidates(:dawn)
|
||||||
|
|
||||||
|
candidate.build_reviews
|
||||||
|
assert_equal 3, candidate.votes.count
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
34
test/models/reviewer_vote_test.rb
Normal file
34
test/models/reviewer_vote_test.rb
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class ReviewerVoteTest < ActiveSupport::TestCase
|
||||||
|
test "the truth" do
|
||||||
|
assert ReviewerVoteTest
|
||||||
|
end
|
||||||
|
|
||||||
|
test "richard has 3 votes" do
|
||||||
|
richard = candidates(:richard)
|
||||||
|
|
||||||
|
assert_equal 3, richard.votes.size
|
||||||
|
end
|
||||||
|
|
||||||
|
test "manager has 4 votes" do
|
||||||
|
manager = users(:manager)
|
||||||
|
|
||||||
|
assert_equal 4, manager.votes.size
|
||||||
|
end
|
||||||
|
|
||||||
|
test "richard has been approved" do
|
||||||
|
richard = candidates(:richard)
|
||||||
|
|
||||||
|
assert richard.approved?
|
||||||
|
refute richard.declined?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "stacy has been declined" do
|
||||||
|
stacy = candidates(:stacy)
|
||||||
|
|
||||||
|
assert stacy.declined?
|
||||||
|
refute stacy.approved?
|
||||||
|
end
|
||||||
|
end
|
@ -20,14 +20,14 @@ class UserTest < ActiveSupport::TestCase
|
|||||||
refute user.reviewer?
|
refute user.reviewer?
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'manager should act as manager' do
|
test 'manager should act as manager and reviewer' do
|
||||||
user = users(:manager)
|
user = users(:manager)
|
||||||
|
|
||||||
assert user.acts_as_manager?
|
assert user.acts_as_manager?
|
||||||
|
assert user.acts_as_reviewer?
|
||||||
|
|
||||||
refute user.acts_as_admin?
|
refute user.acts_as_admin?
|
||||||
refute user.acts_as_recruiter?
|
refute user.acts_as_recruiter?
|
||||||
refute user.acts_as_reviewer?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'manager should only be manager' do
|
test 'manager should only be manager' do
|
||||||
|
66
test/policies/reviewer_vote_policy_test.rb
Normal file
66
test/policies/reviewer_vote_policy_test.rb
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class ReviewerVotePolicyTest < PolicyAssertions::Test
|
||||||
|
test 'should require current_user' do
|
||||||
|
assert_raise Pundit::NotAuthorizedError do
|
||||||
|
ReviewerVotePolicy.new(nil, ReviewerVote.first).view?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'should allow admin to scope' do
|
||||||
|
scope = ReviewerVotePolicy::Scope.new(users(:admin), ReviewerVote).resolve
|
||||||
|
assert_equal ReviewerVote.count, scope.count
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'should allow manager to scope' do
|
||||||
|
scope = ReviewerVotePolicy::Scope.new(users(:manager), ReviewerVote).resolve
|
||||||
|
assert_equal ReviewerVote.count, scope.count
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'should allow reviewer to scope' do
|
||||||
|
scope = ReviewerVotePolicy::Scope.new(users(:reviewer), ReviewerVote).resolve
|
||||||
|
assert_equal users(:reviewer).votes.count, scope.count
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'should NOT allow recruiter to scope' do
|
||||||
|
scope = ReviewerVotePolicy::Scope.new(users(:recruiter), ReviewerVote).resolve
|
||||||
|
assert_equal 0, scope.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_up
|
||||||
|
assert_permit users(:manager), reviewer_votes(:manager_richard)
|
||||||
|
assert_permit users(:reviewer), reviewer_votes(:reviewer_richard)
|
||||||
|
assert_permit users(:admin), reviewer_votes(:manager_henry)
|
||||||
|
|
||||||
|
refute_permit users(:recruiter), reviewer_votes(:manager_henry)
|
||||||
|
refute_permit users(:reviewer), reviewer_votes(:gustov)
|
||||||
|
refute_permit users(:manager), reviewer_votes(:gustov)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_down
|
||||||
|
assert_permit users(:manager), reviewer_votes(:manager_richard)
|
||||||
|
assert_permit users(:reviewer), reviewer_votes(:reviewer_richard)
|
||||||
|
assert_permit users(:admin), reviewer_votes(:manager_henry)
|
||||||
|
|
||||||
|
refute_permit users(:recruiter), reviewer_votes(:manager_henry)
|
||||||
|
refute_permit users(:reviewer), reviewer_votes(:gustov)
|
||||||
|
refute_permit users(:manager), reviewer_votes(:gustov)
|
||||||
|
end
|
||||||
|
|
||||||
|
def approve
|
||||||
|
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(:admin), reviewer_votes(:manager_henry)
|
||||||
|
|
||||||
|
refute_permit users(:recruiter), reviewer_votes(:manager_henry)
|
||||||
|
refute_permit users(:reviewer), reviewer_votes(:reviewer_richard)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user