Great Expectations, fixes SD-2065

Find/fix/prevent weird stuff we are doing (or not doing) with `expect`.

Chapter I:

    "I have been bent and broken, but - I hope - into a better shape."

Disallow `expect`s in a `before`. `before` is by definition before the
test, so it's not the place to be asserting anything. You can of course
still set up mocks there.

Chapter II:

    "Take nothing on its looks; take everything on evidence. There's no
    better rule."

Ensure every `expect` actually runs. i.e. it needs `to` or `not_to` in
order to actually do anything.

Chapter III:

    "Ask no questions, and you'll be told no lies."

In selenium land, ensure every spec has at least one expecation. In
regular rspec land we just warn to stderr, but we may get more strict
in the future.

Test Plan: Specs

Change-Id: I5fc353ee8171e5191853d45f42ae42478f9220b4
Reviewed-on: https://gerrit.instructure.com/101224
Tested-by: Jenkins
Reviewed-by: Rob Orton <rob@instructure.com>
Reviewed-by: Landon Wilkins <lwilkins@instructure.com>
Product-Review: Jon Jensen <jon@instructure.com>
QA-Review: Jon Jensen <jon@instructure.com>
This commit is contained in:
Jon Jensen 2017-02-06 17:46:06 -07:00
parent 09d187e470
commit fb3966a62d
85 changed files with 391 additions and 416 deletions

View File

@ -258,8 +258,6 @@ Implemented for: Canvas LMS}
'', ['itemType', 'course'],
['itemID', @course.to_param],
['clearState', ''])
expect(status).to eq "Success"
@mock_migration = ContentMigration.create!(context: @course)
def @mock_migration.export_content
self.workflow_state = 'importing'
@ -271,8 +269,6 @@ Implemented for: Canvas LMS}
'PublishServerItem', 'nobody@example.com', 'asdfasdf', context,
['itemType', 'quiz'], ['itemName', 'my quiz'], ['uploadType', 'zipPackage'],
['fileName', 'import.zip'], ['fileData', 'pretend this is a zip file'])
expect(status).to eq "Success"
expect(item_id).to eq 'pending'
@token = context
end

View File

@ -52,23 +52,6 @@ def api_call(method, path, params, body_params = {}, headers = {}, opts = {})
raw_api_call(method, path, params, body_params, headers, opts)
if opts[:expected_status]
assert_status(opts[:expected_status])
else
unless response.success?
error_message = response.body
begin
json = JSON.parse(response.body)
error_report_id = json['error_report_id']
error_report = ErrorReport.find_by(id: error_report_id) if error_report_id
if error_report
error_message << "\n"
error_message << error_report.message
error_message << "\n"
error_message << error_report.backtrace
end
rescue JSON::ParserError
end
end
expect(response).to be_success, error_message
end
if response.headers['Link']
@ -76,14 +59,9 @@ def api_call(method, path, params, body_params = {}, headers = {}, opts = {})
Api.parse_pagination_links(response.headers['Link'])
end
if jsonapi_call?(headers) && method == :delete
assert_status(204)
return
end
case params[:format]
when 'json'
expect(response.header[content_type_key]).to eq 'application/json; charset=utf-8'
raise "got non-json" unless response.header[content_type_key] == 'application/json; charset=utf-8'
body = response.body
if body.respond_to?(:call)
@ -133,7 +111,7 @@ def raw_api_call(method, path, params, body_params = {}, headers = {}, opts = {}
enable_forgery_protection do
route_params = params_from_with_nesting(method, path)
route_params.each do |key, value|
expect(params[key].to_s).to eq(value.to_s), lambda{ "Expected value of params[\'#{key}\'] to equal #{value}, actual: #{params[key]}"}
raise "Expected value of params[\'#{key}\'] to equal #{value}, actual: #{params[key]}" unless params[key].to_s == value.to_s
end
if @use_basic_auth
user_session(@user)

View File

@ -588,7 +588,6 @@ describe "API Authentication", type: :request do
user_with_pseudonym(:active_user => true, :username => 'test1@example.com', :password => 'test1234')
course_with_teacher(:user => @user)
@token = @user.access_tokens.create!
expect(@token.full_token).not_to be_nil
end
def check_used

View File

@ -1477,7 +1477,7 @@ describe AssignmentsApiController, type: :request do
api_call_to_update_adhoc_override(student_ids: [@student.id])
ao = @assignment.assignment_overrides.where(set_type: 'ADHOC').first
expect(AssignmentOverrideStudent.count ==1)
expect(AssignmentOverrideStudent.count).to eq 1
end
it 'allows the update of an adhoc override with different student' do
@ -3246,7 +3246,6 @@ describe AssignmentsApiController, type: :request do
@tool_tag.save!
@assignment.submission_types = 'external_tool'
@assignment.save!
expect(@assignment.external_tool_tag).not_to be_nil
end
before :each do

View File

@ -321,7 +321,6 @@ describe "Modules API", type: :request do
:course_id => @course.to_param }
@test_modules = (1..4).map { |x| @course.context_modules.create! :name => "test module #{x}" }
@test_modules[2..3].each { |m| m.update_attribute(:workflow_state , 'unpublished') }
expect(@test_modules.map { |tm| tm.workflow_state }).to eq %w(active active unpublished unpublished)
@modules_to_update = [@test_modules[1], @test_modules[3]]
@wiki_page = @course.wiki.wiki_pages.create(:title => 'Wiki Page Title')

View File

@ -1763,9 +1763,10 @@ describe DiscussionTopicsController, type: :request do
RoleOverride.create!(:context => @course.account, :permission => 'read_forum',
:role => observer_role, :enabled => false)
expect { api_call(:get, "/api/v1/courses/#{@course.id}/discussion_topics.json",
{:controller => 'discussion_topics', :action => 'index', :format => 'json',
:course_id => @course.id.to_s}) }.to raise_error
api_call(:get, "/api/v1/courses/#{@course.id}/discussion_topics.json",
{:controller => 'discussion_topics', :action => 'index', :format => 'json',
:course_id => @course.id.to_s})
expect(response).to be_client_error
end
end
@ -2492,7 +2493,6 @@ describe DiscussionTopicsController, type: :request do
before :each do
course_with_teacher(active_all: true, is_public: true) # sets @teacher and @course
expect(@course.is_public).to be_truthy
account_admin_user(account: @course.account) # sets @admin
@student1 = student_in_course(active_all: true).user
@student2 = student_in_course(active_all: true).user

View File

@ -212,7 +212,7 @@ describe "Favorites API", type: :request do
expect(@user.favorites.size).to eql(0)
json = api_call(:get, "/api/v1/users/self/favorites/groups",
:controller=>"favorites", :action=>"list_favorite_groups", :format=>"json")
expect(json.any?)
expect(json.any?).to be
end
end

View File

@ -66,8 +66,7 @@ describe 'Moderated Grades API', type: :request do
@parsed_json = api_call :post,
"/api/v1/courses/#{@course.id}/assignments/#{@assignment.id}/moderated_students",
{controller: 'moderation_set', action: 'create',
format: 'json', course_id: @course.id, assignment_id: @assignment.id}, {}, {},
{ expected_status: 400 }
format: 'json', course_id: @course.id, assignment_id: @assignment.id}, {}, {}
end
it 'responds with a bad request' do

View File

@ -663,23 +663,12 @@ describe "Outcomes API", type: :request do
context "mastery calculations" do
context "not allow updating the outcome after being used for assessing" do
before :each do
expect(@outcome).not_to be_assessed
@outcome.calculation_method = 'decaying_average'
@outcome.calculation_int = 62
@outcome.save!
@outcome.reload
expect(@outcome.calculation_method).to eq('decaying_average')
expect(@outcome.calculation_int).to eq(62)
assess_outcome(@outcome)
expect(@outcome).to be_assessed
# make sure that the process of getting assessed didn't change
# the things our tests expect to be true
expect(@outcome.calculation_method).to eq('decaying_average')
expect(@outcome.calculation_int).to eq(62)
end
let(:update_outcome_api) do

View File

@ -183,10 +183,6 @@ describe 'Provisional Grades API', type: :request do
@submission = @assignment.submit_homework(@student, :body => "hello")
@submission.add_comment(author: @ta, provisional: true, comment: 'A provisional comment')
@provisional_grade = @submission.provisional_grades.first
expect(@submission.workflow_state).to eq('submitted')
expect(@submission.grade).to be_nil
expect(@provisional_grade.graded_at).to be_nil
end
it 'publishes an empty provisional grade for an active student' do

View File

@ -184,14 +184,11 @@ describe Quizzes::QuizQuestionsController, type: :request do
end
context "non-existent question" do
before do
@json = api_call(:get, "/api/v1/courses/#{@course.id}/quizzes/#{@quiz.id}/questions/9034831",
{:controller => "quizzes/quiz_questions", :action => "show", :format => "json", :course_id => @course.id.to_s, :quiz_id => @quiz.id.to_s, :id => "9034831"},
{}, {}, {:expected_status => 404})
end
it "should return a not found error message" do
expect(@json.inspect).to include "does not exist"
json = api_call(:get, "/api/v1/courses/#{@course.id}/quizzes/#{@quiz.id}/questions/9034831",
{:controller => "quizzes/quiz_questions", :action => "show", :format => "json", :course_id => @course.id.to_s, :quiz_id => @quiz.id.to_s, :id => "9034831"},
{}, {}, {:expected_status => 404})
expect(json.inspect).to include "does not exist"
end
end
end

View File

@ -241,14 +241,11 @@ describe Quizzes::QuizzesApiController, type: :request do
end
context "non-existent quiz" do
before do
@json = api_call(:get, "/api/v1/courses/#{@course.id}/quizzes/10101",
{:controller=>"quizzes/quizzes_api", :action=>"show", :format=>"json", :course_id=>"#{@course.id}", :id => "10101"},
{}, {}, {:expected_status => 404})
end
it "should return a not found error message" do
expect(@json.inspect).to include "does not exist"
json = api_call(:get, "/api/v1/courses/#{@course.id}/quizzes/10101",
{:controller=>"quizzes/quizzes_api", :action=>"show", :format=>"json", :course_id=>"#{@course.id}", :id => "10101"},
{}, {}, {:expected_status => 404})
expect(json.inspect).to include "does not exist"
end
end
end

View File

@ -315,7 +315,7 @@ describe UserObserveesController, type: :request do
unique_id: student_pseudonym.unique_id,
password: student_pseudonym.password,
}
expect(create_call({observee: observee}, api_user: student, expected_status: 401))
create_call({observee: observee}, api_user: student, expected_status: 401)
expect(student.reload.observed_users).to eq []
end

View File

@ -33,8 +33,6 @@ describe AssignmentsController do
:assignment_group => @group,
:due_at => Time.zone.now + 1.week
)
expect(@assignment.assignment_group).to eql(@group)
expect(@group.assignments).to be_include(@assignment)
@assignment
end

View File

@ -1473,7 +1473,6 @@ describe CoursesController do
Assignment.where(:id => @assignment).update_all(:updated_at => @time)
@assignment.reload
expect(@assignment.updated_at).to eq @time
end
it "should touch content when is_public is updated" do

View File

@ -389,10 +389,6 @@ describe Login::SamlController do
)
controller.request.env['canvas.domain_root_account'] = @account
post :create, :SAMLResponse => "foo", :RelayState => "/courses"
expect(response).to redirect_to(courses_url)
expect(session[:saml_unique_id]).to eq @unique_id
expect(session[:login_aac]).to eq @aac2.id
end
describe '#destroy' do

View File

@ -452,10 +452,6 @@ describe ApplicationHelper do
end
describe "hidden dialogs" do
before do
expect(hidden_dialogs).to be_empty
end
it "should generate empty string when there are no dialogs" do
str = render_hidden_dialogs
expect(str).to eq ''

View File

@ -277,14 +277,14 @@ describe GradebooksHelper do
describe '#percentage?' do
it 'returns true if given grade is a percentage' do
expect(helper.percentage?('42%'))
expect(helper.percentage?('42.32%'))
expect(helper.percentage?('42%')).to eq true
expect(helper.percentage?('42.32%')).to eq true
end
it 'returns false if given grade is not a percentage' do
expect(helper.percentage?('42'))
expect(helper.percentage?('42.32'))
expect(helper.percentage?('A'))
expect(helper.percentage?('42')).to eq false
expect(helper.percentage?('42.32')).to eq false
expect(helper.percentage?('A')).to eq false
end
end

View File

@ -41,14 +41,12 @@ describe "concluded/unconcluded courses" do
@group = @course.assignment_groups.create!(:name => "default")
@assignment = @course.assignments.create!(:submission_types => 'online_quiz', :title => 'quiz assignment', :assignment_group => @group)
@quiz = @assignment.reload.quiz
expect(@quiz).not_to be_nil
@qsub = Quizzes::SubmissionManager.new(@quiz).find_or_create_submission(@student)
@qsub.quiz_data = [{:correct_comments=>"", :assessment_question_id=>nil, :incorrect_comments=>"", :question_name=>"Question 1", :points_possible=>1, :question_text=>"Which book(s) are required for this course?", :name=>"Question 1", :id=>128, :answers=>[{:weight=>0, :text=>"A", :comments=>"", :id=>1490}, {:weight=>0, :text=>"B", :comments=>"", :id=>1020}, {:weight=>0, :text=>"C", :comments=>"", :id=>7051}], :question_type=>"multiple_choice_question"}]
@qsub.submission_data = [{:points=>0, :text=>"7051", :question_id=>128, :correct=>false, :answer_id=>7051}]
@qsub.workflow_state = 'complete'
@qsub.save!
@sub = @qsub.submission
expect(@sub).not_to be_nil
end
it "should let the teacher change grades in the speed grader by default" do

View File

@ -42,12 +42,13 @@ describe "discussion_topics" do
end
it "should not allow concluded students to update topic" do
student_enrollment = course_with_student(:course => @course, :user => @user, :active_enrollment => true)
student_enrollment = course_with_student(:course => @course, :active_all => true)
@topic = DiscussionTopic.new(:context => @course, :title => "will this work?", :user => @user)
@topic.save!
expect(@topic.grants_right?(@user, :update))
expect(@topic.grants_right?(@user, :update)).to be
student_enrollment.send("conclude")
expect(!@topic.grants_right?(@user, :update))
AdheresToPolicy::Cache.clear
expect(@topic.grants_right?(@user, :update)).not_to be
end
it "should allow teachers to edit concluded students topics" do
@ -55,9 +56,10 @@ describe "discussion_topics" do
student_enrollment = course_with_student(:course => @course, :user => @student, :active_enrollment => true)
@topic = DiscussionTopic.new(:context => @course, :title => "will this work?", :user => @student)
@topic.save!
expect(@topic.grants_right?(@teacher, :update))
expect(@topic.grants_right?(@teacher, :update)).to be
student_enrollment.send("conclude")
expect(@topic.grants_right?(@teacher, :update))
AdheresToPolicy::Cache.clear
expect(@topic.grants_right?(@teacher, :update)).to be
end
it "should show speed grader button" do

View File

@ -31,6 +31,9 @@ describe "one time passwords" do
context "mid-login" do
before do
post '/login/canvas', :pseudonym_session => { :unique_id => @pseudonym.unique_id, :password => 'qwertyuiop' }
end
it "should redirect" do
expect(response).to redirect_to(otp_login_url)
end

View File

@ -35,7 +35,6 @@ describe "QuizRegrading" do
course_with_student_logged_in(active_all: true)
quiz_model(course: @course)
@regrade = @quiz.quiz_regrades.where(quiz_id: @quiz.id, quiz_version: @quiz.version_number).first_or_create(user: @student)
expect(@regrade).not_to be_new_record
@true_false_question = create_quiz_question!({
:points_possible => 1,
:question_type => 'true_false_question',
@ -73,7 +72,6 @@ describe "QuizRegrading" do
@submission = @quiz.generate_submission(@student)
reset_submission_data!
@submission.save!
expect(@submission.score).to eq 0.5
end
it 'succesfully regrades the submissions and updates the scores' do

View File

@ -160,10 +160,7 @@ describe GradeCalculator do
describe "group with no grade or muted grade" do
before(:each) do
two_groups_two_assignments(50, 10, 50, 10)
expect(@user.enrollments.first.computed_current_score).to eql(nil)
expect(@user.enrollments.first.computed_final_score).to eql(0.0)
@submission = @assignment.grade_student(@user, grade: "5", grader: @teacher)
expect(@submission[0].score).to eql(5.0)
end
it "should ignore no grade for current grade calculation, even when weighted" do

View File

@ -27,9 +27,7 @@ describe Turnitin::Client do
end
def turnitin_submission
expects_job_with_tag('Submission#submit_to_turnitin') do
@submission = @assignment.submit_homework(@user, :submission_type => 'online_upload', :attachments => [attachment_model(:context => @user, :content_type => 'text/plain')])
end
@submission = @assignment.submit_homework(@user, :submission_type => 'online_upload', :attachments => [attachment_model(:context => @user, :content_type => 'text/plain')])
@submission.reload
end

View File

@ -989,7 +989,6 @@ describe Account do
before do
account.authentication_providers.scope.delete_all
expect(account.delegated_authentication?).to eq false
end
it "is false for LDAP" do

View File

@ -577,8 +577,6 @@ describe ActiveRecord::Base do
@u4 = User.create!(name: 'b')
@us = [@u1, @u2, @u3, @u4]
# for sanity
expect(User.where(id: @us, name: nil).order(:id).all).to eq [@u1, @u3]
end
it "should sort nulls first" do

View File

@ -179,7 +179,7 @@ describe Assignment do
end
it 'returns a jwt' do
expect(Canvas::Security.decode_jwt @assignment.secure_params)
expect(Canvas::Security.decode_jwt(@assignment.secure_params)).to be
end
end
@ -4036,87 +4036,80 @@ describe Assignment do
end
end
end
end
def setup_assignment_with_group
assignment_model(:group_category => "Study Groups", :course => @course)
@group = @a.context.groups.create!(:name => "Study Group 1", :group_category => @a.group_category)
@u1 = @a.context.enroll_user(User.create(:name => "user 1")).user
@u2 = @a.context.enroll_user(User.create(:name => "user 2")).user
@u3 = @a.context.enroll_user(User.create(:name => "user 3")).user
@group.add_user(@u1)
@group.add_user(@u2)
@assignment.reload
end
def setup_assignment_without_submission
assignment_model(:course => @course)
@assignment.reload
end
def setup_assignment_with_homework
setup_assignment_without_submission
res = @assignment.submit_homework(@user, {:submission_type => 'online_text_entry', :body => 'blah'})
expect(res).not_to be_nil
expect(res).to be_is_a(Submission)
@assignment.reload
end
def setup_assignment_with_students
@graded_notify = Notification.create!(:name => "Submission Graded")
@grade_change_notify = Notification.create!(:name => "Submission Grade Changed")
@stu1 = @student
@course.enroll_student(@stu2 = user_factory)
@assignment = @course.assignments.create(:title => "asdf", :points_possible => 10)
@sub1 = @assignment.grade_student(@stu1, grade: 9, grader: @teacher).first
expect(@sub1.score).to eq 9
# Took this out until it is asked for
# @sub1.published_score.should_not == @sub1.score
expect(@sub1.published_score).to eq @sub1.score
@assignment.reload
expect(@assignment.submissions).to be_include(@sub1)
end
def submit_homework(student)
file_context = @assignment.group_category.group_for(student) if @assignment.has_group_category?
file_context ||= student
a = Attachment.create! context: file_context,
filename: "homework.pdf",
uploaded_data: StringIO.new("blah blah blah")
@assignment.submit_homework(student, attachments: [a],
submission_type: "online_upload")
a
end
def zip_submissions
zip = Attachment.new filename: 'submissions.zip'
zip.user = @teacher
zip.workflow_state = 'to_be_zipped'
zip.context = @assignment
zip.save!
ContentZipper.process_attachment(zip, @teacher)
raise "zip failed" if zip.workflow_state != "zipped"
zip
end
def setup_differentiated_assignments(opts={})
if !opts[:course]
course_with_teacher(active_all: true)
def setup_assignment_with_group
assignment_model(:group_category => "Study Groups", :course => @course)
@group = @a.context.groups.create!(:name => "Study Group 1", :group_category => @a.group_category)
@u1 = @a.context.enroll_user(User.create(:name => "user 1")).user
@u2 = @a.context.enroll_user(User.create(:name => "user 2")).user
@u3 = @a.context.enroll_user(User.create(:name => "user 3")).user
@group.add_user(@u1)
@group.add_user(@u2)
@assignment.reload
end
@section1 = @course.course_sections.create!(name: 'Section One')
@section2 = @course.course_sections.create!(name: 'Section Two')
if opts[:ta]
@ta = course_with_ta(course: @course, active_all: true).user
def setup_assignment_without_submission
assignment_model(:course => @course)
@assignment.reload
end
@student1, @student2, @student3 = create_users(3, return_type: :record)
student_in_section(@section1, user: @student1)
student_in_section(@section2, user: @student2)
def setup_assignment_with_homework
setup_assignment_without_submission
res = @assignment.submit_homework(@user, {:submission_type => 'online_text_entry', :body => 'blah'})
@assignment.reload
end
@assignment = assignment_model(course: @course, submission_types: "online_url", workflow_state: "published")
@override_s1 = differentiated_assignment(assignment: @assignment, course_section: @section1)
@override_s1.due_at = 1.day.from_now
@override_s1.save!
def setup_assignment_with_students
@graded_notify = Notification.create!(:name => "Submission Graded")
@grade_change_notify = Notification.create!(:name => "Submission Grade Changed")
@stu1 = @student
@course.enroll_student(@stu2 = user_factory)
@assignment = @course.assignments.create(:title => "asdf", :points_possible => 10)
@sub1 = @assignment.grade_student(@stu1, grade: 9, grader: @teacher).first
@assignment.reload
end
def submit_homework(student)
file_context = @assignment.group_category.group_for(student) if @assignment.has_group_category?
file_context ||= student
a = Attachment.create! context: file_context,
filename: "homework.pdf",
uploaded_data: StringIO.new("blah blah blah")
@assignment.submit_homework(student, attachments: [a],
submission_type: "online_upload")
a
end
def zip_submissions
zip = Attachment.new filename: 'submissions.zip'
zip.user = @teacher
zip.workflow_state = 'to_be_zipped'
zip.context = @assignment
zip.save!
ContentZipper.process_attachment(zip, @teacher)
raise "zip failed" if zip.workflow_state != "zipped"
zip
end
def setup_differentiated_assignments(opts={})
if !opts[:course]
course_with_teacher(active_all: true)
end
@section1 = @course.course_sections.create!(name: 'Section One')
@section2 = @course.course_sections.create!(name: 'Section Two')
if opts[:ta]
@ta = course_with_ta(course: @course, active_all: true).user
end
@student1, @student2, @student3 = create_users(3, return_type: :record)
student_in_section(@section1, user: @student1)
student_in_section(@section2, user: @student2)
@assignment = assignment_model(course: @course, submission_types: "online_url", workflow_state: "published")
@override_s1 = differentiated_assignment(assignment: @assignment, course_section: @section1)
@override_s1.due_at = 1.day.from_now
@override_s1.save!
end
end

View File

@ -1,25 +0,0 @@
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
describe "Broadcast Policies" do
it "should send a notification when a new announcement is created" do
end
end

View File

@ -401,7 +401,7 @@ describe ConditionalRelease::Service do
context 'assignment data' do
before(:each) do
allow(Service).to receive(:enabled_in_context?).and_return(true)
expect(CanvasHttp).to receive(:get).once.
allow(CanvasHttp).to receive(:get).once.
and_return(double({ code: '200', body: default_rules.to_json }))
end
@ -530,7 +530,7 @@ describe ConditionalRelease::Service do
it 'returns a list of rules' do
expect_cyoe_request '200', @a1
expect(rules.length > 0)
expect(rules.length).to be > 0
expect(models0).to eq [@a1]
end

View File

@ -40,8 +40,6 @@ shared_examples_for "course copy" do
yield(export) if block_given?
export.save
export.export_course
expect(export.workflow_state).to eq 'exported'
expect(export.attachment_id).not_to be_nil
export
end

View File

@ -24,10 +24,6 @@ describe ContextExternalTool do
@root_account = @course.root_account
@account = account_model(:root_account => @root_account, :parent_account => @root_account)
@course.update_attribute(:account, @account)
expect(@course.account).to eql(@account)
expect(@course.root_account).to eql(@root_account)
expect(@account.parent_account).to eql(@root_account)
expect(@account.root_account).to eql(@root_account)
end
describe '#content_migration_configured?' do

View File

@ -1050,9 +1050,6 @@ describe ContextModule do
{id: @other_assignment_tag.id, type: 'min_score', min_score: 90},
]
@module.save!
expect(@module.completion_requirements.include?({id: @assignment_tag.id, type: 'min_score', min_score: 90})).to be_truthy
expect(@module.completion_requirements.include?({id: @other_assignment_tag.id, type: 'min_score', min_score: 90})).to be_truthy
end
it 'should not prevent a student from completing a module' do

View File

@ -821,10 +821,6 @@ describe Conversation do
ConversationMessageParticipant.update_all "tags = NULL"
@conversation = Conversation.find(@conversation.id)
expect(@conversation.tags).to eql []
expect(@u1.conversations.first.tags).to eql []
expect(@u2.conversations.first.tags).to eql []
expect(@u3.conversations.first.tags).to eql []
end
it "should set the default tags when migrating" do

View File

@ -39,7 +39,6 @@ class Enrollment
before(:once) { course_with_student(:active_all => 1) }
let(:recent_activity) { Enrollment::RecentActivity.new(@enrollment) }
let(:now){ Time.zone.now }
before(:each){ expect(@enrollment.last_activity_at).to be_nil }
describe "#record!" do
it "should record on the first call (last_activity_at is nil)" do

View File

@ -992,7 +992,6 @@ describe Enrollment do
describe 'term dates' do
before do
@term = @course.enrollment_term
expect(@term).to_not be_nil
end
it "should return active" do
@ -1042,7 +1041,6 @@ describe Enrollment do
describe 'enrollment_dates_override dates' do
before do
@term = @course.enrollment_term
expect(@term).to_not be_nil
@override = @term.enrollment_dates_overrides.create!(:enrollment_type => @enrollment.type, :enrollment_term => @term)
end
@ -1099,7 +1097,6 @@ describe Enrollment do
describe 'section dates' do
before do
@section = @course.course_sections.first
expect(@section).to_not be_nil
@section.restrict_enrollments_to_section_dates = true
end
@ -1915,8 +1912,8 @@ describe Enrollment do
describe "effective_start_at" do
before :once do
course_with_student(:active_all => true)
expect(@term = @course.enrollment_term).not_to be_nil
expect(@section = @enrollment.course_section).not_to be_nil
@term = @course.enrollment_term
@section = @enrollment.course_section
# 7 different possible times, make sure they're distinct
@enrollment_date_start_at = 7.days.ago
@ -1974,8 +1971,8 @@ describe Enrollment do
describe "effective_end_at" do
before :once do
course_with_student(:active_all => true)
expect(@term = @course.enrollment_term).not_to be_nil
expect(@section = @enrollment.course_section).not_to be_nil
@term = @course.enrollment_term
@section = @enrollment.course_section
# 5 different possible times, make sure they're distinct
@enrollment_date_end_at = 1.days.ago

View File

@ -235,7 +235,6 @@ describe ModeratedGrading::ProvisionalGrade do
:assessment => { :assessment_type => 'grading',
:"criterion_#{@rubric.criteria_object.first.id}" => { :points => 3, :comments => "wat" } })
@prov_comment = @sub.add_comment(:commenter => @scorer, :comment => 'blah', :provisional => true)
expect(@prov_comment.provisional_grade_id).to eq @pg.id
end
def test_copy_to_final_mark

View File

@ -184,7 +184,6 @@ describe Quizzes::QuizQuestionLinkMigrator do
context "with assessment questions" do
before :once do
@question = @quiz.quiz_questions.create!(:question_data => {:question_type => :multiple_choice})
expect(@question.assessment_question).not_to be_nil
end
let_once(:file) do

View File

@ -2038,10 +2038,6 @@ describe Quizzes::Quiz do
end
context 'assignment is not locked' do
before do
expect(assignment_lock_info).not_to be_present
end
it { is_expected.to be false }
end

View File

@ -31,21 +31,17 @@ describe Quizzes::QuizSubmissionHistory do
@submission.score = 5.0
@submission.attempt = 1
@submission.with_versioning(true, &:save!)
expect(@submission.version_number).to eql(1)
expect(@submission.score).to eql(5.0)
# regrade 1
@submission.score_before_regrade = 5.0
@submission.score = 4.0
@submission.attempt = 1
@submission.with_versioning(true, &:save!)
expect(@submission.version_number).to eql(2)
# new attempt
@submission.score = 3.0
@submission.attempt = 2
@submission.with_versioning(true, &:save!)
expect(@submission.version_number).to eql(3)
end
describe "#initialize" do

View File

@ -285,7 +285,7 @@ describe Quizzes::QuizSubmissionService do
context 'as a teacher' do
before :each do
expect(qs).to receive(:grants_right?).with(participant.user, :update_scores).and_return true
allow(qs).to receive(:grants_right?).with(participant.user, :update_scores).and_return true
end
def expect_version_object
@ -373,7 +373,7 @@ describe Quizzes::QuizSubmissionService do
context 'as someone else' do
before :each do
expect(qs).to receive(:grants_right?).with(participant.user, :update_scores).and_return false
allow(qs).to receive(:grants_right?).with(participant.user, :update_scores).and_return false
end
it 'should deny access' do

View File

@ -1135,13 +1135,11 @@ describe Quizzes::QuizSubmission do
s.score = 10
s.save(:validate => false)
end
expect(submission.version_number).to eq 1
submission.with_versioning(true) do |s|
s.score = 15
s.save(:validate => false)
end
expect(submission.version_number).to eq 2
end
it "updates a previous version given current attributes" do
@ -1350,8 +1348,6 @@ describe Quizzes::QuizSubmission do
@submission.score = 5.0
@submission.attempt = 1
@submission.with_versioning(true, &:save!)
expect(@submission.version_number).to eql(1)
expect(@submission.score).to eql(5.0)
@submission.save
end

View File

@ -40,13 +40,13 @@ describe Setting do
context "setting" do
it 'should set values as strings' do
expect(Setting.set('my_new_setting', true))
Setting.set('my_new_setting', true)
expect(Setting.get('my_new_setting', '1')).to eq 'true'
end
it 'should set values as strings' do
time = Time.now.utc
expect(Setting.set('my_new_setting', time))
Setting.set('my_new_setting', time)
expect(Setting.get('my_new_setting', '1')).to eq time.to_s
end
end

View File

@ -40,14 +40,11 @@ describe SisBatch do
end
end
old_job_count = sis_jobs.count
batch = File.open(path, 'rb') do |tmp|
# arrrgh attachment.rb
def tmp.original_filename; File.basename(path); end
SisBatch.create_with_attachment(@account, 'instructure_csv', tmp, @user || user_factory)
end
# SisBatches shouldn't need any background processing
expect(sis_jobs.count).to eq old_job_count
yield batch if block_given?
batch
end
@ -153,7 +150,6 @@ test_1,TC 101,Test Course 101,,term1,deleted
}
before do
expect(@account).to respond_to(:update_account_associations)
track_jobs { job.reschedule }
end

View File

@ -24,14 +24,9 @@ describe StudentEnrollment do
@student = User.create(:name => "some student")
@course = Course.create(:name => "some course")
@se = @course.enroll_student(@student)
expect(@se.user_id).to eql(@student.id)
expect(@course.students).to include(@student)
@assignment = @course.assignments.create!(:title => 'some assignment')
expect(@course.assignments).to include(@assignment)
@submission = @assignment.submit_homework(@student)
@assignment.reload
expect(@submission).not_to be_nil
expect(@assignment.submissions.to_a).to eql([@submission])
@course.save!
@se = @course.student_enrollments.first
end

View File

@ -24,14 +24,9 @@ describe StudentViewEnrollment do
@student = User.create(:name => "some student")
@course = Course.create(:name => "some course")
@se = @course.enroll_student(@student)
expect(@se.user_id).to eql(@student.id)
expect(@course.students).to include(@student)
@assignment = @course.assignments.create!(:title => 'some assignment')
expect(@course.assignments).to include(@assignment)
@submission = @assignment.submit_homework(@student)
@assignment.reload
expect(@submission).not_to be_nil
expect(@assignment.submissions.to_a).to eql([@submission])
@course.save!
@se = @course.student_enrollments.first
end

View File

@ -2454,12 +2454,9 @@ describe Submission do
expect(Submission.needs_grading.count).to eq(0)
end
end
end
def submission_spec_model(opts={})
@submission = Submission.new(@valid_attributes.merge(opts))
expect(@submission.assignment).to eql(@assignment)
expect(@assignment.context).to eql(@context)
expect(@submission.assignment.context).to eql(@context)
@submission.save!
def submission_spec_model(opts={})
@submission = Submission.new(@valid_attributes.merge(opts))
@submission.save!
end
end

View File

@ -132,7 +132,6 @@ describe UserObserver do
@observer = user_with_pseudonym
student.observers << @observer
@observer_enrollment = @observer.enrollments.where(type: 'ObserverEnrollment', course_id: @course, associated_user_id: student).first
expect(@observer_enrollment).not_to be_nil
end
it "should not attempt to add a duplicate observer enrollment" do

View File

@ -117,7 +117,7 @@ describe "add_people" do
# check the checkbox
f('label[for="limit_privileges_to_course_section"]').click
expect(f('#limit_privileges_to_course_section').selected?)
expect(f('#limit_privileges_to_course_section')).to be_selected
# cancel the dialog
f('#addpeople_cancel').click
@ -129,7 +129,7 @@ describe "add_people" do
# check the checkbox again
f('label[for="limit_privileges_to_course_section"]').click
expect(f('#limit_privileges_to_course_section').selected?)
expect(f('#limit_privileges_to_course_section')).to be_selected
end
end

View File

@ -241,8 +241,8 @@ describe "admin settings tab" do
before(:each) do
f("#enable_equella").click
expect(is_checked("#enable_equella")).to be_truthy
end
it "should add an equella feature" do
add_equella_feature
end

View File

@ -41,13 +41,10 @@ describe "site admin jobs ui" do
all_jobs.each { |job| expect(job).to have_class('selected') }
end
def first_jobs_cell_displayed?
expect(f('#jobs-grid .slick-cell')).to be
end
def load_jobs_page
get "/jobs"
first_jobs_cell_displayed?
# wait for it
f('#jobs-grid .slick-cell')
end
def filter_jobs(job_flavor_text)

View File

@ -46,8 +46,6 @@ describe "admin_tools" do
def click_view_tab(tab_name)
wait_for_ajaximations
tab = fj("#adminToolsTabs .#{tab_name} > a")
expect(tab).not_to be_nil
expect(tab).to be_displayed
tab.click
wait_for_ajaximations
end

View File

@ -71,7 +71,7 @@ describe "collaborations" do
user_session(@student)
get "/courses/#{@course.id}/collaborations"
ff('#collaborations .collaboration').length == 1
expect(ff('#collaborations .collaboration')).to have_size(1)
end
end
end

View File

@ -66,14 +66,14 @@ describe "collaborations" do
all_icons.last.click
driver.switch_to.alert.accept
wait_for_ajaximations
expect(check_element_has_focus(all_icons.first))
expect(check_element_has_focus(all_icons.first)).to be
end
it 'should set focus to the add collaboration button if there are no previous collaborations' do
f('.delete_collaboration_link').click
driver.switch_to.alert.accept
wait_for_ajaximations
expect(check_element_has_focus(f('.add_collaboration_link')))
expect(check_element_has_focus(f('.add_collaboration_link'))).to be
end
end
end

View File

@ -117,6 +117,12 @@ shared_context "in-process server selenium tests" do
driver.ready_for_interaction = false # need to `get` before we do anything selenium-y in a spec
end
around :all do |group|
GreatExpectations.with_config(MISSING: :raise) do
group.run_examples
end
end
append_before :all do
retry_count = 0
begin

View File

@ -40,8 +40,10 @@ describe 'Web conferences' do
context 'when concluding a conference' do
let(:conference_title) { 'Newer Conference' }
before(:once) { create_wimba_conference(conference_title) }
before(:each) { start_first_conference_in_list }
before(:once) do
conference = create_wimba_conference(conference_title)
conference.add_attendee(@user)
end
context 'as a teacher' do
it 'concludes the conference', priority: "1", test_id: 323320 do

View File

@ -655,17 +655,15 @@ describe "context modules" do
context "module item cog focus management", priority: "1" do
before :each do
create_modules(1)[0].add_item({id: @assignment.id, type: 'assignment'})
get "/courses/#{@course.id}/modules"
add_existing_module_item('#assignments_select', 'Assignment', @assignment.title)
@tag = ContentTag.last
f("#context_module_item_#{@tag.id} .al-trigger").click
end
it "should return focus to the cog menu when closing the edit dialog for an item" do
hover_and_click("#context_module_item_#{@tag.id} .edit_item_link")
cancel_buttons = ff('.cancel_button.ui-button')
expect(cancel_buttons).to have_size(2)
cancel_buttons[1].click
f('.cancel_button.ui-button').click
check_element_has_focus(fj("#context_module_item_#{@tag.id} .al-trigger"))
end

View File

@ -79,6 +79,12 @@ describe "conversations new" do
wait_for_ajaximations
select_message_course(@group, true)
add_message_recipient @s2
write_message_subject('blah')
write_message_body('bluh')
click_send
run_jobs
conv = @s2.conversations.last.conversation
expect(conv.subject).to eq 'blah'
end
it "should allow messages to be sent individually for account-level groups", priority: "2", test_id: 201506 do
@ -261,7 +267,7 @@ describe "conversations new" do
fj('.btn.dropdown-toggle :contains("Select course")').click
wait_for_ajaximations
expect(f('.dropdown-menu.open')).to be_truthy
f('.dropdown-menu.open')
fj('.message-header-input .text:contains("Unnamed Course")').click
wait_for_ajaximations
@ -318,7 +324,7 @@ describe "conversations new" do
wait_for_ajaximations
f('.icon-compose').click
wait_for_ajaximations
expect(f("#compose-new-message")).to be_present
f("#compose-new-message")
end
end

View File

@ -169,12 +169,12 @@ describe "course copy" do
before(:each) do
course_with_admin_logged_in
@date_to_use = 2.weeks.from_now.monday.strftime("%Y-%m-%d")
get "/calendar"
quick_jump_to_date(@date_to_use)
create_calendar_event('Monday Event', true, false, false, @date_to_use, true)
end
it "shifts the dates a week later", priority: "2", test_id: 2953906 do
get "/calendar"
quick_jump_to_date(@date_to_use)
create_calendar_event('Monday Event', true, false, false, @date_to_use, true)
get "/courses/#{@course.id}/copy"
new_course_name = "copied course"
replace_content(f("input[type=text][id=course_name]"), new_course_name)
@ -184,8 +184,7 @@ describe "course copy" do
replace_content(f("input[type=text][id=newStartDate]"), date)
submit_form('#copy_course_form')
run_jobs
status = f('div.progressStatus span').text
keep_trying_until { expect(status == 'Completed') }
expect(f('div.progressStatus span')).to include_text 'Completed'
get "/calendar#view_name=week"
quick_jump_to_date(@date_to_use)
f('.fc-event').click

View File

@ -22,13 +22,11 @@ describe "discussion assignments" do
context "create group discussion" do
before do
get "/courses/#{@course.id}/discussion_topics"
expect_new_page_load{f("#new-discussion-btn").click}
get "/courses/#{@course.id}/discussion_topics/new"
f("#discussion-title").send_keys("New Discussion Title")
type_in_tiny('textarea[name=message]', 'Discussion topic message body')
f("#has_group_category").click
drop_down = get_options('#assignment_group_category_id').map(&:text).map(&:strip)
expect(drop_down).to include('category 1')
click_option('#assignment_group_category_id', 'category 1')
end

View File

@ -11,8 +11,7 @@ describe "add content box" do
@assignment.submit_homework(@student)
attachment_model(:context => @student)
eportfolio_model({:user => @user, :name => "student content"})
get "/eportfolios/#{@eportfolio.id}"
expect_new_page_load { f(".icon-arrow-right").click }
get "/eportfolios/#{@eportfolio.id}?view=preview"
f("#right-side .edit_content_link").click
wait_for_ajaximations
end

View File

@ -60,7 +60,7 @@ module Gradebook
period = gp_menu_list.find do |item|
f('label', item).attribute("for") == "period_option_#{grading_period_id}"
end
expect_new_page_load { period.click }
wait_for_new_page_load { period.click } or raise "page not loaded"
end
def enter_grade(grade, x_coordinate, y_coordinate)

View File

@ -40,7 +40,7 @@ module Gradezilla
period = gp_menu_list.find do |item|
f('label', item).attribute("for") == "period_option_#{grading_period_id}"
end
expect_new_page_load { period.click }
wait_for_new_page_load { period.click } or raise "page not loaded"
end
def enter_grade(grade, x_coordinate, y_coordinate)

View File

@ -349,7 +349,6 @@ describe 'Speedgrader' do
user_session(@teacher)
# see first student
get "/courses/#{@course.id}/gradebook/speed_grader?assignment_id=#{@assignment.id}"
expect(Speedgrader.selected_student).to include_text(@students[0].name)
end
after :each do
@ -359,26 +358,30 @@ describe 'Speedgrader' do
let(:next_) {'.next'}
let(:previous) {'.prev'}
it 'selects the first student' do
expect(Speedgrader.selected_student).to include_text(@students[0].name)
end
it 'has working next and previous arrows ', priority: "1", test_id: 164018 do
# click next to second student
expect(cycle_students_correctly(next_))
expect(cycle_students_correctly(next_)).to be
# click next to third student
expect(cycle_students_correctly(next_))
expect(cycle_students_correctly(next_)).to be
# go bak to the first student
expect(cycle_students_correctly(previous))
# go back to the first student
expect(cycle_students_correctly(previous)).to be
end
it 'arrows wrap around to start when you reach the last student', priority: "1", test_id: 272512 do
# click next to second student
expect(cycle_students_correctly(next_))
expect(cycle_students_correctly(next_)).to be
# click next to third student
expect(cycle_students_correctly(next_))
expect(cycle_students_correctly(next_)).to be
# wrap around to the first student
expect(cycle_students_correctly(next_))
expect(cycle_students_correctly(next_)).to be
end
it 'list all students', priority: "1", test_id: 164206 do

View File

@ -1000,7 +1000,7 @@ describe "new groups" do
select_change_groups_option
expect(f('.progressbar').displayed?)
expect(f('.progressbar')).to be_displayed
end
context "dragging and dropping a student" do

View File

@ -8,18 +8,10 @@ module ConferencesCommon
f('.new-conference-btn')
end
def start_conference_button
f('.start-button', new_conference_list)
end
def end_conference_button
f('.close_conference_link', new_conference_list)
end
def start_first_conference_in_list
expect_new_page_load { start_conference_button.click }
end
def end_first_conference_in_list
end_conference_button.click
close_modal_if_present

View File

@ -90,9 +90,7 @@ module ContextModulesCommon
fj('.add_item_button.ui-button').click
wait_for_ajaximations
tag = ContentTag.last
module_item = f("#context_module_item_#{tag.id}")
expect(module_item).to include_text(item_name)
module_item
fj("#context_module_item_#{tag.id}:contains(#{item_name.inspect})")
end
def select_module_item(select_element_css, item_text)
@ -100,11 +98,8 @@ module ContextModulesCommon
end
def new_module_form
add_form = f('#add_context_module_form')
f(".add_module_link").click
expect(add_form).to be_displayed
add_form
fj('#add_context_module_form:visible')
end
def add_module(module_name = 'Test Module')

View File

@ -304,7 +304,7 @@ module GroupsCommon
def verify_member_sees_group_page(index = 0)
get pages_page
expect_new_page_load { ff('.wiki-page-link')[index].click }
expect expect(f('.page-title')).to include_text("#{@page.title}")
expect(f('.page-title')).to include_text(@page.title)
end
# context test. if true, allows you to test files both in and out of group context,

View File

@ -349,11 +349,11 @@ module QuizzesCommon
end
if access_code.nil?
expect_new_page_load { f('#take_quiz_link').click }
wait_for_new_page_load { f('#take_quiz_link').click }
else
f('#quiz_access_code').send_keys(access_code)
expect_new_page_load { fj('.btn', '#main').click }
end
wait_for_new_page_load { fj('.btn', '#main').click }
end or raise "unable to start quiz"
wait_for_quiz_to_begin
end

View File

@ -126,7 +126,7 @@ module SchedulerCommon
def click_appointment_link
f('.view_calendar_link').click
expect(f('.agenda-wrapper.active')).to be_displayed
fj('.agenda-wrapper.active:visible')
wait_for_ajaximations
end

View File

@ -82,7 +82,7 @@ module SpeedGraderCommon
# move onto next student
direction = direction_string.equal?(:next) ? 1 : -1
new_index = (current_index + direction) % @students.length
student_x_of_x_string = "Student #{new_index + 1} of #{@students.length}"
student_x_of_x_string = "#{new_index + 1}/#{@students.length}"
Speedgrader.selected_student.text.include?(@students[new_index].name) &&
Speedgrader.student_x_of_x_label.text.include?(student_x_of_x_string)

View File

@ -92,7 +92,7 @@ describe 'new ui' do
get "/courses/#{@course.id}/files"
add_folder
# verifying new files folder icon css property still displays with new ui
f('.media-object.ef-big-icon.FilesystemObjectThumbnail.mimeClass-folder').displayed?
expect(f('.media-object.ef-big-icon.FilesystemObjectThumbnail.mimeClass-folder')).to be_displayed
end
it 'should not override high contrast theme', priority: "2", test_id: 244898 do
@ -123,7 +123,7 @@ describe 'new ui' do
get "/courses/#{@course.id}/assignments/new"
f('div#mceu_19.mce-widget.mce-btn').click
wait_for_ajaximations
f('.mathquill-toolbar-panes, .mathquill-tab-bar').displayed?
expect(f('.mathquill-toolbar-panes, .mathquill-tab-bar')).to be_displayed
end
end

View File

@ -131,9 +131,7 @@ describe "account admin outcomes" do
end
def click_on_state_standards
top_level_groups = ff(".outcome-level .outcome-group")
expect(top_level_groups.count).to eq 3
top_level_groups[1].click
fj(".outcome-level .outcome-group:eq(1)").click
wait_for_ajaximations
end

View File

@ -94,57 +94,28 @@ describe 'creating a quiz' do
context 'when on the quizzes index page' do
before(:each) do
get "/courses/#{@course.id}/quizzes"
end
def create_new_quiz
expect_new_page_load do
f('.new-quiz-link').click
end
end
it 'creates a quiz directly from the index page', priority: "1", test_id: 210055 do
expect_new_page_load do
click_save_settings_button
end
expect(f('#quiz_title')).to include_text 'Unnamed Quiz'
expect do
create_new_quiz
end.to change{ Quizzes::Quiz.count }.by(1)
end
it 'redirects to the correct quiz edit form', priority: "2", test_id: 399887 do
create_new_quiz
# check url
expect(driver.current_url).to match %r{/courses/\d+/quizzes/\d+\/edit}
# check quiz id
# The =~ operator compares the regex with the string.
# The (?<quiz_id>(\d+)) part of the regex tells the =~ to assign
# the value of the number found therein to the variable, quiz_id.
%r courses/\d+/quizzes/(?<quiz_id>(\d+))/edit =~ driver.current_url
expect(quiz_id.to_i).to be > 0
expect(driver.current_url).to match %r{/courses/\d+/quizzes/#{Quizzes::Quiz.last.id}\/edit}
end
it 'creates and previews a new quiz', priority: "1", test_id: 210056 do
# input name and description then save quiz
replace_content(f('#quiz_title'), 'new quiz')
description_text = 'new description'
expect(f('#quiz_description_ifr')).to be_displayed
type_in_tiny '#quiz_description', description_text
in_frame 'quiz_description_ifr' do
expect(f('#tinymce')).to include_text(description_text)
end
# add a question
click_questions_tab
click_new_question_button
submit_form('.question_form')
wait_for_ajaximations
# save the quiz
expect_new_page_load do
click_save_settings_button
wait_for_ajaximations
end
wait_for_ajaximations
# check quiz preview
f('#preview_quiz_button').click
expect(f('#questions')).to be_present
end
# TODO: remove this from test-rail, this test is redundant
it 'creates and previews a new quiz', priority: "1", test_id: 210056
end
it 'inserts files using the rich content editor', priority: "1", test_id: 132545 do

View File

@ -277,15 +277,8 @@ describe 'quizzes question creation' do
# get focus out of tinymce to allow change event to propogate
f(".question_header").click
f('button.recompute_variables').click
val = f('.variable .value').text.to_i
expect(val <= 10 && val >= 0)
recompute_button = f('button.recompute_variables')
var_el = f('.variable .value')
keep_trying_until do
recompute_button.click
var_el.text.to_i != val
end
recompute_button.click
fj('.supercalc:visible').send_keys('x + y')
f('button.save_formula_button').click
# normally it's capped at 200 (to keep the yaml from getting crazy big)...
@ -418,6 +411,7 @@ describe 'quizzes question creation' do
type_in_tiny '.question:visible textarea.question_content', 'This is an essay question.'
submit_form(fj('.question_form:visible'))
wait_for_ajax_requests
expect(Quizzes::QuizQuestion.where("question_data like '%This is an essay question%'")).to be_present
end
end

View File

@ -19,7 +19,7 @@ module CustomPageLoaders
close_modal_if_present
wait_for_ajaximations
else
expect_new_page_load(true) do
wait_for_new_page_load(true) do
driver.get(app_url + link)
end
end

View File

@ -214,7 +214,6 @@ module CustomSeleniumActions
assert_can_switch_views!
switch_editor_views(tiny_controlling_element)
tiny_controlling_element.clear
expect(tiny_controlling_element[:value]).to be_empty
switch_editor_views(tiny_controlling_element)
end
end

View File

@ -80,18 +80,26 @@ module CustomValidators
expect(box[0]).to be_displayed
end
def expect_new_page_load(accept_alert = false)
def wait_for_new_page_load(accept_alert = false)
driver.execute_script("window.INST = window.INST || {}; INST.still_on_old_page = true;")
yield
wait_for method: :expect_new_page_load do
wait_for(method: :wait_for_new_page_load) do
begin
driver.execute_script("return window.INST && INST.still_on_old_page !== true;")
rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnknownError
raise unless accept_alert
driver.switch_to.alert.accept
end
end or raise(RSpec::Expectations::ExpectationNotMetError, "expected new page load, none happened")
end or return false
wait_for_dom_ready
wait_for_ajaximations
true
end
def expect_new_page_load(accept_alert = false)
success = wait_for_new_page_load(accept_alert) do
yield
end
expect(success).to be, "expected new page load, none happened"
end
end

View File

@ -32,7 +32,7 @@ module SeleniumExtensions
yield
rescue Selenium::WebDriver::Error::StaleElementReferenceError
raise unless finder_proc
location = CallStackUtils.best_line_for($ERROR_INFO.backtrace, /test_setup/)
location = CallStackUtils.best_line_for($ERROR_INFO.backtrace)
$stderr.puts "WARNING: StaleElementReferenceError at #{location}, attempting to recover..."
@id = finder_proc.call.ref
retry

View File

@ -63,7 +63,7 @@ describe "Wiki pages and Tiny WYSIWYG editor features" do
f(".mce-i-bullist").click
in_frame wiki_page_body_ifr_id do
ff('#tinymce li').length == 3
expect(ff('#tinymce li').length).to eq 3
end
end
@ -85,7 +85,7 @@ describe "Wiki pages and Tiny WYSIWYG editor features" do
f('.mce-i-numlist').click
in_frame wiki_page_body_ifr_id do
ff('#tinymce li').length == 3
expect(ff('#tinymce li').length).to eq 3
end
end

View File

@ -55,6 +55,7 @@ Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
# let/before/example
BlankSlateProtection.truncate_all_tables! unless defined?(TestQueue::Runner::RSpec) # we do this in each runner
BlankSlateProtection.install!
GreatExpectations.install!
ActionView::TestCase::TestController.view_paths = ApplicationController.view_paths
@ -572,8 +573,8 @@ RSpec.configure do |config|
def process_csv_data_cleanly(*lines_or_opts)
importer = process_csv_data(*lines_or_opts)
expect(importer.errors).to eq []
expect(importer.warnings).to eq []
raise "csv errors" if importer.errors.present?
raise "csv warning" if importer.warnings.present?
end
def enable_cache(new_cache=:memory_store)
@ -726,8 +727,6 @@ RSpec.configure do |config|
skip "Please put valid S3 credentials in config/amazon_s3.yml"
end
end
expect(Attachment.s3_storage?).to be true
expect(Attachment.local_storage?).to be false
end
def local_storage!
@ -738,9 +737,6 @@ RSpec.configure do |config|
model.stubs(:s3_storage?).returns(false)
model.stubs(:local_storage?).returns(true)
end
expect(Attachment.local_storage?).to be true
expect(Attachment.s3_storage?).to be false
end
def run_job(job)

View File

@ -6,7 +6,7 @@ module BlankSlateProtection
return super unless BlankSlateProtection.enabled?
return super if caller.grep(BlankSlateProtection.exempt_patterns).present?
location = CallStackUtils.best_line_for(caller).sub(/:in .*/, '')
location = CallStackUtils.best_line_for(caller)
if caller.grep(/_context_hooks/).present?
$stderr.puts "\e[31mError: Don't create records inside `:all` hooks!"
$stderr.puts "See: " + location + "\e[0m"

View File

@ -1,14 +1,8 @@
module CallStackUtils
def self.best_line_for(call_stack, except = nil)
def self.best_line_for(call_stack)
line = CallStackUtils.prune_backtrace!(call_stack).first
root = Rails.root.to_s + "/"
lines = call_stack
lines = lines.reject { |l| l =~ except } if except
app_lines = lines.select { |s| s.starts_with?(root) }
line = app_lines.grep(%r{_spec\.rb:}).first ||
app_lines.grep(%r{/spec(_canvas?)/}).first ||
app_lines.first ||
lines.first
line.sub(root, '')
line.sub(root, '').sub(/:in .*/, '')
end
# (re-)raise the exception while preserving its backtrace
@ -16,16 +10,21 @@ module CallStackUtils
super exception.class, exception.message, exception.backtrace
end
def self.prune_backtrace!(bt)
line_regex = RSpec.configuration.in_project_source_dir_regex
# remove things until we get to the frd error cause
bt.shift while bt.first !~ line_regex || bt.first =~ %r{/spec/(support|selenium/test_setup/)}
bt
end
module ExceptionPresenter
def exception_backtrace
bt = super
# for our custom matchers/validators/finders/etc., prune their lines
# from the top of the stack so that you get a pretty/useful error
# message and backtrace
if exception_class_name =~ /\A(RSpec::|Selenium::WebDriver::Error::|SeleniumExtensions::)/
line_regex = RSpec.configuration.in_project_source_dir_regex
# remove things until we get to the frd error cause
bt.shift while bt.first !~ line_regex || bt.first =~ %r{/spec/(support|selenium/test_setup/)}
if exception_class_name =~ /\A(RSpec::|Selenium::WebDriver::Error::|SeleniumExtensions::|GreatExpectations::)/
CallStackUtils.prune_backtrace! bt
end
bt
end

View File

@ -0,0 +1,170 @@
# Ensure we aren't doing silly things with expectations, such as:
#
# 1. `expect` in a `before` ... `before` implies it's before the spec, so
# why are you testing things there?
# 2. `expect` without a `to` / `not_to` ... it will never get checked
# 3. specs with no expectations... what's the point?
module GreatExpectations
class Error < StandardError
def self.for(message, location = nil)
error = new(message)
bt = caller
# not a legit backtrace, but this way the rspec error/context
# will point right at the example in the file
bt.unshift "#{File.expand_path(location)}:in block in <top (required)>'" if location
error.set_backtrace(bt)
error
end
end
# default behavior, can be overridden with `.with_config`
CONFIG = {
# what to do if there's an `expect` in a `before`
EARLY: :raise,
# what to do if an `expect` has no `to`
UNCHECKED: :raise,
# what to do if a spec has no `expect`s
MISSING: :warn
}.freeze
module Example
# allow expectations at the last possible second (right after the
# inner-most before hooks run)
def run_before_example
super
GreatExpectations.example_started(self)
end
# immediately before running any after hooks, ensure the spec had some
# expectations. this includes mocha/rspec-mocks which will be verified
# in the super call
def run_after_example
GreatExpectations.example_finished
super
end
end
module AssertionDelegator
def assert(*)
GreatExpectations.expectation_checked
super
end
end
module ExpectationTarget
def initialize(*)
GreatExpectations.expectation_created(self)
super
end
def to(*)
GreatExpectations.expectation_checked(self)
super
end
def not_to(*)
GreatExpectations.expectation_checked(self)
super
end
alias to_not not_to
end
class << self
attr_accessor :config
attr_accessor :current_example
attr_accessor :expectation_count
def install!
self.config = CONFIG
::RSpec::Core::Example.prepend Example
::RSpec::Expectations::ExpectationTarget.prepend ExpectationTarget
::RSpec::Rails::MinitestAssertionAdapter::AssertionDelegator.prepend AssertionDelegator
end
def with_config(config)
orig_config = @config
@config = orig_config.merge(config)
yield
ensure
@config = orig_config
end
def expectation_created(expectation)
assert_not_early!
unchecked_expectations << expectation
end
def expectation_checked(expectation = nil)
unchecked_expectations.delete(expectation) if expectation
self.expectation_count += 1
end
def unchecked_expectations
@unchecked_expectations ||= Set.new
end
def example_started(example)
self.current_example = example
self.expectation_count = 0
end
def example_finished
return if current_example.nil? || # like if we `skip` in a before
current_example.exception ||
current_example.skipped? ||
current_example.pending?
assert_not_unchecked!
assert_not_missing!
rescue Error
current_example.set_exception($ERROR_INFO)
ensure
self.current_example = nil
unchecked_expectations.clear
end
def assert_not_early!
return if current_example
generate_error config[:EARLY], "Don't `expect` outside of the spec itself. `before`/`after` should only be used for setup/teardown"
end
def assert_not_unchecked!
return if unchecked_expectations.empty?
generate_error config[:UNCHECKED], "This spec has unchecked expectations, i.e. you forgot to call `to` or `not_to`", current_example.location
end
def assert_not_missing!
# vanilla expectation
return if expectation_count > 0
# rspec message expectations
return if ::RSpec::Mocks.space.proxies.any? do |_, proxy|
proxy.instance_variable_get(:@method_doubles).any? do |_, double|
double.expectations.any?
end
end
return if ::RSpec::Mocks.space.any_instance_recorders.any? do |_, recorder|
recorder.instance_variable_get(:@expectation_set)
end
# mocha expectations
return if ::Mocha::Mockery.instance.send(:expectations).any? do |expectation|
expectation.instance_variable_get(:@cardinality).needs_verifying?
end
generate_error config[:MISSING], "This spec has no expectations. Add one!", current_example.location
end
def generate_error(action, message, location = nil)
if action == :raise
raise Error.for(message, location)
else
$stderr.puts "\e[31mWarning: #{message}"
$stderr.puts "See: " + (location || CallStackUtils.best_line_for(caller)) + "\e[0m"
end
end
end
end

View File

@ -102,7 +102,6 @@ describe "/courses/_recent_event" do
@quiz.workflow_state = 'available'
@quiz.published_at = Time.zone.now
@quiz.save
expect(@quiz.assignment).not_to be_nil
@quiz_submission = @quiz.generate_submission(@user)
Quizzes::SubmissionGrader.new(@quiz_submission).grade_submission

View File

@ -56,8 +56,6 @@ describe "login/canvas/new.html.erb" do
config.save!
account.change_password_url = "http://www.instructure.com"
account.save!
expect(account.forgot_password_external_url).
to eq(account.change_password_url)
assigns[:domain_root_account] = account
end