2020-10-27 00:51:19 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2011-06-03 00:09:33 +08:00
|
|
|
#
|
2017-04-28 12:02:05 +08:00
|
|
|
# Copyright (C) 2011 - present Instructure, Inc.
|
2011-06-03 00:09:33 +08:00
|
|
|
#
|
|
|
|
# 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')
|
|
|
|
|
2015-04-09 01:21:08 +08:00
|
|
|
require 'nokogiri'
|
|
|
|
|
2011-06-03 00:09:33 +08:00
|
|
|
describe ContextModule do
|
|
|
|
def course_module
|
2015-02-05 23:44:37 +08:00
|
|
|
course_with_student_logged_in(:active_all => true)
|
2011-06-03 00:09:33 +08:00
|
|
|
@module = @course.context_modules.create!(:name => "some module")
|
|
|
|
end
|
|
|
|
|
2013-12-10 07:09:43 +08:00
|
|
|
describe "index" do
|
|
|
|
it "should require manage_content permission before showing add controls" do
|
|
|
|
course_with_teacher_logged_in active_all: true
|
|
|
|
get "/courses/#{@course.id}/modules"
|
2021-01-12 02:24:13 +08:00
|
|
|
doc = Nokogiri::HTML5(response.body)
|
2014-10-24 20:52:42 +08:00
|
|
|
expect(doc.at_css('.add_module_link')).not_to be_nil
|
2013-12-10 07:09:43 +08:00
|
|
|
|
2014-09-08 20:48:45 +08:00
|
|
|
@course.account.role_overrides.create! role: ta_role, permission: 'manage_content', enabled: false
|
2013-12-10 07:09:43 +08:00
|
|
|
course_with_ta course: @course
|
|
|
|
user_session(@ta)
|
|
|
|
get "/courses/#{@course.id}/modules"
|
2021-01-12 02:24:13 +08:00
|
|
|
doc = Nokogiri::HTML5(response.body)
|
2014-10-24 20:52:42 +08:00
|
|
|
expect(doc.at_css('.add_module_link')).to be_nil
|
2013-12-10 07:09:43 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-02-02 04:39:03 +08:00
|
|
|
it "should clear the page cache on individual tag change" do
|
|
|
|
enable_cache do
|
|
|
|
course_with_teacher_logged_in(:active_all => true)
|
|
|
|
context_module = @course.context_modules.create!
|
|
|
|
content_tag = context_module.add_item :type => 'context_module_sub_header', :title => "My Sub Header Title"
|
2013-03-26 23:19:59 +08:00
|
|
|
ContextModule.where(:id => context_module).update_all(:updated_at => 1.hour.ago)
|
2012-02-02 04:39:03 +08:00
|
|
|
get "/courses/#{@course.id}/modules"
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(response.body).to match(/My Sub Header Title/)
|
2012-02-02 04:39:03 +08:00
|
|
|
|
2020-01-09 07:39:02 +08:00
|
|
|
content_tag.update(:title => "My New Title")
|
touch module outside content_tag save txn to prevent deadlock
fixes CNVS-7757
test plan:
- open two Rails consoles
- in each, create a bunch of items in the same
module at the same time, e.g.
mod = ContextModule.last
100.times do |x|
mod.add_item type: 'context_module_sub_header',
title: "item #{x}"
end
- each console should finish adding its items
or, when deployed in a production-like environment
(with multiple app servers, or at least a multithreaded server)
- generate a curl command line to add an item to a module
(works with a subheader, so you don't need corresponding
assets)
curl -H "Authorization: Bearer {token} \
https://<canvas>/api/v1/courses/XXX/modules/YYY/items \
-F module_item[title]=newthing -F module_item[type]=SubHeader &
(the trailing &, after a space, means run in the background;
that is, don't wait for the preceding request to finish
before returning to the command line)
- run the preceding command several times in rapid succession
(up, enter, up, enter, etc.)
(I was able to reproduce in less than 10 requests)
- look at the json blocks returned, and make sure none of them
are an internal server error (if they are, there will be an
error report number you can look up and see if the deadlock
happened)
Change-Id: I63834cfd98393e5207625db27d18451103aa2944
Reviewed-on: https://gerrit.instructure.com/23783
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Rob Orton <rob@instructure.com>
QA-Review: August Thornton <august@instructure.com>
Product-Review: Jeremy Stanley <jeremy@instructure.com>
2013-08-28 00:46:08 +08:00
|
|
|
|
2012-02-02 04:39:03 +08:00
|
|
|
get "/courses/#{@course.id}/modules"
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(response.body).to match(/My New Title/)
|
2012-02-02 04:39:03 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-06-03 00:09:33 +08:00
|
|
|
describe "must_contribute" do
|
|
|
|
before do
|
|
|
|
course_module
|
|
|
|
@module.require_sequential_progress = true
|
|
|
|
@module.save!
|
|
|
|
end
|
|
|
|
|
|
|
|
def before_after
|
|
|
|
@module.completion_requirements = { @tag.id => { :type => 'must_contribute' } }
|
|
|
|
@module.save!
|
|
|
|
|
2014-03-08 07:11:50 +08:00
|
|
|
@progression = @module.evaluate_for(@user)
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(@progression).not_to be_nil
|
|
|
|
expect(@progression).not_to be_completed
|
|
|
|
expect(@progression).to be_unlocked
|
|
|
|
expect(@progression.current_position).to eql(@tag.position)
|
2011-06-03 00:09:33 +08:00
|
|
|
yield
|
2014-03-08 07:11:50 +08:00
|
|
|
@progression = @module.evaluate_for(@user)
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(@progression).to be_completed
|
|
|
|
expect(@progression.current_position).to eql(@tag.position)
|
2011-06-03 00:09:33 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "should progress for discussions" do
|
|
|
|
@discussion = @course.discussion_topics.create!(:title => "talk")
|
|
|
|
@tag = @module.add_item(:type => 'discussion_topic', :id => @discussion.id)
|
|
|
|
before_after do
|
2017-07-24 22:30:12 +08:00
|
|
|
post "/courses/#{@course.id}/discussion_entries", params: {:discussion_entry => { :message => 'ohai', :discussion_topic_id => @discussion.id }}
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(response).to be_redirect
|
2011-06-03 00:09:33 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should progress for wiki pages" do
|
2017-08-08 01:28:58 +08:00
|
|
|
@page = @course.wiki_pages.create!(:title => "talk page", :body => 'ohai', :editing_roles => 'teachers,students')
|
2011-06-03 00:09:33 +08:00
|
|
|
@tag = @module.add_item(:type => 'wiki_page', :id => @page.id)
|
|
|
|
before_after do
|
2017-07-24 22:30:12 +08:00
|
|
|
put "/api/v1/courses/#{@course.id}/pages/#{@page.url}", params: {:wiki_page => { :body => 'i agree', :title => 'talk page' }}
|
2011-06-03 00:09:33 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should progress for assignment discussions" do
|
2012-06-21 02:58:03 +08:00
|
|
|
@assignment = @course.assignments.create!(:title => 'talk assn', :submission_types => 'discussion_topic')
|
2011-06-03 00:09:33 +08:00
|
|
|
@tag = @module.add_item(:type => 'assignment', :id => @assignment.id)
|
|
|
|
before_after do
|
2017-07-24 22:30:12 +08:00
|
|
|
post "/courses/#{@course.id}/discussion_entries", params: {:discussion_entry => { :message => 'ohai', :discussion_topic_id => @assignment.discussion_topic.id }}
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(response).to be_redirect
|
2011-06-03 00:09:33 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2015-06-09 04:22:01 +08:00
|
|
|
|
2011-12-08 11:06:24 +08:00
|
|
|
describe "progressing before job is run" do
|
2012-05-10 02:57:05 +08:00
|
|
|
def progression_testing(progress_by_item_link)
|
2011-12-08 11:06:24 +08:00
|
|
|
enable_cache do
|
|
|
|
@is_attachment = false
|
|
|
|
course_with_student_logged_in(:active_all => true)
|
|
|
|
@quiz = @course.quizzes.create!(:title => "new quiz", :shuffle_answers => true)
|
2014-02-11 06:48:17 +08:00
|
|
|
@quiz.publish!
|
|
|
|
|
2013-08-28 05:45:46 +08:00
|
|
|
# separate timestamps so touch_context will actually invalidate caches
|
|
|
|
Timecop.freeze(4.seconds.ago) do
|
|
|
|
@mod1 = @course.context_modules.create!(:name => "some module")
|
|
|
|
@mod1.require_sequential_progress = true
|
|
|
|
@mod1.save!
|
|
|
|
@tag1 = @mod1.add_item(:type => 'quiz', :id => @quiz.id)
|
|
|
|
@mod1.completion_requirements = {@tag1.id => {:type => 'min_score', :min_score => 1}}
|
|
|
|
@mod1.save!
|
|
|
|
end
|
|
|
|
|
|
|
|
Timecop.freeze(2.second.ago) do
|
|
|
|
@mod2 = @course.context_modules.create!(:name => "dependant module")
|
|
|
|
@mod2.prerequisites = "module_#{@mod1.id}"
|
|
|
|
@mod2.save!
|
|
|
|
end
|
|
|
|
|
2014-02-11 06:48:17 +08:00
|
|
|
# all modules, tags, etc need to be published
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(@mod1).to be_published
|
|
|
|
expect(@mod2).to be_published
|
|
|
|
expect(@quiz).to be_published
|
|
|
|
expect(@tag1).to be_published
|
2014-02-11 06:48:17 +08:00
|
|
|
|
2011-12-08 11:06:24 +08:00
|
|
|
yield '<div id="test_content">yay!</div>'
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(@tag2).to be_published
|
2014-02-11 06:48:17 +08:00
|
|
|
|
|
|
|
# verify the second item is locked (doesn't display)
|
2011-12-08 11:06:24 +08:00
|
|
|
get @test_url
|
2018-02-06 06:54:03 +08:00
|
|
|
if @test_url.match?('files')
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
else
|
2018-08-01 01:25:10 +08:00
|
|
|
expect(response).to be_successful
|
2018-02-06 06:54:03 +08:00
|
|
|
end
|
2021-01-12 02:24:13 +08:00
|
|
|
html = Nokogiri::HTML5(response.body)
|
2016-04-23 09:14:52 +08:00
|
|
|
expect(html.css('#test_content').length).to eq(@test_content_length || 0)
|
2014-02-11 06:48:17 +08:00
|
|
|
|
|
|
|
# complete first module's requirements
|
2014-03-08 07:11:50 +08:00
|
|
|
p1 = @mod1.evaluate_for(@student)
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(p1.workflow_state).to eq 'unlocked'
|
2014-02-11 06:48:17 +08:00
|
|
|
|
|
|
|
@quiz_submission = @quiz.generate_submission(@student)
|
2014-04-22 04:14:52 +08:00
|
|
|
Quizzes::SubmissionGrader.new(@quiz_submission).grade_submission
|
2014-03-07 04:04:07 +08:00
|
|
|
@quiz_submission.workflow_state = 'complete'
|
|
|
|
@quiz_submission.manually_scored = true
|
2011-12-08 11:06:24 +08:00
|
|
|
@quiz_submission.kept_score = 1
|
|
|
|
@quiz_submission.save!
|
2014-02-11 06:48:17 +08:00
|
|
|
|
|
|
|
# navigate to the second item (forcing update to progression)
|
2015-06-09 04:22:01 +08:00
|
|
|
next_link = progress_by_item_link ?
|
2012-05-10 02:57:05 +08:00
|
|
|
"/courses/#{@course.id}/modules/items/#{@tag2.id}" :
|
|
|
|
"/courses/#{@course.id}/modules/#{@mod2.id}/items/first"
|
|
|
|
get next_link
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(response).to be_redirect
|
2019-03-09 00:36:53 +08:00
|
|
|
expect(response.location.ends_with?("module_item_id=#{@tag2.id}")).to be_truthy
|
2014-02-11 06:48:17 +08:00
|
|
|
|
2014-03-08 07:11:50 +08:00
|
|
|
# verify the second item is accessible
|
2011-12-08 11:06:24 +08:00
|
|
|
get @test_url
|
2018-08-01 01:25:10 +08:00
|
|
|
expect(response).to be_successful
|
2021-01-12 02:24:13 +08:00
|
|
|
html = Nokogiri::HTML5(response.body)
|
2011-12-08 11:06:24 +08:00
|
|
|
if @is_attachment
|
2019-03-09 00:36:53 +08:00
|
|
|
expect(html.at_css('#file_content')['src']).to match %r{#{@test_url.split("?").first}}
|
2014-10-24 01:00:21 +08:00
|
|
|
elsif @is_wiki_page
|
|
|
|
expect(html.css('#wiki_page_show').length).to eq 1
|
2011-12-08 11:06:24 +08:00
|
|
|
else
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(html.css('#test_content').length).to eq 1
|
2011-12-08 11:06:24 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2015-06-09 04:22:01 +08:00
|
|
|
|
2011-12-08 11:06:24 +08:00
|
|
|
it "should progress to assignment" do
|
2012-05-10 02:57:05 +08:00
|
|
|
[true, false].each do |progress_type|
|
|
|
|
progression_testing(progress_type) do |content|
|
|
|
|
asmnt = @course.assignments.create!(:title => 'assignment', :description => content)
|
|
|
|
@test_url = "/courses/#{@course.id}/assignments/#{asmnt.id}"
|
|
|
|
@tag2 = @mod2.add_item(:type => 'assignment', :id => asmnt.id)
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(@tag2).to be_published
|
2012-05-10 02:57:05 +08:00
|
|
|
end
|
2011-12-08 11:06:24 +08:00
|
|
|
end
|
|
|
|
end
|
2015-06-09 04:22:01 +08:00
|
|
|
|
2011-12-08 11:06:24 +08:00
|
|
|
it "should progress to discussion topic" do
|
2012-05-10 02:57:05 +08:00
|
|
|
[true, false].each do |progress_type|
|
|
|
|
progression_testing(progress_type) do |content|
|
|
|
|
discussion = @course.discussion_topics.create!(:title => "topic", :message => content)
|
|
|
|
@test_url = "/courses/#{@course.id}/discussion_topics/#{discussion.id}"
|
|
|
|
@tag2 = @mod2.add_item(:type => 'discussion_topic', :id => discussion.id)
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(@tag2).to be_published
|
2012-05-10 02:57:05 +08:00
|
|
|
end
|
2011-12-08 11:06:24 +08:00
|
|
|
end
|
|
|
|
end
|
2015-06-09 04:22:01 +08:00
|
|
|
|
2011-12-08 11:06:24 +08:00
|
|
|
it "should progress to a quiz" do
|
2012-05-10 02:57:05 +08:00
|
|
|
[true, false].each do |progress_type|
|
|
|
|
progression_testing(progress_type) do |content|
|
|
|
|
quiz = @course.quizzes.create!(:title => "quiz", :description => content)
|
2014-02-11 06:48:17 +08:00
|
|
|
quiz.publish!
|
2012-05-10 02:57:05 +08:00
|
|
|
@test_url = "/courses/#{@course.id}/quizzes/#{quiz.id}"
|
|
|
|
@tag2 = @mod2.add_item(:type => 'quiz', :id => quiz.id)
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(@tag2).to be_published
|
2012-05-10 02:57:05 +08:00
|
|
|
end
|
2011-12-08 11:06:24 +08:00
|
|
|
end
|
|
|
|
end
|
2015-06-09 04:22:01 +08:00
|
|
|
|
2011-12-08 11:06:24 +08:00
|
|
|
it "should progress to a wiki page" do
|
2012-05-10 02:57:05 +08:00
|
|
|
[true, false].each do |progress_type|
|
|
|
|
progression_testing(progress_type) do |content|
|
2017-08-08 01:28:58 +08:00
|
|
|
page = @course.wiki_pages.create!(:title => "wiki", :body => content)
|
2014-10-24 01:00:21 +08:00
|
|
|
@test_url = "/courses/#{@course.id}/pages/#{page.url}"
|
2012-05-10 02:57:05 +08:00
|
|
|
@tag2 = @mod2.add_item(:type => 'wiki_page', :id => page.id)
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(@tag2).to be_published
|
2014-10-24 01:00:21 +08:00
|
|
|
@is_wiki_page = true
|
2012-05-10 02:57:05 +08:00
|
|
|
end
|
2011-12-08 11:06:24 +08:00
|
|
|
end
|
|
|
|
end
|
2015-06-09 04:22:01 +08:00
|
|
|
|
2011-12-08 11:06:24 +08:00
|
|
|
it "should progress to an attachment" do
|
2012-05-10 02:57:05 +08:00
|
|
|
[true, false].each do |progress_type|
|
|
|
|
progression_testing(progress_type) do |content|
|
|
|
|
@is_attachment = true
|
|
|
|
att = Attachment.create!(:filename => 'test.html', :display_name => "test.html", :uploaded_data => StringIO.new(content), :folder => Folder.unfiled_folder(@course), :context => @course)
|
2019-03-09 00:36:53 +08:00
|
|
|
@test_url = "/courses/#{@course.id}/files/#{att.id}?fd_cookie_set=1"
|
2012-05-10 02:57:05 +08:00
|
|
|
@tag2 = @mod2.add_item(:type => 'attachment', :id => att.id)
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(@tag2).to be_published
|
2012-05-10 02:57:05 +08:00
|
|
|
end
|
2011-12-08 11:06:24 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2014-09-19 00:58:41 +08:00
|
|
|
|
|
|
|
describe "caching" do
|
|
|
|
it "should cache the view separately for each time zone" do
|
|
|
|
enable_cache do
|
2016-12-25 13:35:06 +08:00
|
|
|
course_factory active_all: true
|
2014-09-19 00:58:41 +08:00
|
|
|
|
|
|
|
mod = @course.context_modules.create!
|
|
|
|
mod.unlock_at = Time.utc(2014, 12, 25, 12, 0)
|
|
|
|
mod.save!
|
|
|
|
|
|
|
|
teacher1 = teacher_in_course(active_all: true).user
|
|
|
|
teacher1.time_zone = 'America/Los_Angeles'
|
|
|
|
teacher1.save!
|
|
|
|
|
|
|
|
teacher2 = teacher_in_course(active_all: true).user
|
|
|
|
teacher2.time_zone = 'America/New_York'
|
|
|
|
teacher2.save!
|
|
|
|
|
|
|
|
user_session teacher1
|
|
|
|
get "/courses/#{@course.id}/modules"
|
2018-08-01 01:25:10 +08:00
|
|
|
expect(response).to be_successful
|
2021-01-12 02:24:13 +08:00
|
|
|
body1 = Nokogiri::HTML5(response.body)
|
2014-09-19 00:58:41 +08:00
|
|
|
|
|
|
|
user_session teacher2
|
|
|
|
get "/courses/#{@course.id}/modules"
|
2018-08-01 01:25:10 +08:00
|
|
|
expect(response).to be_successful
|
2021-01-12 02:24:13 +08:00
|
|
|
body2 = Nokogiri::HTML5(response.body)
|
2014-09-19 00:58:41 +08:00
|
|
|
|
2014-10-14 02:51:52 +08:00
|
|
|
expect(body1.at_css("#context_module_content_#{mod.id} .unlock_details").text).to match /4am/
|
|
|
|
expect(body2.at_css("#context_module_content_#{mod.id} .unlock_details").text).to match /7am/
|
2014-09-19 00:58:41 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2011-06-03 00:09:33 +08:00
|
|
|
end
|