<% if content_for?(:callout) %>
<%= yield :callout %>
diff --git a/app/views/shared/lessons/_grid.html.erb b/app/views/shared/lessons/_grid.html.erb
index a1f0178a..7757ce39 100644
--- a/app/views/shared/lessons/_grid.html.erb
+++ b/app/views/shared/lessons/_grid.html.erb
@@ -1,5 +1,5 @@
- <% @course.lessons.published.each do |l| %>
+ <% @course.lessons.each do |l| %>
<%= render partial: "shared/lessons/lesson_tile_link",
locals: { lesson: l, completed: lessons_completed(@course).include?(l.id) } %>
<% end %>
diff --git a/app/views/shared/lessons/_lesson_tile.html.erb b/app/views/shared/lessons/_lesson_tile.html.erb
index c7a4789b..bdf4f07e 100644
--- a/app/views/shared/lessons/_lesson_tile.html.erb
+++ b/app/views/shared/lessons/_lesson_tile.html.erb
@@ -2,7 +2,7 @@
<% if completed %>
<% else %>
- <%= lesson.published_lesson_order %>
+ <%= lesson.lesson_order %>
<% end %>
<%= lesson.title %>
diff --git a/app/views/shared/lessons/_lesson_tile_link.html.erb b/app/views/shared/lessons/_lesson_tile_link.html.erb
index d7e0b04b..646e80e6 100644
--- a/app/views/shared/lessons/_lesson_tile_link.html.erb
+++ b/app/views/shared/lessons/_lesson_tile_link.html.erb
@@ -1,3 +1,3 @@
-<%= link_to course_lesson_path(@course, lesson), class: "lesson-tile #{'completed' if completed}", data: { cpl_ga_event: 'on', cpl_ga_value: 'user-click-lesson' } do %>
+<%= link_to course_lesson_path(@course, lesson, preview: @preview), class: "lesson-tile #{'completed' if completed}", data: { cpl_ga_event: 'on', cpl_ga_value: 'user-click-lesson' } do %>
<%= render partial: "shared/lessons/lesson_tile", locals: { completed: completed, lesson: lesson } %>
<% end %>
diff --git a/config/deploy/staging.rb b/config/deploy/staging.rb
index 0a1a4581..908fb232 100644
--- a/config/deploy/staging.rb
+++ b/config/deploy/staging.rb
@@ -1,6 +1,6 @@
set :bundle_without, %w{production test}.join(" ")
-set :branch, "staging"
+set :branch, "develop"
server "dl-stageapp-01.do.lark-it.com",
user: fetch(:application),
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 0e9681ed..3bd60b15 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -34,8 +34,6 @@ en:
password: "Password"
course:
pub_status: "Publication Status"
- lesson:
- pub_status: "Publication Status"
common:
submit: "Submit"
save: "Save"
@@ -237,7 +235,7 @@ en:
there_is_no_text_copy_of_course: "There is no text copy for this course"
there_are_no_additional_resources: "There are no additional resources for this course."
there_are_no_notes_for_course: "There are no notes or partner resources for this course"
- practice_and_use_your_skills: "Use my skills now! (click each link below)"
+ practice_and_use_your_skills: "Use my skills now!"
lessons_in_this_course: "Activities in This Course"
start_lesson: "Start"
lesson: "Activity"
@@ -248,7 +246,7 @@ en:
course_completion_page:
default_org:
user_survey_button_text: "We Need Your Help - Please Take a Quick Survey"
- practice_new_skills: "Practice and use your new skills! (click each link below)"
+ practice_new_skills: "Practice and use your new skills!"
congratulations: "Congratulations"
you_successfully_completed: "You've successfully completed"
return_to_my_courses: "Return to My Courses"
diff --git a/config/locales/es.yml b/config/locales/es.yml
index fb7cb5f4..05e21ec0 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -36,8 +36,6 @@ es:
password: "Contraseña"
course:
pub_status: "Estado de la publicación"
- lesson:
- pub_status: "Estado de la publicación"
errors:
models:
profile:
@@ -239,7 +237,7 @@ es:
there_is_no_text_copy_of_course: "No hay ninguna copia de texto para este curso."
there_are_no_additional_resources: "No hay recursos adicionales para este curso."
there_are_no_notes_for_course: "No hay notas o recursos de los asociados para este curso"
- practice_and_use_your_skills: "Utilizar mis habilidades ahora! ( Haga clic en cada enlace de abajo )"
+ practice_and_use_your_skills: "Utilizar mis habilidades ahora!"
lessons_in_this_course: "Actividades en este curso"
start_lesson: "Empezar"
lesson: "Actividad"
@@ -250,7 +248,7 @@ es:
course_completion_page:
default_org:
user_survey_button_text: "Necesitamos su ayuda - Por favor tome una encuesta rápida"
- practice_new_skills: "¡Practica y usa sus nuevas habilidades! (Da clic en cada liga abajo)"
+ practice_new_skills: "¡Practica y usa sus nuevas habilidades!"
congratulations: "Felicitaciones"
you_successfully_completed: "Ha completado la Actividad"
return_to_my_courses: "Regresar a Mis Cursos"
diff --git a/config/routes.rb b/config/routes.rb
index efc73e39..b15376e6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -23,7 +23,6 @@
resources :courses, only: [:index, :show] do
post 'start'
- get 'attachment/:attachment_id' => 'courses#view_attachment', as: :attachment
get 'skills', to: 'courses#skills', as: :skills
resources :lessons, only: [:index, :show] do
get 'lesson_complete'
@@ -31,6 +30,8 @@
end
end
+ resources :attachments, only: [:show]
+
resources :my_courses, only: [:index], param: :course_id
resources :course_progresses, only: [:create, :update], param: :course_id
@@ -47,9 +48,6 @@
get '/static/portfolio', to: redirect('/cms_pages/see-our-work-in-action')
get '/static/overview', to: redirect('/cms_pages/get-digitallearn-for-your-library')
- get 'designing-courses-1', to: 'courses#designing_courses_1'
- get 'designing-courses-2', to: 'courses#designing_courses_2'
-
namespace :trainer do
root 'home#index'
resources :dashboard, only: [:index]
@@ -100,9 +98,10 @@
get 'users/export_user_info', to: 'users#export_user_info', as: :export_user_info
- resources :courses do
+ resources :courses, except: [:show] do
put :sort, on: :collection
patch 'update_pub_status'
+ get :preview
resources :lessons, except: [:index, :show] do
collection do
diff --git a/db/data/20200405224603_rename_attachment_types.rb b/db/data/20200405224603_rename_attachment_types.rb
new file mode 100644
index 00000000..c1c096e9
--- /dev/null
+++ b/db/data/20200405224603_rename_attachment_types.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class RenameAttachmentTypes < ActiveRecord::Migration[5.2]
+ def up
+ Attachment.where(doc_type: 'post-course').each do |a|
+ a.update!(doc_type: 'additional-resource')
+ end
+
+ Attachment.where(doc_type: 'supplemental').each do |a|
+ a.update!(doc_type: 'text-copy')
+ end
+ end
+
+ def down
+ Attachment.where(doc_type: 'additional-resource').each do |a|
+ a.update!(doc_type: 'post-course')
+ end
+
+ Attachment.where(doc_type: 'text-copy').each do |a|
+ a.update!(doc_type: 'supplemental')
+ end
+
+ end
+end
diff --git a/db/data_schema.rb b/db/data_schema.rb
index 2d79e584..42973583 100644
--- a/db/data_schema.rb
+++ b/db/data_schema.rb
@@ -1,3 +1,3 @@
# frozen_string_literal: true
-DataMigrate::Data.define(version: 20_191_205_164_530)
+DataMigrate::Data.define(version: 20_200_405_224_603)
diff --git a/db/migrate/20200327172506_remove_lesson_pub_status.rb b/db/migrate/20200327172506_remove_lesson_pub_status.rb
new file mode 100644
index 00000000..023b53d4
--- /dev/null
+++ b/db/migrate/20200327172506_remove_lesson_pub_status.rb
@@ -0,0 +1,5 @@
+class RemoveLessonPubStatus < ActiveRecord::Migration[5.2]
+ def change
+ remove_column :lessons, :pub_status, :string, default: 'D'
+ end
+end
diff --git a/db/migrate/20200327214039_remove_parent_lesson_id_from_lesson.rb b/db/migrate/20200327214039_remove_parent_lesson_id_from_lesson.rb
new file mode 100644
index 00000000..45b09027
--- /dev/null
+++ b/db/migrate/20200327214039_remove_parent_lesson_id_from_lesson.rb
@@ -0,0 +1,5 @@
+class RemoveParentLessonIdFromLesson < ActiveRecord::Migration[5.2]
+ def change
+ remove_column :lessons, :parent_lesson_id, :integer
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 8a96963f..7c23d1ab 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -535,8 +535,6 @@ CREATE TABLE public.lessons (
story_line_content_type character varying,
story_line_file_size bigint,
story_line_updated_at timestamp without time zone,
- pub_status character varying,
- parent_lesson_id integer,
parent_id integer
);
@@ -1834,6 +1832,8 @@ INSERT INTO "schema_migrations" (version) VALUES
('20191211193827'),
('20200120023743'),
('20200220181809'),
-('20200220184942');
+('20200220184942'),
+('20200327172506'),
+('20200327214039');
diff --git a/erd.pdf b/erd.pdf
index 7ef202e3..42850105 100644
Binary files a/erd.pdf and b/erd.pdf differ
diff --git a/lib/tasks/demo.rake b/lib/tasks/demo.rake
index 51cd0c58..239ee233 100644
--- a/lib/tasks/demo.rake
+++ b/lib/tasks/demo.rake
@@ -45,7 +45,7 @@ namespace :demo do
story_line_file_size: cpl_lesson.story_line_file_size,
story_line_updated_at: cpl_lesson.story_line_updated_at,
pub_status: cpl_lesson.pub_status,
- parent_lesson_id: cpl_lesson.id)
+ parent_id: cpl_lesson.id)
@new_course.lessons << new_lesson
end
diff --git a/spec/controllers/admin/courses_controller/courses_controller_spec.rb b/spec/controllers/admin/courses_controller/courses_controller_spec.rb
new file mode 100644
index 00000000..9d2be8c4
--- /dev/null
+++ b/spec/controllers/admin/courses_controller/courses_controller_spec.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::CoursesController do
+ let(:org) { FactoryBot.create(:organization) }
+ let(:admin) { FactoryBot.create(:user, :admin, organization: org) }
+ let(:category1) { FactoryBot.create(:category, organization: org) }
+ let(:category2) { FactoryBot.create(:category, organization: org) }
+ let!(:course1) { FactoryBot.create(:course_with_lessons, title: 'Course1', course_order: 1, category: category1, organization: org) }
+ let!(:course2) { FactoryBot.create(:course, title: 'Course2', course_order: 2, category: category2, organization: org) }
+ let!(:course3) { FactoryBot.create(:course, title: 'Course3', course_order: 3, organization: org) }
+
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:pla_course) { FactoryBot.create(:course, organization: pla) }
+
+ before(:each) do
+ @request.host = "#{org.subdomain}.test.host"
+ sign_in admin
+ end
+
+ describe 'GET #index' do
+ before(:each) do
+ get :index, params: { subdomain: 'chipublib' }
+ end
+
+ it 'assigns all courses as @courses' do
+ expect(assigns(:courses)).to include(course1, course2, course3)
+ end
+
+ it 'only assigns correct number of courses' do
+ expect(assigns(:courses).count).to eq(3)
+ end
+
+ it 'assigns category_ids' do
+ expect(assigns(:category_ids)).to include(category1.id, category2.id)
+ end
+
+ it 'only assigns proper category ids' do
+ expect(assigns(:category_ids).count).to eq(2)
+ end
+
+ it 'assigns uncategorized_courses' do
+ expect(assigns(:uncategorized_courses)).to include(course3)
+ end
+
+ it 'only assigns uncategorized courses' do
+ expect(assigns(:uncategorized_courses).count).to eq(1)
+ end
+ end
+
+ describe 'GET #new' do
+ it 'assigns a new course as @course' do
+ get :new
+ expect(assigns(:course)).to be_a_new(Course)
+ end
+ end
+
+ describe 'GET #preview' do
+ it 'assigns the requested course as @course' do
+ get :preview, params: { course_id: pla_course.to_param }
+ expect(assigns(:course)).to eq(pla_course)
+ end
+
+ it 'renders course show' do
+ get :preview, params: { course_id: pla_course.to_param }
+ expect(response).to render_template('courses/show')
+ end
+ end
+
+ describe 'PATCH #update_pub_status' do
+ it 'updates the status' do
+ patch :update_pub_status, params: { course_id: course1.id.to_param, value: 'P' }
+ course1.reload
+ expect(course1.pub_status).to eq('P')
+ end
+
+ it 'updates the pub_date if status is published' do
+ Timecop.freeze do
+ patch :update_pub_status, params: { course_id: course1.id.to_param, value: 'A' }
+ course1.reload
+ expect(course1.pub_date).to be(nil)
+
+ patch :update_pub_status, params: { course_id: course1.id.to_param, value: 'P' }
+ course1.reload
+ expect(course1.pub_date.to_i).to eq(Time.zone.now.to_i)
+ end
+ end
+ end
+
+ describe 'GET #edit' do
+ let(:imported_course) { FactoryBot.create(:course, organization: org, parent: pla_course) }
+
+ it 'assigns the requested course as @course' do
+ get :edit, params: { id: course1.to_param }
+ expect(assigns(:course)).to eq(course1)
+ end
+
+ it 'assigns imported_course instance variable to true if course is imported' do
+ get :edit, params: { id: imported_course.to_param }
+ expect(assigns(:imported_course)).to eq(true)
+ end
+
+ it 'assigns imported_course to false if course is original' do
+ get :edit, params: { id: course1.to_param }
+ expect(assigns(:imported_course)).to eq(false)
+ end
+ end
+
+ describe 'POST #sort' do
+ it 'should change course order' do
+ order_params = { '0' => { id: course2.id, position: 1 }, '1' => { id: course1.id, position: 2 } }
+ post :sort, params: { order: order_params }
+ expect(course1.reload.course_order).to eq(2)
+ expect(course2.reload.course_order).to eq(1)
+ end
+ end
+end
diff --git a/spec/controllers/admin/courses_controller/create_spec.rb b/spec/controllers/admin/courses_controller/create_spec.rb
new file mode 100644
index 00000000..2cbab832
--- /dev/null
+++ b/spec/controllers/admin/courses_controller/create_spec.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::CoursesController do
+ let(:org) { FactoryBot.create(:organization) }
+ let(:admin) { FactoryBot.create(:user, :admin, organization: org) }
+ let(:topic) { FactoryBot.create(:topic) }
+
+ before(:each) do
+ @request.host = "#{org.subdomain}.test.host"
+ sign_in admin
+ end
+
+ describe 'POST #create' do
+
+ let(:existing_category) { FactoryBot.create(:category, organization: org) }
+
+ let(:valid_attributes) do
+ { title: 'Course you can',
+ seo_page_title: 'Doo it | Foo it | Moo it ',
+ meta_desc: "You're so friggin meta",
+ summary: "Basically it's basic",
+ description: 'More descriptive that you know!',
+ contributor: "MeMyself&I
",
+ format: 'D',
+ topic_ids: [topic.id],
+ language_id: @english.id,
+ level: 'Advanced',
+ course_order: '',
+ organization_id: org.id }
+ end
+
+ let(:invalid_attributes) do
+ { title: '',
+ seo_page_title: '',
+ meta_desc: '',
+ summary: '',
+ description: '',
+ contributor: '',
+ format: '',
+ language_id: '',
+ level: '',
+ course_order: '',
+ organization_id: org.id }
+ end
+
+ context 'with valid params' do
+ it 'creates a new Course' do
+ expect do
+ post :create, params: { course: valid_attributes }
+ end.to change(Course, :count).by(1)
+ end
+
+ it 'assigns a newly created course as @course' do
+ post :create, params: { course: valid_attributes }
+ expect(assigns(:course)).to be_a(Course)
+ expect(assigns(:course)).to be_persisted
+ end
+
+ it 'assigns an existing topic' do
+ expect do
+ post :create, params: { course: valid_attributes }
+ end.to change { topic.courses.count }.by(1)
+ end
+
+ it 'creates a new topic, if given' do
+ valid_attributes[:course_topics_attributes] = [{ topic_attributes: { title: 'Some other topic' } }]
+ expect do
+ post :create, params: { course: valid_attributes }
+ end.to change(Topic, :count).by(1)
+ end
+
+ it 'redirects to the admin edit view of the course' do
+ post :create, params: { course: valid_attributes }
+ expect(response).to have_http_status(:redirect)
+ expect(response).to redirect_to(edit_admin_course_path(Course.find_by(title: valid_attributes[:title])))
+ end
+
+ it 'redirects to the lesson edit page if specified' do
+ post :create, params: { course: valid_attributes, commit: 'Save & Edit Lessons' }
+ expect(response).to have_http_status(:redirect)
+ expect(response).to redirect_to(new_admin_course_lesson_path(Course.find_by(title: valid_attributes[:title])))
+ end
+
+ it 'adds an existing category if provided' do
+ category = FactoryBot.create(:category, organization: org)
+ post :create, params: { course: valid_attributes.merge(category_id: category.id) }
+ expect(assigns(:course).category).to eq(category)
+ end
+
+ it 'creates and adds category if new category selected' do
+ expect do
+ post :create, params: {
+ course: valid_attributes.merge(
+ category_id: '0',
+ category_attributes: {
+ name: Faker::Lorem.word,
+ organization_id: org.id
+ }
+ )
+ }
+ end.to change(Category, :count).by(1)
+
+ expect(assigns(:course).category).not_to be_nil
+ end
+
+ it 're-reders new if repeat category name' do
+ post :create, params: {
+ course: valid_attributes.merge(
+ category_id: '0',
+ category_attributes: {
+ name: existing_category.name,
+ organization_id: org.id
+ }
+ )
+ }
+ expect(response).to render_template('new')
+ end
+
+ it 'publishes course if committed with publish' do
+ expect do
+ post :create, params: { course: valid_attributes.merge(pub_status: 'P') }
+ end.to change { Course.where(pub_status: 'P').count }.by(1)
+ end
+ end
+
+ context 'with invalid params' do
+ it 'assigns a newly created but unsaved course as @course' do
+ post :create, params: { course: invalid_attributes }
+ expect(assigns(:course)).to be_a_new(Course)
+ end
+
+ it "re-renders the 'new' template" do
+ post :create, params: { course: invalid_attributes }
+ expect(response).to render_template('new')
+ end
+ end
+ end
+end
diff --git a/spec/controllers/admin/courses_controller/update_spec.rb b/spec/controllers/admin/courses_controller/update_spec.rb
new file mode 100644
index 00000000..be10f2ad
--- /dev/null
+++ b/spec/controllers/admin/courses_controller/update_spec.rb
@@ -0,0 +1,226 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::CoursesController do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:pla_admin) { FactoryBot.create(:user, :admin, organization: pla) }
+ let(:pla_category1) { FactoryBot.create(:category, organization: pla) }
+ let(:pla_category2) { FactoryBot.create(:category, organization: pla) }
+ let(:pla_course) { FactoryBot.create(:course, organization: pla, title: 'PLA Course', category: pla_category1) }
+
+ let(:org) { FactoryBot.create(:organization) }
+ let(:admin) { FactoryBot.create(:user, :admin, organization: org) }
+ let!(:child_course) { FactoryBot.create(:course, title: 'Child Course', parent: pla_course, organization: org, pub_status: 'D') }
+
+ describe 'POST #update' do
+ context 'pla course' do
+ before(:each) do
+ @request.host = "#{pla.subdomain}.test.host"
+ sign_in pla_admin
+ end
+
+ let(:update_params) do
+ pla_course.attributes.merge('title' => 'Updated Title', 'access_level' => 'authenticated_users', 'category_id' => pla_category2.id, 'notes' => 'New Notes')
+ end
+
+ let(:new_topic_params) do
+ update_params.merge(course_topics_attributes: [{ topic_attributes: { title: 'Another new topic' } }])
+ end
+
+ let(:save_request) do
+ patch :update, params: { id: pla_course.to_param, course: update_params, commit: 'Save Course' }
+ end
+
+ it 'redirects to edit path' do
+ save_request
+ expect(response).to redirect_to(edit_admin_course_path(pla_course.reload))
+ end
+
+ it 'updates title' do
+ expect do
+ save_request
+ end.to change { pla_course.reload.title }.from('PLA Course').to('Updated Title')
+ end
+
+ it 'updates access level' do
+ expect do
+ save_request
+ end.to change { pla_course.reload.access_level }.from('everyone').to('authenticated_users')
+ end
+
+ it 'updates category' do
+ expect do
+ save_request
+ end.to change { pla_course.reload.category }.from(pla_category1).to(pla_category2)
+ end
+
+ it 'displays appropriate notice for successful course update' do
+ save_request
+ expect(flash[:notice]).to eq('Course was successfully updated.')
+ end
+
+ it 'redirects to lesson edit page if desired' do
+ patch :update, params: { id: pla_course.to_param, course: update_params, commit: 'Save & Edit Lessons' }
+ expect(response).to redirect_to(new_admin_course_lesson_path(pla_course.reload, pla_course.lessons.first))
+ end
+
+ it 'creates a new topic, if given' do
+ expect do
+ patch :update, params: { id: pla_course.to_param, course: new_topic_params }
+ end.to change(Topic, :count).by(1)
+ end
+
+ describe 'propagation' do
+ it 'propagates title to child courses' do
+ expect do
+ save_request
+ end.to change { child_course.reload.title }.to('Updated Title')
+ end
+
+ it 'propagates topic to child courses' do
+ expect do
+ patch :update, params: { id: pla_course.to_param, course: new_topic_params }
+ end.to(change { child_course.reload.topics })
+ end
+
+ it 'does not assign course to main site category' do
+ expect do
+ save_request
+ end.to_not(change { child_course.reload.category })
+ end
+
+ it 'does not change child course access level' do
+ expect do
+ save_request
+ end.to_not(change { child_course.reload.access_level })
+ end
+
+ it 'does not propagate notes/content for further learning to child courses' do
+ expect do
+ save_request
+ end.to_not(change { child_course.reload.notes })
+ end
+
+ describe 'attachments' do
+ let(:document) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf') }
+
+ let(:attachment_attributes) do
+ { attachments_attributes: {
+ '0' => {
+ document: document,
+ title: '',
+ doc_type: 'additional-resource',
+ file_description: 'additional-resource attachment test'
+ }
+ } }
+ end
+
+ it 'adds attachments to parent course' do
+ expect do
+ patch :update, params: { id: pla_course.to_param, course: attachment_attributes }
+ end.to change { pla_course.attachments.count }.by(1)
+ end
+
+ it 'does not propagate attachments to child course' do
+ expect do
+ patch :update, params: { id: pla_course.to_param, course: attachment_attributes }
+ end.to_not(change { child_course.attachments.count })
+ end
+ end
+
+ describe 'new category' do
+ let(:new_category_params) do
+ { id: pla_course.to_param,
+ course: { category_id: '0',
+ category_attributes: { name: 'New Category', organization_id: pla.id } },
+ commit: 'Save Course' }
+ end
+
+ it 'should create a new category in originating org' do
+ expect do
+ patch :update, params: new_category_params
+ end.to change { pla.categories.count }.by(1)
+ end
+
+ it 'should not change category for child course' do
+ expect do
+ patch :update, params: new_category_params
+ end.to_not(change { child_course.reload.category })
+ end
+ end
+ end
+ end
+
+ context 'imported course' do
+ before do
+ @request.host = "#{org.subdomain}.test.host"
+ sign_in admin
+ end
+
+ describe 'save an imported course' do
+ let(:new_category) { FactoryBot.create(:category, organization: org) }
+ let(:document) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf') }
+
+ let(:additional_resource_attachment_attributes) do
+ { '0' => {
+ document: document,
+ title: '',
+ doc_type: 'additional-resource',
+ file_description: 'additional-resource attachment test'
+ } }
+ end
+
+ let(:text_copy_attachment_attributes) do
+ { '0' => {
+ document: document,
+ title: '',
+ doc_type: 'text-copy',
+ file_description: 'text-copy attachment test'
+ } }
+ end
+
+ it 'should not change title, if new title is given' do
+ expect do
+ patch :update, params: { id: child_course.to_param, course: { title: 'New Title' }, commit: 'Save Course' }
+ end.to_not(change { child_course.reload.title })
+ end
+
+ it 'should redirect to course edit page' do
+ patch :update, params: { id: child_course.to_param, course: child_course.attributes, commit: 'Save Course' }
+ expect(response).to redirect_to(edit_admin_course_path(child_course))
+ end
+
+ it 'should change the course category, if given' do
+ expect do
+ patch :update, params: { id: child_course.to_param, course: { category_id: new_category.id }, commit: 'Save Course' }
+ end.to change { child_course.reload.category }.to(new_category)
+ end
+
+ it 'should change the course access level, if given' do
+ expect do
+ patch :update, params: { id: child_course.to_param, course: { access_level: 'authenticated_users' }, commit: 'Save Course' }
+ end.to change { child_course.reload.access_level }.from('everyone').to('authenticated_users')
+ end
+
+ it 'should change publication status, if given' do
+ expect do
+ patch :update, params: { id: child_course.to_param, course: { pub_status: 'P' }, commit: 'Save Course' }
+ end.to change { child_course.reload.pub_status }.from('D').to('P')
+ end
+
+ it 'should add an additional resource attachment' do
+ expect do
+ patch :update, params: { id: child_course.to_param, course: { attachments_attributes: additional_resource_attachment_attributes } }
+ end.to change { child_course.reload.attachments.count }.by(1)
+ end
+
+ it 'should update course notes' do
+ expect do
+ patch :update, params: { id: child_course.to_param, course: { notes: 'new course notes' } }
+ end.to change { child_course.reload.notes }.from(nil).to('new course notes')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/controllers/admin/courses_controller_spec.rb b/spec/controllers/admin/courses_controller_spec.rb
deleted file mode 100644
index 070186fc..00000000
--- a/spec/controllers/admin/courses_controller_spec.rb
+++ /dev/null
@@ -1,302 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe Admin::CoursesController do
- let(:org) { FactoryBot.create(:organization) }
- let(:other_org) { FactoryBot.create(:organization, subdomain: 'dpl') }
- let(:admin) { FactoryBot.create(:user, :admin, organization: org) }
- let(:category1) { FactoryBot.create(:category, organization: org) }
- let(:category2) { FactoryBot.create(:category, organization: org) }
- let(:category3) { FactoryBot.create(:category, organization: other_org) }
- let!(:course1) { FactoryBot.create(:course, title: 'Course1', course_order: 1, category: category1, organization: org) }
- let!(:course2) { FactoryBot.create(:course, title: 'Course2', course_order: 2, category: category2, organization: org) }
- let!(:course3) { FactoryBot.create(:course, title: 'Course3', course_order: 3, organization: org) }
- let!(:course_for_different_org) { create(:course, title: 'Different Org', organization: other_org) }
-
- before(:each) do
- @request.host = "#{org.subdomain}.test.host"
- sign_in admin
- end
-
- describe 'GET #index' do
- before(:each) do
- get :index, params: { subdomain: 'chipublib' }
- end
-
- it 'assigns all courses as @courses' do
- expect(assigns(:courses)).to include(course1, course2, course3)
- end
-
- it 'only assigns correct number of courses' do
- expect(assigns(:courses).count).to eq(3)
- end
-
- it 'assigns category_ids' do
- expect(assigns(:category_ids)).to include(category1.id, category2.id)
- end
-
- it 'only assigns proper category ids' do
- expect(assigns(:category_ids).count).to eq(2)
- end
-
- it 'assigns uncategorized_courses' do
- expect(assigns(:uncategorized_courses)).to include(course3)
- end
-
- it 'only assigns uncategorized courses' do
- expect(assigns(:uncategorized_courses).count).to eq(1)
- end
- end
-
- describe 'GET #show' do
- it 'assigns the requested course as @course' do
- get :show, params: { id: course1.to_param }
- expect(assigns(:course)).to eq(course1)
- end
- end
-
- describe 'GET #new' do
- it 'assigns a new course as @course' do
- get :new
- expect(assigns(:course)).to be_a_new(Course)
- end
- end
-
- describe 'PATCH #update_pub_status' do
- it 'updates the status' do
- patch :update_pub_status, params: { course_id: course1.id.to_param, value: 'P' }
- course1.reload
- expect(course1.pub_status).to eq('P')
- end
-
- it 'updates the pub_date if status is published' do
- Timecop.freeze do
- patch :update_pub_status, params: { course_id: course1.id.to_param, value: 'A' }
- course1.reload
- expect(course1.pub_date).to be(nil)
-
- patch :update_pub_status, params: { course_id: course1.id.to_param, value: 'P' }
- course1.reload
- expect(course1.pub_date.to_i).to eq(Time.zone.now.to_i)
- end
- end
- end
-
- describe 'GET #edit' do
- it 'assigns the requested course as @course' do
- get :edit, params: { id: course1.to_param }
- expect(assigns(:course)).to eq(course1)
- end
- end
-
- describe 'POST #create' do
- let(:valid_attributes) do
- { title: 'Course you can',
- seo_page_title: 'Doo it | Foo it | Moo it ',
- meta_desc: "You're so friggin meta",
- summary: "Basically it's basic",
- description: 'More descriptive that you know!',
- contributor: "MeMyself&I
",
- pub_status: 'P',
- format: 'D',
- other_topic_text: 'Learning',
- language_id: @english.id,
- level: 'Advanced',
- course_order: '',
- organization_id: org.id }
- end
-
- let(:invalid_attributes) do
- { title: '',
- seo_page_title: '',
- meta_desc: '',
- summary: '',
- description: '',
- contributor: '',
- pub_status: '',
- format: '',
- language_id: '',
- level: '',
- other_topic_text: '',
- course_order: '',
- organization_id: org.id }
- end
-
- context 'with valid params' do
- it 'creates a new Course' do
- expect do
- post :create, params: { course: valid_attributes }
- end.to change(Course, :count).by(1)
- end
-
- it 'assigns a newly created course as @course' do
- post :create, params: { course: valid_attributes }
- expect(assigns(:course)).to be_a(Course)
- expect(assigns(:course)).to be_persisted
- end
-
- it 'creates a new topic, if given' do
- valid_attributes[:other_topic] = '1'
- valid_attributes[:other_topic_text] = 'Some other topic'
- post :create, params: { course: valid_attributes }
- expect(assigns(:course)).to be_a(Course)
- expect(assigns(:course)).to be_persisted
- expect(assigns(:course).topics.last.title).to include('Some other topic')
- end
-
- it 'redirects to the admin edit view of the course' do
- post :create, params: { course: valid_attributes }
- expect(response).to have_http_status(:redirect)
- expect(response).to redirect_to(new_admin_course_lesson_path(Course.find_by(title: valid_attributes[:title])))
- end
-
- it 'adds an existing category if provided' do
- category = FactoryBot.create(:category, organization: org)
- post :create, params: { course: valid_attributes.merge(category_id: category.id) }
- expect(assigns(:course).category).to eq(category)
- end
-
- it 'creates and adds category if new category selected' do
- expect do
- post :create, params: {
- course: valid_attributes.merge(
- category_id: '0',
- category_attributes: {
- name: Faker::Lorem.word,
- organization_id: org.id
- }
- )
- }
- end.to change(Category, :count).by(1)
-
- expect(assigns(:course).category).not_to be_nil
- end
-
- it 're-reders new if repeat category name' do
- @existing_category = FactoryBot.create(:category, organization: org)
- post :create, params: {
- course: valid_attributes.merge(
- category_id: '0',
- category_attributes: {
- name: @existing_category.name,
- organization_id: org.id
- }
- )
- }
- expect(response).to render_template('new')
- end
- end
-
- context 'with invalid params' do
- it 'assigns a newly created but unsaved course as @course' do
- post :create, params: { course: invalid_attributes }
- expect(assigns(:course)).to be_a_new(Course)
- end
-
- it "re-renders the 'new' template" do
- post :create, params: { course: invalid_attributes }
- expect(response).to render_template('new')
- end
- end
- end
-
- describe 'POST #update' do
- context 'with valid params' do
- let(:course1_attributes) do
- course1.attributes.merge(access_level: 'everyone', category_id: category2.id)
- end
-
- it 'updates an existing Course' do
- patch :update, params: { id: course1.to_param, course: course1_attributes, commit: 'Save Course' }
- expect(response).to redirect_to(edit_admin_course_path(course1))
- end
-
- it 'displays appropriate notice for successful course update' do
- patch :update, params: { id: course1.to_param, course: course1_attributes, commit: 'Save Course' }
- expect(flash[:notice]).to eq('Course was successfully updated.')
- end
-
- it 'updates an existing Course, and moves on to lessons' do
- patch :update, params: { id: course1.to_param, course: course1_attributes, commit: 'Save Course and Add Lessons' }
- expect(response).to redirect_to(new_admin_course_lesson_path(course1, course1.lessons.first))
- end
-
- it 'creates a new topic, if given' do
- valid_attributes = course1_attributes
- valid_attributes[:other_topic] = '1'
- valid_attributes[:other_topic_text] = 'Another new topic'
- patch :update, params: { id: course1.to_param, course: valid_attributes }
- expect(assigns(:course).topics.last.title).to include('Another new topic')
- end
-
- describe 'propagation' do
- let(:org2) { FactoryBot.create(:organization) }
- let(:update_params) do
- { id: course1.to_param,
- course: course1_attributes.merge(propagation_org_ids: [org2.id], title: 'Test Course'),
- commit: 'Save Course' }
- end
-
- before do
- course2.update(organization: org2, parent_id: course1.id)
- course1.update(propagation_org_ids: [org2])
- end
-
- it 'propagates changes to selected courses' do
- patch :update, params: update_params
- course2.reload
- expect(course2.title).to eq('Test Course')
- end
-
- it 'displays propagation success message' do
- patch :update, params: update_params
- expect(flash[:notice]).to eq('Course was successfully updated. Changes propagated to courses for 1 subsite.')
- end
-
- it 'creates a new category on org2' do
- expect do
- patch :update, params: update_params
- end.to change { org2.categories.count }.by(1)
- end
-
- it 'does not assign course to main site category' do
- expect do
- patch :update, params: update_params
- end.to_not(change { category2.courses.count })
- end
-
- describe 'new category' do
- let(:new_category_params) do
- { id: course1.to_param,
- course: { category_id: '0',
- category_attributes: { name: 'New Category', organization_id: org.id },
- propagation_org_ids: [org2.id] },
- commit: 'Save Course' }
- end
-
- it 'should create a new category in originating org' do
- expect do
- patch :update, params: new_category_params
- end.to change { org.categories.count }.by(1)
- end
-
- it 'should create a new category in subsite org' do
- expect do
- patch :update, params: new_category_params
- end.to change { org2.categories.count }.by(1)
- end
- end
- end
- end
- end
-
- describe 'POST #sort' do
- it 'should change course order' do
- order_params = { '0' => { id: course2.id, position: 1 }, '1' => { id: course1.id, position: 2 } }
- post :sort, params: { order: order_params }
- expect(course1.reload.course_order).to eq(2)
- expect(course2.reload.course_order).to eq(1)
- end
- end
-end
diff --git a/spec/controllers/admin/dashboard_controller_spec.rb b/spec/controllers/admin/dashboard_controller_spec.rb
index a5191131..986493fd 100644
--- a/spec/controllers/admin/dashboard_controller_spec.rb
+++ b/spec/controllers/admin/dashboard_controller_spec.rb
@@ -81,6 +81,17 @@
end.to change(Course, :count).by(1)
end
+ it 'should redirect to edit page for new course' do
+ post :add_imported_course, params: { course_id: importable_course1.id }
+ new_course = Course.where(parent: importable_course1).first
+ expect(response).to redirect_to(edit_admin_course_path(new_course))
+ end
+
+ it 'should assign correct flash' do
+ post :add_imported_course, params: { course_id: importable_course1.id }
+ expect(flash[:notice]).to eq('Congrats! You have just imported a PLA course.')
+ end
+
it 'should create new subdomain course with new category with same name as imported course' do
post :add_imported_course, params: { course_id: importable_course1.id }
course_category = org.courses.last.category
diff --git a/spec/controllers/admin/lessons_controller_spec.rb b/spec/controllers/admin/lessons_controller_spec.rb
index 29b53bf1..3eb617c3 100644
--- a/spec/controllers/admin/lessons_controller_spec.rb
+++ b/spec/controllers/admin/lessons_controller_spec.rb
@@ -3,28 +3,32 @@
require 'rails_helper'
describe Admin::LessonsController do
- let(:org) { FactoryBot.create(:organization) }
- let(:admin) { FactoryBot.create(:user, :admin, organization: org) }
- let(:other_subsite_admin) { FactoryBot.create(:user, :admin) }
- let(:course) { FactoryBot.create(:course, organization: org) }
- let(:lesson1) { FactoryBot.create(:lesson, title: 'Lesson1', course: course, lesson_order: 1) }
- let(:lesson2) { FactoryBot.create(:lesson, title: 'Lesson2', course: course, lesson_order: 2) }
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:pla_admin) { FactoryBot.create(:user, :admin, organization: pla) }
+ let(:subsite) { FactoryBot.create(:organization) }
+ let(:subsite_admin) { FactoryBot.create(:user, :admin, organization: subsite) }
+
+ let!(:pla_course) { FactoryBot.create(:course, organization: pla) }
+ let!(:child_course) { FactoryBot.create(:course, organization: subsite, parent: pla_course) }
+
+ let(:lesson1) { FactoryBot.create(:lesson, title: 'Lesson1', course: pla_course, lesson_order: 1) }
+ let(:lesson2) { FactoryBot.create(:lesson, title: 'Lesson2', course: pla_course, lesson_order: 2) }
before(:each) do
- @request.host = "#{org.subdomain}.test.host"
- sign_in admin
+ @request.host = "#{pla.subdomain}.test.host"
+ sign_in pla_admin
end
describe 'GET #edit' do
it 'assigns the requested lesson as @lesson' do
- get :edit, params: { course_id: course.to_param, id: lesson1.id.to_param }
+ get :edit, params: { course_id: pla_course.to_param, id: lesson1.id.to_param }
expect(assigns(:lesson)).to eq(lesson1)
end
end
describe 'GET #new' do
it 'assigns a new lesson as @lesson' do
- get :new, params: { course_id: course.to_param }
+ get :new, params: { course_id: pla_course.to_param }
expect(assigns(:lesson)).to be_a_new(Lesson)
end
end
@@ -67,18 +71,18 @@
context 'unauthorized user' do
before do
- sign_out admin
- sign_in other_subsite_admin
+ sign_out pla_admin
+ sign_in subsite_admin
end
it 'should respond with unauthorized flash' do
- post :create, params: { course_id: course.to_param, lesson: valid_attributes }, format: :js
+ post :create, params: { course_id: pla_course.to_param, lesson: valid_attributes }, format: :js
expect(flash[:alert]).to eq('You are not authorized to perform this action.')
end
it 'should not create a lesson' do
expect do
- post :create, params: { course_id: course.to_param, lesson: valid_attributes }, format: :js
+ post :create, params: { course_id: pla_course.to_param, lesson: valid_attributes }, format: :js
end.to_not change(Lesson, :count)
end
end
@@ -86,50 +90,56 @@
context 'with valid params' do
it 'creates a new lesson' do
expect do
- post :create, params: { course_id: course.to_param, lesson: valid_attributes }, format: :js
- end.to change(Lesson, :count).by(1)
+ post :create, params: { course_id: pla_course.to_param, lesson: valid_attributes }, format: :js
+ end.to change { pla.lessons.count }.by(1)
+ end
+
+ it 'creates a new lesson on child courses' do
+ expect do
+ post :create, params: { course_id: pla_course.to_param, lesson: valid_attributes }, format: :js
+ end.to change { subsite.lessons.count }.by(1)
end
- it 'creates a new assessment' do
+ it 'creates a new assessment for both parent and child' do
expect do
- post :create, params: { course_id: course.to_param, lesson: assessment_attributes }, format: :js
- end.to change(Lesson, :count).by(1)
+ post :create, params: { course_id: pla_course.to_param, lesson: assessment_attributes }, format: :js
+ end.to change { Lesson.where(is_assessment: true).count }.by(2)
end
it 'assigns a new assessment to the end of the course lessons' do
- FactoryBot.create(:lesson, course: course)
- post :create, params: { course_id: course.to_param, lesson: assessment_attributes }, format: :js
+ FactoryBot.create(:lesson, course: pla_course)
+ post :create, params: { course_id: pla_course.to_param, lesson: assessment_attributes }, format: :js
lesson = Lesson.last
expect(lesson.lesson_order).to eq(2)
end
it 'does not create a second assessment' do
- post :create, params: { course_id: course.to_param, lesson: assessment_attributes }, format: :js
+ post :create, params: { course_id: pla_course.to_param, lesson: assessment_attributes }, format: :js
expect do
- post :create, params: { course_id: course.to_param, lesson: assessment_attributes, title: 'something different' }
+ post :create, params: { course_id: pla_course.to_param, lesson: assessment_attributes, title: 'something different' }
end.to_not change(Lesson, :count)
end
it 'assigns a new lesson as @lesson' do
- post :create, params: { course_id: course.to_param, lesson: valid_attributes }, format: :js
+ post :create, params: { course_id: pla_course.to_param, lesson: valid_attributes }, format: :js
expect(assigns(:lesson)).to be_a(Lesson)
expect(assigns(:lesson)).to be_persisted
end
it 'redirects to the admin edit view of the lesson' do
- post :create, params: { course_id: course.to_param, lesson: valid_attributes, format: :js }
- expect(response).to redirect_to(edit_admin_course_lesson_path(course, 'lesson-your-load-man'))
+ post :create, params: { course_id: pla_course.to_param, lesson: valid_attributes, format: :js }
+ expect(response).to redirect_to(edit_admin_course_lesson_path(pla_course, 'lesson-your-load-man'))
end
end
context 'with invalid params' do
it 'assigns a newly created but unsaved lesson as @lesson' do
- post :create, params: { course_id: course.to_param, lesson: invalid_attributes }, format: :js
+ post :create, params: { course_id: pla_course.to_param, lesson: invalid_attributes }, format: :js
expect(assigns(:lesson)).to be_a_new(Lesson)
end
it "re-renders the 'new' template" do
- post :create, params: { course_id: course.to_param, lesson: invalid_attributes }, format: :js
+ post :create, params: { course_id: pla_course.to_param, lesson: invalid_attributes }, format: :js
expect(response).to render_template('new')
end
end
@@ -138,7 +148,7 @@
describe 'POST #update' do
context 'with valid params' do
it 'updates an existing Lesson' do
- update_params = { course_id: course.to_param, id: lesson1.to_param,
+ update_params = { course_id: pla_course.to_param, id: lesson1.to_param,
lesson: lesson1.attributes, commit: 'Save Lesson' }
patch :update, params: update_params
expect(response).to have_http_status(:redirect)
@@ -147,34 +157,42 @@
it 'updates with duration as a string' do
@lesson_attributes = lesson1.attributes
@lesson_attributes['duration'] = '1:00'
- update_params = { course_id: course.to_param, id: lesson1.to_param,
+ update_params = { course_id: pla_course.to_param, id: lesson1.to_param,
lesson: @lesson_attributes, commit: 'Save Lesson' }
patch :update, params: update_params
expect(response).to have_http_status(:redirect)
end
it 'propagates updates to child lessons' do
- org = create(:organization)
- child_course = create(:course, organization: org)
- lesson1_child = create(:lesson, course: child_course, parent: lesson1)
- update_params = { course_id: course.to_param, id: lesson1.to_param,
- lesson: lesson1.attributes.merge(propagation_org_ids: [org.id], title: 'Test Lesson'),
+ child_lesson = create(:lesson, course: child_course, parent: lesson1)
+ update_params = { course_id: pla_course.to_param, id: lesson1.to_param,
+ lesson: lesson1.attributes.merge(title: 'New Lesson Title'),
commit: 'Save Lesson' }
patch :update, params: update_params
- lesson1_child.reload
- expect(lesson1_child.title).to eq('Test Lesson')
+ expect(child_lesson.reload.title).to eq('New Lesson Title')
end
end
end
describe 'POST #sort' do
- it 'should change course order' do
- order_params = { '0' => { id: lesson2.id, position: 1 }, '1' => { id: lesson1.id, position: 2 } }
+ let(:order_params) { { '0' => { id: lesson2.id, position: 1 }, '1' => { id: lesson1.id, position: 2 } } }
+
+ it 'should change lesson order' do
post :sort, params: { order: order_params }
expect(lesson1.reload.lesson_order).to eq(2)
expect(lesson2.reload.lesson_order).to eq(1)
end
+
+ it 'should change child lesson order' do
+ child_lesson1 = FactoryBot.create(:lesson, parent: lesson1)
+ child_lesson2 = FactoryBot.create(:lesson, parent: lesson2)
+
+ post :sort, params: { order: order_params }
+
+ expect(child_lesson1.reload.lesson_order).to eq(2)
+ expect(child_lesson2.reload.lesson_order).to eq(1)
+ end
end
end
diff --git a/spec/controllers/attachments_controller_spec.rb b/spec/controllers/attachments_controller_spec.rb
new file mode 100644
index 00000000..56b5b5df
--- /dev/null
+++ b/spec/controllers/attachments_controller_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe AttachmentsController do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:pla_course) { FactoryBot.create(:course, organization: pla) }
+ let(:subsite_course) { FactoryBot.create(:course, parent: pla_course) }
+ let(:subsite) { subsite_course.organization }
+ let(:subsite_user) { FactoryBot.create(:user, organization: subsite) }
+ let(:other_subsite_user) { FactoryBot.create(:user) }
+
+ let(:document) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf') }
+ let(:attachment) { FactoryBot.create(:attachment, document: document, course: pla_course) }
+
+ describe '#show' do
+ context 'visitor on subsite' do
+ before do
+ request.host = "#{subsite.subdomain}.example.com"
+ end
+
+ it 'allows the visitor to view an attachment' do
+ get :show, params: { id: attachment.id }
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'does not allow visitor to view attachment if course is private' do
+ subsite_course.update!(access_level: 'authenticated_users')
+ get :show, params: { id: attachment.id }
+ expect(response).to redirect_to(root_path)
+ end
+ end
+
+ context 'user on subsite' do
+ before do
+ request.host = "#{subsite.subdomain}.example.com"
+ sign_in subsite_user
+ end
+
+ it 'allows the user to view an attachment' do
+ get :show, params: { id: attachment.id }
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'allows user to view attachment if course is private' do
+ subsite_course.update!(access_level: 'authenticated_users')
+ get :show, params: { id: attachment.id }
+ expect(response).to have_http_status(:success)
+ end
+ end
+
+ context 'user on another subsite' do
+ before do
+ request.host = "#{other_subsite_user.organization.subdomain}.example.com"
+ sign_in other_subsite_user
+ end
+
+ it 'does not allow user to view attachment' do
+ get :show, params: { id: attachment.id }
+ expect(response).to redirect_to(root_path)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/course_progresses_controller_spec.rb b/spec/controllers/course_progresses_controller_spec.rb
index 83716a9b..e87c27fc 100644
--- a/spec/controllers/course_progresses_controller_spec.rb
+++ b/spec/controllers/course_progresses_controller_spec.rb
@@ -7,14 +7,12 @@
let(:organization) { user.organization }
let(:course1) { FactoryBot.create(:course, title: 'Course 1', language: @english, organization: organization) }
let(:course2) { FactoryBot.create(:course, title: 'Course 2', language: @english, organization: organization) }
- let(:lesson1) { create(:lesson, lesson_order: 1) }
- let(:lesson2) { create(:lesson, lesson_order: 2) }
- let(:lesson3) { create(:lesson, lesson_order: 3) }
- let(:lesson4) { create(:lesson) }
+ let(:lesson1) { create(:lesson, lesson_order: 1, course: course1) }
+ let(:lesson2) { create(:lesson, lesson_order: 2, course: course1) }
+ let(:lesson3) { create(:lesson, lesson_order: 3, course: course1) }
+ let(:lesson4) { create(:lesson, course: course2) }
before(:each) do
- course1.lessons << [lesson1, lesson2, lesson3]
- course2.lessons << [lesson4]
request.host = "#{organization.subdomain}.example.com"
end
diff --git a/spec/controllers/courses_controller_spec.rb b/spec/controllers/courses_controller_spec.rb
index 4263480b..803062e8 100644
--- a/spec/controllers/courses_controller_spec.rb
+++ b/spec/controllers/courses_controller_spec.rb
@@ -81,30 +81,6 @@
end
end
- describe 'GET #view_attachment' do
- context 'when logged in' do
- before(:each) do
- sign_in user
- end
-
- it 'allows the user to view an uploaded file' do
- file = fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf')
- course1.attachments.create(document: file, doc_type: 'post-course')
- get :view_attachment, params: { course_id: course1, attachment_id: course1.attachments.first.id }
- expect(response).to have_http_status(:success)
- end
- end
-
- context 'when not logged in' do
- it 'should allow view' do
- file = fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf')
- course1.attachments.create(document: file, doc_type: 'post-course')
- get :view_attachment, params: { course_id: course1, attachment_id: course1.attachments.first.id }
- expect(response).to have_http_status(:success)
- end
- end
- end
-
describe 'GET #skills' do
context 'when logged in' do
before(:each) do
diff --git a/spec/controllers/lessons_controller_spec.rb b/spec/controllers/lessons_controller_spec.rb
index e367651a..faf53722 100644
--- a/spec/controllers/lessons_controller_spec.rb
+++ b/spec/controllers/lessons_controller_spec.rb
@@ -3,17 +3,15 @@
require 'rails_helper'
describe LessonsController do
- let(:org) { FactoryBot.create(:default_organization) }
+ let(:org) { FactoryBot.create(:organization, login_required: false) }
let(:user) { FactoryBot.create(:user, organization: org) }
let(:course) { FactoryBot.create(:course, organization: org) }
let!(:lesson1) { FactoryBot.create(:lesson, lesson_order: 1, course: course) }
let!(:lesson2) { FactoryBot.create(:lesson, lesson_order: 2, course: course) }
let!(:lesson3) { FactoryBot.create(:lesson, lesson_order: 3, course: course) }
- let!(:draft_lesson) { FactoryBot.create(:lesson, course: course, pub_status: 'D') }
- let!(:archived_lesson) { FactoryBot.create(:lesson, course: course, pub_status: 'A') }
before(:each) do
- @request.host = 'www.test.host'
+ @request.host = "#{org.subdomain}.test.host"
sign_in user
end
@@ -58,24 +56,24 @@
expect(response).to have_http_status(:success)
end
- it 'sets correct flash for draft lessons' do
- get :show, params: { course_id: course.to_param, id: draft_lesson.id }, format: :json
- expect(flash[:notice]).to eq('That lesson is not available at this time.')
- end
-
- it 'redirects to root for archived lessons' do
- get :show, params: { course_id: course.to_param, id: draft_lesson.id }, format: :json
- expect(response).to redirect_to(root_path)
- end
+ context 'preview' do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:pla_course) { FactoryBot.create(:course_with_lessons, organization: pla) }
+ let(:pla_lesson) { pla_course.lessons.first }
+ let(:subsite_admin) { FactoryBot.create(:user, :admin, organization: org) }
- it 'sets correct flash for archived lessons' do
- get :show, params: { course_id: course.to_param, id: archived_lesson.id }, format: :json
- expect(flash[:notice]).to eq('That lesson is no longer available.')
- end
+ it 'authorizes course preview' do
+ expect(@controller).to receive(:authorize).with(pla_course, :preview?)
+ allow(@controller).to receive(:verify_authorized)
+ get :show, params: { course_id: pla_course.to_param, id: pla_lesson.to_param, preview: true }
+ end
- it 'redirects to root for archived lessons' do
- get :show, params: { course_id: course.to_param, id: archived_lesson.id }, format: :json
- expect(response).to redirect_to(root_path)
+ it 'should respond with 200 if accessed by subsite admin' do
+ sign_out user
+ sign_in subsite_admin
+ get :show, params: { course_id: pla_course.to_param, id: pla_lesson.to_param, preview: true }
+ expect(response).to have_http_status :ok
+ end
end
end
@@ -116,6 +114,36 @@
post :complete, params: { course_id: course.to_param, lesson_id: lesson1.to_param }, format: :json
end.to_not change(LessonCompletion, :count)
end
+
+ context 'preview' do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:pla_course) { FactoryBot.create(:course_with_lessons, organization: pla) }
+ let(:pla_lesson) { pla_course.lessons.first }
+ let(:subsite_admin) { FactoryBot.create(:user, :admin, organization: org) }
+
+ before do
+ sign_out user
+ sign_in subsite_admin
+ end
+
+ it 'authorizes course preview' do
+ expect(@controller).to receive(:authorize).with(pla_course, :preview?)
+ allow(@controller).to receive(:verify_authorized)
+ post :complete, params: { course_id: pla_course.to_param, lesson_id: pla_lesson.to_param, preview: true }, format: :json
+ end
+
+ it 'includes preview parameter if a preview lesson is finished' do
+ post :complete, params: { course_id: pla_course.to_param, lesson_id: pla_lesson.to_param, preview: true }, format: :json
+ expect(JSON.parse(response.body)['redirect_path']).to eq(course_lesson_lesson_complete_path(pla_course, pla_lesson, preview: true))
+ end
+
+ it 'returns to course preview if finishing a preview course' do
+ pla_assessment = pla_course.lessons.last
+ pla_assessment.update(is_assessment: true)
+ post :complete, params: { course_id: pla_course.to_param, lesson_id: pla_assessment.to_param, preview: true }, format: :json
+ expect(JSON.parse(response.body)['redirect_path']).to eq(admin_course_preview_path(pla_course.to_param))
+ end
+ end
end
describe 'GET #lesson_complete' do
diff --git a/spec/factories/attachments.rb b/spec/factories/attachments.rb
index 205caff9..0da4fa8f 100644
--- a/spec/factories/attachments.rb
+++ b/spec/factories/attachments.rb
@@ -2,7 +2,7 @@
FactoryBot.define do
factory :attachment do
- document_file_name 'post-course-info.pdf'
+ document_file_name 'additional-resource-info.pdf'
document_content_type 'application/pdf'
document_file_size 22
document_updated_at '2015-10-10 20:00:00'
diff --git a/spec/factories/lessons.rb b/spec/factories/lessons.rb
index e2979095..5d376da3 100644
--- a/spec/factories/lessons.rb
+++ b/spec/factories/lessons.rb
@@ -2,11 +2,12 @@
FactoryBot.define do
factory :lesson do
- title 'Lesson 1'
+ sequence :title do |n|
+ "Lesson #{n}"
+ end
summary 'Lesson summary'
duration 90
lesson_order 1
- pub_status 'P'
story_line { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip'), 'application/zip') }
course
end
@@ -14,7 +15,6 @@
factory :lesson_without_story, class: Lesson do
title 'Lesson without story'
summary 'Lesson summary'
- pub_status 'P'
duration 90
lesson_order 1
course
diff --git a/spec/features/admin/course_preview_spec.rb b/spec/features/admin/course_preview_spec.rb
new file mode 100644
index 00000000..5f60aeea
--- /dev/null
+++ b/spec/features/admin/course_preview_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require 'feature_helper'
+
+feature 'Admin previews a PLA course' do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:subsite_admin) { FactoryBot.create(:user, :admin) }
+ let(:org) { subsite_admin.organization }
+
+ let!(:pla_course) { FactoryBot.create(:course_with_lessons, organization: pla) }
+
+ before do
+ switch_to_subdomain(org.subdomain)
+ login_as subsite_admin
+ visit admin_dashboard_index_path
+ end
+
+ scenario 'admin clicks course preview link from course import view' do
+ click_link 'Import DigitalLearn Courses'
+ click_link 'Preview Course'
+ expect(current_path).to eq(admin_course_preview_path(pla_course))
+ expect(page).to have_content('You are previewing this course.')
+
+ expect(page).to have_link 'Return to Admin Dashboard'
+ expect(page).to have_link 'Import'
+
+ expect(page).to_not have_link 'Edit Course >>'
+
+ # Return to import courses view
+ click_link 'Return to Admin Dashboard'
+ expect(current_path).to eq(admin_import_courses_path)
+ click_link 'Preview Course'
+
+ # Course preview
+ expect(page).to have_content(pla_course.title)
+ pla_course.lessons.each do |lesson|
+ expect(page).to have_content(lesson.title)
+ expect(page).to have_content(lesson.summary)
+ end
+
+ # Lesson preview
+ lesson = pla_course.lessons.first
+ click_link 'Start Course'
+ expect(page).to have_current_path(course_lesson_path(pla_course, lesson, preview: true))
+ expect(page).to have_content(lesson.title)
+ expect(page).to have_content('You are previewing this course.')
+
+ # Skip to next lesson
+ click_link 'Skip to next Activity'
+ next_lesson = pla_course.lesson_after(lesson)
+ expect(page).to have_current_path(course_lesson_path(pla_course, next_lesson, preview: true))
+
+ # Lesson playlist navigation
+ last_lesson = pla_course.lessons.last
+ within('.playlist') do
+ find('.lesson-title', text: last_lesson.title).ancestor('.lesson-listing_link').click
+ end
+
+ expect(page).to have_current_path(course_lesson_path(pla_course, last_lesson, preview: true))
+ expect(page).to have_content(last_lesson.title)
+ expect(page).to have_content('You are previewing this course.')
+ end
+
+ scenario 'admin previews course and clicks lesson tiles' do
+ lesson = pla_course.lessons.first
+ click_link 'Import DigitalLearn Courses'
+ click_link 'Preview Course'
+ expect(page).to have_content(lesson.title)
+ find('.lesson-title', text: lesson.title).ancestor('.lesson-tile').click
+ expect(page).to have_current_path(course_lesson_path(pla_course, lesson, preview: true))
+ end
+
+ scenario 'admin finishes lessons in preview mode' do
+ first_lesson = pla_course.lessons.first
+ second_lesson = pla_course.lessons.second
+ last_lesson = pla_course.lessons.last
+ last_lesson.update(is_assessment: true)
+
+ visit course_lesson_lesson_complete_path(pla_course, first_lesson, preview: true)
+ expect(page).to have_content("You've completed Activity 1: #{first_lesson.title}")
+
+ # Repeat activity link
+ click_link 'Repeat Activity'
+ expect(page).to have_current_path(course_lesson_path(pla_course, first_lesson, preview: true))
+
+ # Continue link
+ visit course_lesson_lesson_complete_path(pla_course, first_lesson, preview: true)
+ click_link 'Continue'
+ expect(page).to have_current_path(course_lesson_path(pla_course, second_lesson, preview: true))
+ end
+
+ scenario 'admin imports course from preview' do
+ visit admin_course_preview_path(pla_course.id)
+ click_link 'Import'
+
+ new_course = org.courses.where(parent: pla_course).first
+
+ expect(current_path).to eq(edit_admin_course_path(new_course))
+ end
+end
diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb
index 2bb073b7..20f4e070 100644
--- a/spec/features/admin/dashboard_spec.rb
+++ b/spec/features/admin/dashboard_spec.rb
@@ -5,6 +5,7 @@
feature 'Admin visits dashboard' do
let!(:default_organization) { FactoryBot.create(:default_organization) }
let(:admin) { FactoryBot.create(:user, :admin) }
+ let!(:course) { FactoryBot.create(:course, pub_status: 'D', organization: admin.organization) }
before do
switch_to_subdomain(admin.organization.subdomain)
@@ -44,4 +45,11 @@
expect(page).to_not have_link(link)
end
end
+
+ scenario 'changes course publication status', js: true do
+ expect(page).to have_select("course_#{course.id}", selected: 'Draft')
+ select('Published', from: "course_#{course.id}")
+ visit admin_dashboard_index_path
+ expect(page).to have_select("course_#{course.id}", selected: 'Published')
+ end
end
diff --git a/spec/features/admin/edit_course_spec.rb b/spec/features/admin/edit_course_spec.rb
new file mode 100644
index 00000000..bec384c4
--- /dev/null
+++ b/spec/features/admin/edit_course_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require 'feature_helper'
+
+feature 'Admin user updates course' do
+ let(:story_line) do
+ fixture_file_upload(Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip'), 'application/zip')
+ end
+
+ let(:file_attachment) do
+ fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf')
+ end
+
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:user) { FactoryBot.create(:user, :admin) }
+ let(:org) { user.organization }
+ let(:pla_course) { FactoryBot.create(:course, organization: pla) }
+ let(:subsite_course) { FactoryBot.create(:course, organization: org, parent: pla_course) }
+ let!(:course_attachment) do
+ FactoryBot.create(:attachment, document: file_attachment, doc_type: 'additional-resource', course: subsite_course)
+ end
+ let!(:text_copy_attachment) do
+ FactoryBot.create(:attachment, document: file_attachment, doc_type: 'text-copy', course: subsite_course)
+ end
+
+ let!(:topic) { FactoryBot.create(:topic) }
+ let!(:category) { FactoryBot.create(:category, organization: org) }
+ let!(:disabled_category) { FactoryBot.create(:category, :disabled, organization: org) }
+
+ before(:each) do
+ switch_to_subdomain(org.subdomain)
+ log_in_with user.email, user.password
+ end
+
+ context 'imported PLA Course' do
+ scenario 'sees restricted version of form' do
+ visit edit_admin_course_path(subsite_course)
+ ['Title',
+ 'Contributor',
+ 'Course Summary',
+ 'Course Description',
+ topic.title,
+ 'Course Language',
+ 'Course Format',
+ 'Course Level',
+ 'SEO Page Title',
+ 'SEO Meta Description'].each do |label|
+ expect(page).to have_field(label, disabled: true)
+ end
+
+ expect(page).to_not have_field('Other Topic')
+ expect(page).to_not have_field('course_other_topic_text')
+
+ expect(page).to have_link('Delete', count: 1)
+ expect(page).to have_css('.attachment-upload-fields', count: 1)
+ expect(page).to have_link('Add Attachment', count: 1)
+
+ expect(page).to have_content('Text copies of the course to allow users to download content and view offline or follow along with the online course.')
+ expect(page).to have_content('Upload any supplemental materials for further learning. These files are available to users after completing the course.')
+
+ expect(page).to have_button('Save Course')
+ expect(page).to have_content('If you wish to edit additional details of this course and use the PLA-created Storyline files, please contact a PLA Administrator.')
+ expect(page).to have_link('contact a PLA Administrator')
+ end
+
+ scenario 'changes access level for course' do
+ visit edit_admin_course_path(subsite_course)
+ within(:css, 'main') do
+ select('Authenticated Users', from: 'course_access_level')
+ click_button 'Save Course'
+ end
+ expect(current_path).to eq(edit_admin_course_path(subsite_course))
+ expect(page).to have_content('Course was successfully updated.')
+ expect(page).to have_select('course_access_level', selected: 'Authenticated Users')
+ end
+
+ scenario 'changes publication status for course' do
+ visit edit_admin_course_path(subsite_course)
+ expect(page).to have_select('Publication Status', selected: 'Published')
+ within(:css, 'main') do
+ select('Draft', from: 'Publication Status')
+ click_button 'Save Course'
+ end
+ expect(current_path).to eq(edit_admin_course_path(subsite_course))
+ expect(page).to have_content('Course was successfully updated.')
+ expect(page).to have_select('Publication Status', selected: 'Draft')
+
+ end
+
+ scenario 'selects existing category for course' do
+ visit edit_admin_course_path(subsite_course)
+ within(:css, 'main') do
+ select(category.name, from: 'course_category_id')
+ click_button 'Save Course'
+ end
+ expect(page).to have_select('course_category_id', selected: category.name)
+ end
+
+ scenario 'attempts to add a duplicate category to course' do
+ visit edit_admin_course_path(subsite_course)
+ within(:css, 'main') do
+ select('Create new category', from: 'course_category_id')
+ fill_in :course_category_attributes_name, with: category.name
+ click_button 'Save Course'
+ end
+ expect(current_path).to eq(admin_course_path(subsite_course))
+ expect(page).to have_content('Category Name is already in use by your organization.')
+ expect(page).to have_select('course_category_id', selected: 'Create new category')
+ expect(page).to have_selector(:css, ".field_with_errors #course_category_attributes_name[value='#{category.name}']")
+ end
+
+ scenario 'adds a new category to course' do
+ visit edit_admin_course_path(subsite_course)
+ new_word = Faker::Lorem.word
+ within(:css, 'main') do
+ select('Create new category', from: 'course_category_id')
+ fill_in :course_category_attributes_name, with: "#{category.name}_#{new_word}"
+ click_button 'Save Course'
+ end
+ expect(page).to have_select('course_category_id', selected: "#{category.name}_#{new_word}")
+ end
+
+ scenario 'can see which categories are disabled' do
+ visit edit_admin_course_path(subsite_course)
+ expect(page).to have_select('course_category_id', with_options: [category.name.to_s, "#{disabled_category.name} (disabled)"])
+ end
+
+ scenario 'can upload additional resource attachments' do
+ visit edit_admin_course_path(subsite_course)
+ attach_file 'Additional Resources', Rails.root.join('spec', 'fixtures', 'testfile.pdf')
+ click_button 'Save Course'
+ expect(page).to have_content('Course was successfully updated.')
+ expect(page).to have_content('testfile.pdf')
+ end
+
+ scenario 'updates content for further learning' do
+ visit edit_admin_course_path(subsite_course)
+ fill_in 'Content for Further Learning', with: 'New content for further learning'
+ click_button 'Save Course'
+ expect(page).to have_content('Course was successfully updated.')
+ expect(page).to have_field('Content for Further Learning', with: 'New content for further learning')
+ end
+
+ scenario 'can preview course' do
+ skip 'TODO: course preview spec'
+ end
+ end
+
+ context 'subsite created course' do
+ let(:custom_subsite_course) { FactoryBot.create(:course, organization: org) }
+
+ scenario 'attempts to upload attachments to course' do
+ visit edit_admin_course_path(custom_subsite_course)
+ attach_file 'Text Copies of Course', Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip')
+ attach_file 'Additional Resources', Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip')
+ click_button 'Save Course'
+ expect(page).to have_content('Attachments document is invalid. Only PDF, Word, PowerPoint, or Excel files are allowed.', count: 1)
+ end
+
+ scenario 'can edit course title' do
+ visit edit_admin_course_path(custom_subsite_course)
+ fill_in 'Title', with: 'New Course Title'
+ click_button 'Save Course'
+ expect(page).to have_content('Course was successfully updated.')
+ expect(current_path).to eq(edit_admin_course_path(custom_subsite_course.reload))
+ expect(page).to have_field('Title', with: 'New Course Title')
+ end
+
+ scenario 'can preview course' do
+ skip 'TODO: Course preview spec'
+ end
+ end
+end
diff --git a/spec/features/admin/new_course_spec.rb b/spec/features/admin/new_course_spec.rb
index 908ce3e6..d6759a8f 100644
--- a/spec/features/admin/new_course_spec.rb
+++ b/spec/features/admin/new_course_spec.rb
@@ -21,11 +21,10 @@ def fill_basic_course_info
fill_in :course_description, with: 'Description for new course'
check 'Topic A'
check 'Other Topic'
- fill_in :course_other_topic_text, with: 'Some New Topic'
+ fill_in :course_course_topics_attributes_0_topic_attributes_title, with: 'Some New Topic'
select('Desktop', from: 'course_format')
select('English', from: 'course_language_id')
select('Beginner', from: 'course_level')
- select('Published', from: 'course_pub_status')
end
before(:each) do
@@ -34,8 +33,27 @@ def fill_basic_course_info
visit new_admin_course_path
end
+ scenario 'toggles course publication status' do
+ fill_basic_course_info
+ expect(page).to have_select('Publication Status', selected: 'Draft')
+ select('Published', from: 'Publication Status')
+ click_button 'Save Course'
+ expect(page).to have_content('Course was successfully created.')
+ expect(page).to have_select('Publication Status', selected: 'Published')
+ select('Archived', from: 'Publication Status')
+ click_button 'Save Course'
+ visit admin_courses_path
+ expect(page).to_not have_content('New Course Title')
+ end
+
+ scenario 'assigns topics' do
+ fill_basic_course_info
+ click_button 'Save Course'
+ expect(page).to have_field('Topic A', checked: true)
+ expect(page).to have_field('Some New Topic', checked: true)
+ end
+
scenario 'creates course with new category' do
- expect(page).to have_content('Course Information')
fill_basic_course_info
select('Create new category', from: 'course_category_id')
fill_in :course_category_attributes_name, with: new_category_name
@@ -67,7 +85,7 @@ def fill_basic_course_info
expect(page).to have_selector(:css, ".field_with_errors #course_category_attributes_name[value='#{category.name}']")
end
- scenario 'adds supplemental materials and post-course supplemental materials' do
+ scenario 'adds text copies and additional-resource materials' do
fill_basic_course_info
attach_file 'Text Copies of Course', Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip')
attach_file 'Additional Resources', Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip')
@@ -84,13 +102,12 @@ def fill_basic_course_info
scenario 'adds a lesson' do
visit edit_admin_course_path(course_id: course, id: course.id)
- click_button 'Save Course and Add Lessons'
+ click_button 'Save & Edit Lessons'
expect(current_path).to eq(new_admin_course_lesson_path(course))
fill_in :lesson_title, with: 'New Lesson Title'
fill_in :lesson_summary, with: 'Summary for new lesson'
fill_in :lesson_duration, with: '05:15'
attach_file 'Articulate Storyline Package', Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip')
- select 'Published', from: 'Publication Status'
click_button 'Save Lesson'
expect(page).to have_content('Lesson was successfully created.')
expect(current_path).to eq(edit_admin_course_lesson_path(course.to_param, Lesson.last.to_param))
@@ -102,7 +119,7 @@ def fill_basic_course_info
scenario 'attempts to add two assessments' do
FactoryBot.create(:lesson, course: course, is_assessment: true)
visit edit_admin_course_path(course_id: course, id: course.id)
- click_button 'Save Course and Edit Lessons'
+ click_button 'Save & Edit Lessons'
click_link 'Add Another Lesson'
expect(current_path).to eq(new_admin_course_lesson_path(course))
page.find('#lesson_is_assessment_true').click
diff --git a/spec/features/admin/update_course_spec.rb b/spec/features/admin/update_course_spec.rb
deleted file mode 100644
index 657954d7..00000000
--- a/spec/features/admin/update_course_spec.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-# frozen_string_literal: true
-
-require 'feature_helper'
-
-feature 'Admin user updates course' do
- before(:each) do
- @topic = FactoryBot.create(:topic)
- @spanish = FactoryBot.create(:spanish_lang)
- @story_line = Rack::Test::UploadedFile.new(Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip'), 'application/zip')
-
- @organization = FactoryBot.create(:organization)
- @user = FactoryBot.create(:user, organization: @organization)
- @user.add_role(:admin)
- @user.add_role(:admin, @organization)
-
- @course = FactoryBot.create(:course, organization: @organization)
- @category = FactoryBot.create(:category, organization: @organization)
- @disabled_category = FactoryBot.create(:category, :disabled, organization: @organization)
- switch_to_subdomain('chipublib')
- log_in_with @user.email, @user.password
- end
-
- scenario 'selects existing category for course' do
- visit edit_admin_course_path(@course)
- expect(page).to have_content('Course Information')
- within(:css, 'main') do
- select(@category.name, from: 'course_category_id')
- click_button 'Save Course'
- end
- expect(current_path).to eq(edit_admin_course_path(Course.last))
- expect(page).to have_select('course_category_id', selected: @category.name)
- end
-
- scenario 'attempts to add a duplicate category to course' do
- visit edit_admin_course_path(@course)
- expect(page).to have_content('Course Information')
- within(:css, 'main') do
- select('Create new category', from: 'course_category_id')
- fill_in :course_category_attributes_name, with: @category.name
- click_button 'Save Course'
- end
- expect(current_path).to eq(admin_course_path(Course.last))
- expect(page).to have_content('Category Name is already in use by your organization.')
- expect(page).to have_select('course_category_id', selected: 'Create new category')
- expect(page).to have_selector(:css, ".field_with_errors #course_category_attributes_name[value='#{@category.name}']")
- end
-
- scenario 'adds a new category to course' do
- visit edit_admin_course_path(@course)
- new_word = Faker::Lorem.word
- expect(page).to have_content('Course Information')
- within(:css, 'main') do
- select('Create new category', from: 'course_category_id')
- fill_in :course_category_attributes_name, with: "#{@category.name}_#{new_word}"
- click_button 'Save Course'
- end
- expect(current_path).to eq(edit_admin_course_path(Course.last))
- expect(page).to have_select('course_category_id', selected: "#{@category.name}_#{new_word}")
- end
-
- scenario 'can see which categories are disabled' do
- visit edit_admin_course_path(@course)
- expect(page).to have_select('course_category_id', with_options: [@category.name.to_s, "#{@disabled_category.name} (disabled)"])
- end
-
- scenario 'attempts to upload attachments to course' do
- visit edit_admin_course_path(@course)
- attach_file 'Text Copies of Course', Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip')
- attach_file 'Additional Resources', Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip')
- click_button 'Save Course'
- expect(page).to have_content('Attachments document is invalid. Only PDF, Word, PowerPoint, or Excel files are allowed.', count: 1)
- end
-end
diff --git a/spec/features/subdomain_admin_course_import_spec.rb b/spec/features/subdomain_admin_course_import_spec.rb
index 8be5ed87..b9606408 100644
--- a/spec/features/subdomain_admin_course_import_spec.rb
+++ b/spec/features/subdomain_admin_course_import_spec.rb
@@ -90,7 +90,7 @@
click_link('Import Course', href: admin_dashboard_add_imported_course_path(course_id: importable_course1.id))
end.to change(Category, :count).by(1)
- expect(page).to have_content('Course Information')
+ expect(page).to have_content('Edit This Course')
expect(page).to have_select('course_category_id', selected: pla_category.name)
end
@@ -101,7 +101,7 @@
click_link('Import Course', href: admin_dashboard_add_imported_course_path(course_id: importable_course3.id))
end.not_to change(Category, :count)
- expect(page).to have_content('Course Information')
+ expect(page).to have_content('Edit This Course')
expect(page).to have_select('course_category_id', selected: dpl_category.name)
end
end
diff --git a/spec/features/user/user_completes_course_spec.rb b/spec/features/user/user_completes_course_spec.rb
index ab63e44d..f5dfd7ea 100644
--- a/spec/features/user/user_completes_course_spec.rb
+++ b/spec/features/user/user_completes_course_spec.rb
@@ -31,7 +31,7 @@
scenario 'sees practice skills button if post course attachments exist' do
file = fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf')
- course.attachments.create(document: file, doc_type: 'post-course')
+ course.attachments.create(document: file, doc_type: 'additional-resource')
visit course_completion_path(course)
click_link 'Use My Skills Now'
@@ -41,7 +41,7 @@
scenario 'can view skills page' do
file = fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf')
- course.attachments.create(document: file, doc_type: 'post-course')
+ course.attachments.create(document: file, doc_type: 'additional-resource')
course.update(notes: '
Post-Course completion notes...')
visit course_completion_path(course)
click_link 'Use My Skills Now'
@@ -66,14 +66,14 @@
click_link 'Use My Skills Now'
expect(current_path).to eq(course_skills_path(course))
- expect(page).to have_content('Practice and use your new skills! (click each link below)')
+ expect(page).to have_content('Practice and use your new skills!')
expect(page).to have_content('Post-Course completion notes...')
expect(page).to_not have_content('
')
end
- scenario 'can view the supplemental materials link' do
+ scenario 'can view the additional resources link' do
file = fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf')
- course.attachments.create(document: file, doc_type: 'post-course')
+ course.attachments.create(document: file, doc_type: 'additional-resource')
visit course_completion_path(course)
click_link 'Use My Skills Now'
diff --git a/spec/features/user/user_starts_course_spec.rb b/spec/features/user/user_starts_course_spec.rb
index 6c3ffcac..b1e0d4b9 100644
--- a/spec/features/user/user_starts_course_spec.rb
+++ b/spec/features/user/user_starts_course_spec.rb
@@ -83,7 +83,7 @@
expect(current_path).to eq(course_lesson_path(course1, lesson))
expect(page.title).to eq(lesson.title)
expect(page).to_not have_selector('h1', text: course1.title)
- expect(page).to have_content("#{lesson.published_lesson_order}. #{lesson.title}")
+ expect(page).to have_content("#{lesson.lesson_order}. #{lesson.title}")
end
scenario 'can click to start a course and be taken to the first not-completed lesson' do
diff --git a/spec/helpers/courses_helper_spec.rb b/spec/helpers/courses_helper_spec.rb
index 0535a364..34e868c7 100644
--- a/spec/helpers/courses_helper_spec.rb
+++ b/spec/helpers/courses_helper_spec.rb
@@ -111,5 +111,10 @@
expected_path = course_lesson_path(course, course.lessons.second)
expect(helper.start_or_resume_course_link(course)).to include(expected_path)
end
+
+ it 'should include preview param if given' do
+ expected_path = course_lesson_path(course, course.lessons.first, preview: true)
+ expect(helper.start_or_resume_course_link(course, true)).to include(expected_path)
+ end
end
end
diff --git a/spec/models/attachment_spec.rb b/spec/models/attachment_spec.rb
index d4a5aba5..1cdb5559 100644
--- a/spec/models/attachment_spec.rb
+++ b/spec/models/attachment_spec.rb
@@ -15,7 +15,7 @@
end
it 'can only have listed doc_types' do
- allowed_statuses = %w[supplemental post-course]
+ allowed_statuses = %w[text-copy additional-resource]
allowed_statuses.each do |status|
@attachment.doc_type = status
expect(@attachment).to be_valid
diff --git a/spec/models/course/course_spec.rb b/spec/models/course/course_spec.rb
index 19eb11a0..b27fabf5 100644
--- a/spec/models/course/course_spec.rb
+++ b/spec/models/course/course_spec.rb
@@ -31,7 +31,7 @@
it 'should not create a new category' do
expect do
course.update(category_name: category.name)
- end.to_not change { org.categories.count }
+ end.to_not(change { org.categories.count })
end
it 'should add course to existing category' do
@@ -42,7 +42,7 @@
it 'should add course to existing category in case-insensitive manner' do
expect do
- course.update(category_name: "eXisting categorY")
+ course.update(category_name: 'eXisting categorY')
end.to change { category.courses.count }.by(1)
end
end
@@ -120,48 +120,33 @@
expect(course_with_lessons.lesson_after(123)).to eq(first_lesson)
end
- it 'should skip unpublished lessons' do
- second_lesson.update(pub_status: 'D')
- expect(course_with_lessons.lesson_after(first_lesson)).to eq(third_lesson)
- end
-
it 'should raise an error if called when there are no lessons' do
expect { course.lesson_after }.to raise_error(StandardError)
end
end
describe '#duration' do
- let(:lesson1) { FactoryBot.create(:lesson, title: '1', duration: 75) }
- let(:lesson2) { FactoryBot.create(:lesson, title: '2', duration: 150) }
- let(:lesson3) { FactoryBot.create(:lesson, title: '3', duration: 225) }
- let(:lesson4) { FactoryBot.create(:lesson, title: '4', duration: 90) }
- let(:lesson5) { FactoryBot.create(:lesson, title: '5', duration: 9) }
+ let!(:lesson1) { FactoryBot.create(:lesson, course: course, title: '1', duration: 75) }
+ let!(:lesson2) { FactoryBot.create(:lesson, course: course, title: '2', duration: 150) }
+ let!(:lesson3) { FactoryBot.create(:lesson, course: course, title: '3', duration: 225) }
+ let!(:lesson4) { FactoryBot.create(:lesson, title: '4', duration: 90) }
+ let!(:lesson5) { FactoryBot.create(:lesson, title: '5', duration: 9) }
it 'should return the sum of the lesson durations' do
- course.lessons << [lesson1, lesson2, lesson3]
expect(course.duration).to eq('7 mins')
end
it 'should return the sum of the lesson durations' do
- course.lessons << [lesson4]
- expect(course.duration).to eq('1 min')
+ expect(lesson4.course.duration).to eq('1 min')
end
it 'should return the sum of the lesson durations' do
- course.lessons << [lesson5]
- expect(course.duration).to eq('0 mins')
+ expect(lesson5.course.duration).to eq('0 mins')
end
it 'should return duration in format if one is passed' do
- course.lessons << [lesson1, lesson2, lesson3]
expect(course.duration('minutes')).to eq('7 minutes')
end
-
- it 'should not count draft lessons' do
- lesson1.update(pub_status: 'D')
- course.lessons << [lesson1, lesson2, lesson3]
- expect(course.duration).to eq '6 mins'
- end
end
describe '#published?' do
@@ -180,4 +165,33 @@
expect(archived_course.published?).to be_falsey
end
end
+
+ describe 'attachments' do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:pla_course) { FactoryBot.create(:course, organization: pla) }
+ let(:child_course) { FactoryBot.create(:course, parent: pla_course) }
+ let!(:additional_resource_attachment) { FactoryBot.create(:attachment, doc_type: 'additional-resource', course: pla_course) }
+ let!(:text_copy_attachment) { FactoryBot.create(:attachment, doc_type: 'text-copy', course: pla_course) }
+ let!(:subsite_additional_resource_attachment) { FactoryBot.create(:attachment, doc_type: 'additional-resource', course: child_course) }
+
+ context 'parent course' do
+ it 'returns correct additional resource' do
+ expect(pla_course.additional_resource_attachments).to contain_exactly(additional_resource_attachment)
+ end
+
+ it 'returns correct text copy attachments' do
+ expect(pla_course.text_copy_attachments).to contain_exactly(text_copy_attachment)
+ end
+ end
+
+ context 'child course' do
+ it 'returns subsite specific additional_resource_attachments' do
+ expect(child_course.additional_resource_attachments).to contain_exactly(subsite_additional_resource_attachment)
+ end
+
+ it 'returns parent text copies' do
+ expect(child_course.text_copy_attachments).to contain_exactly(text_copy_attachment)
+ end
+ end
+ end
end
diff --git a/spec/models/course/course_validations_spec.rb b/spec/models/course/course_validations_spec.rb
index ac99189a..ac75241f 100644
--- a/spec/models/course/course_validations_spec.rb
+++ b/spec/models/course/course_validations_spec.rb
@@ -188,19 +188,5 @@
course.meta_desc = invalid_meta
expect(course).to_not be_valid
end
-
- it 'should not allow the other topics value to be set without text' do
- course.other_topic = nil
- course.other_topic_text = ''
- expect(course).to be_valid
-
- course.other_topic = '1'
- course.other_topic_text = ''
- expect(course).to_not be_valid
-
- course.other_topic = '1'
- course.other_topic_text = 'New topic'
- expect(course).to be_valid
- end
end
end
diff --git a/spec/models/lesson_spec.rb b/spec/models/lesson_spec.rb
index ef41c897..371832eb 100644
--- a/spec/models/lesson_spec.rb
+++ b/spec/models/lesson_spec.rb
@@ -146,69 +146,16 @@
lesson.meta_desc = 'a' * 157
expect(lesson).to_not be_valid
end
-
- describe 'publication status validations' do
- it 'is invalid with empty string publication status' do
- lesson.pub_status = ''
- expect(lesson).to_not be_valid
- end
-
- it 'has correct error message with empty string publication status' do
- lesson.update(pub_status: '')
- expect(lesson.errors.full_messages).to contain_exactly("Publication Status can't be blank")
- end
-
- it 'is invalid with nil publication status' do
- lesson.pub_status = nil
- expect(lesson).to_not be_valid
- end
-
- it 'has correct error message for nil publication status' do
- lesson.update(pub_status: nil)
- expect(lesson.errors.full_messages).to contain_exactly("Publication Status can't be blank")
- end
-
- it 'is invalid with invalid publication status' do
- lesson.pub_status = 'X'
- expect(lesson).to_not be_valid
- end
-
- it 'has correct error message for invalid publication status' do
- lesson.update(pub_status: 'X')
- expect(lesson.errors.full_messages).to contain_exactly('Publication Status X is not a valid status')
- end
- end
end
context 'scopes' do
-
- context '.published' do
-
- it 'returns all published lessons' do
- expect(course.lessons.published).to contain_exactly(course.lessons.first, course.lessons.second, course.lessons.third)
- end
-
- it 'returns all published lessons' do
- course.lessons.second.update(pub_status: 'D')
- expect(course.lessons.published).to contain_exactly(course.lessons.first, course.lessons.third)
- end
-
- it 'returns all published lessons' do
- course.lessons.second.update(pub_status: 'A')
- expect(course.lessons.published).to contain_exactly(course.lessons.first, course.lessons.third)
- end
-
- end
-
context '.copied_from_lesson' do
-
let(:new_org) { FactoryBot.create(:organization) }
let(:new_course) { FactoryBot.create(:course_with_lessons, organization: new_org) }
let(:original_lesson) { course.lessons.first }
let(:copied_lesson) { new_course.lessons.first }
before(:each) do
- original_lesson.propagation_org_ids << new_org.id
copied_lesson.update(parent_id: original_lesson.id)
end
@@ -219,31 +166,6 @@
it 'does not return non-copied lessons' do
expect(Lesson.copied_from_lesson(original_lesson).count).to eq(1)
end
-
- end
-
- end
-
- context '#published_lesson_order' do
-
- it 'returns the order of only published lessons' do
- course.lessons.second.update(pub_status: 'D')
- expect(course.lessons.first.published_lesson_order).to eq 1
- expect(course.lessons.second.published_lesson_order).to eq 0
- expect(course.lessons.third.published_lesson_order).to eq 2
- end
-
- end
-
- context '#propagates_org_ids' do
- it 'is empty by default' do
- expect(Lesson.new.propagation_org_ids).to eq([])
- end
-
- it 'can be updated' do
- lesson = Lesson.new
- lesson.propagation_org_ids = [1]
- expect(lesson.propagation_org_ids).to eq([1])
end
end
end
diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb
index a0dec91f..cd0aee69 100644
--- a/spec/models/organization_spec.rb
+++ b/spec/models/organization_spec.rb
@@ -3,75 +3,68 @@
require 'rails_helper'
RSpec.describe Organization, type: :model do
- before do
- @org = create(:organization)
- other_org = create(:default_organization)
- @user1 = create(:user, organization: @org)
- @user1.add_role('admin', @org)
-
- @user2 = create(:user, organization: @org)
- @user2.add_role('admin', @org)
-
- @user3 = create(:user, organization: @org)
- @user3.add_role('user', @org)
-
- @user4 = create(:user, organization: other_org)
- @user4.add_role('user', other_org)
- end
+ let(:org) { FactoryBot.create(:organization) }
+ let!(:pla) { FactoryBot.create(:default_organization) }
+ let!(:admin1) { FactoryBot.create(:user, :admin, organization: org) }
+ let!(:admin2) { FactoryBot.create(:user, :admin, organization: org) }
+ let!(:user1) { FactoryBot.create(:user, organization: org) }
+ let!(:user2) { FactoryBot.create(:user) }
it { should have_many(:cms_pages) }
it { should have_many(:library_locations) }
describe 'scopes' do
+ let(:parent_course) { FactoryBot.create(:course_with_lessons) }
+
describe 'using_lesson' do
- it 'includes only orgs using the passed lesson' do
- lesson_org = create(:organization)
- create(:organization)
- parent = create(:lesson)
- lesson = create(:lesson, parent_id: parent.id, course: create(:course))
- lesson_org.courses << lesson.course
+ let(:parent_lesson) { parent_course.lessons.first }
+ let(:subsite_course) { FactoryBot.create(:course, organization: org) }
+ let!(:lesson) { FactoryBot.create(:lesson, parent_id: parent_lesson.id, course: subsite_course) }
- expect(Organization.using_lesson(parent.id)).to eq([lesson_org])
+ it 'includes only orgs using the passed lesson' do
+ expect(Organization.using_lesson(parent_lesson.id)).to eq([org])
end
end
describe 'using_course' do
- it 'includes only orgs using the passed course' do
- course_org = create(:organization)
- create(:organization)
- parent_course = create(:course)
- course = create(:course, parent_id: parent_course.id)
- course_org.courses << course
+ let!(:course) { FactoryBot.create(:course, parent_id: parent_course.id, organization: org) }
- expect(Organization.using_course(parent_course.id)).to eq([course_org])
+ it 'includes only orgs using the passed course' do
+ expect(Organization.using_course(parent_course.id)).to eq([org])
end
end
end
describe 'validations' do
it 'requires a name' do
- @org.name = nil
- expect(@org).to_not be_valid
+ org.name = nil
+ expect(org).to_not be_valid
end
it 'requires a subdomain' do
- @org.subdomain = nil
- expect(@org).to_not be_valid
+ org.subdomain = nil
+ expect(org).to_not be_valid
end
end
describe '#users_count' do
it 'returns the count of its users' do
- expect(@org.user_count).to eq(3)
+ expect(org.user_count).to eq(3)
end
end
describe '#admin_user_emails' do
it 'returns emails of the admins' do
- expect(@org.admin_user_emails).to include(@user1.email)
- expect(@org.admin_user_emails).to include(@user2.email)
- expect(@org.admin_user_emails).not_to include(@user3.email)
- expect(@org.admin_user_emails).not_to include(@user4.email)
+ expect(org.admin_user_emails).to include(admin1.email)
+ expect(org.admin_user_emails).to include(admin2.email)
+ expect(org.admin_user_emails).not_to include(user1.email)
+ expect(org.admin_user_emails).not_to include(user2.email)
+ end
+ end
+
+ describe '#pla' do
+ it 'returns PLA organization' do
+ expect(Organization.pla).to eq(pla)
end
end
end
diff --git a/spec/policies/attachment_policy_spec.rb b/spec/policies/attachment_policy_spec.rb
index c090faa9..42e64b37 100644
--- a/spec/policies/attachment_policy_spec.rb
+++ b/spec/policies/attachment_policy_spec.rb
@@ -10,5 +10,64 @@
let!(:subsite_record) { FactoryBot.create(:attachment, course: course) }
let!(:other_subsite_record) { FactoryBot.create(:attachment) }
- it_behaves_like 'AdminOnly Policy', { skip_scope: true }
+ it_behaves_like 'AdminOnly Policy', { skip_scope: true, skip_actions: [:show?] }
+
+ permissions :show? do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:pla_user) { FactoryBot.create(:user, organization: pla) }
+
+ let(:subsite) { FactoryBot.create(:organization) }
+ let(:subsite_user) { FactoryBot.create(:user, organization: subsite) }
+
+ let(:guest_user) { GuestUser.new(organization: subsite) }
+
+ let(:pla_course) { FactoryBot.create(:course, organization: pla) }
+ let!(:child_course) { FactoryBot.create(:course, organization: subsite, parent: pla_course) }
+
+ let(:attachment) { FactoryBot.create(:attachment, course: pla_course) }
+ let(:other_course_attachment) { FactoryBot.create(:attachment) }
+
+ subject { described_class }
+
+ context 'guest user' do
+ it 'should be permitted' do
+ expect(subject).to permit(guest_user, attachment)
+ end
+
+ it 'should not be permitted for attachment not related to subsite course' do
+ expect(subject).to_not permit(guest_user, other_course_attachment)
+ end
+
+ it 'should not be permitted for private course attachment' do
+ child_course.update!(access_level: 'authenticated_users')
+ expect(subject).to_not permit(guest_user, attachment)
+ end
+ end
+
+ context 'original course user' do
+ it 'should be permitted for current organization' do
+ expect(subject).to permit(pla_user, attachment)
+ end
+
+ it 'should be permitted for private course' do
+ child_course.update!(access_level: 'authenticated_users')
+ expect(subject).to permit(pla_user, attachment)
+ end
+ end
+
+ context 'authenticated user' do
+ it 'should be permitted for current organization' do
+ expect(subject).to permit(subsite_user, attachment)
+ end
+
+ it 'should not be permitted for another organization' do
+ expect(subject).to_not permit(subsite_user, other_course_attachment)
+ end
+
+ it 'should be permitted for private course' do
+ child_course.update!(access_level: 'authenticated_users')
+ expect(subject).to permit(subsite_user, attachment)
+ end
+ end
+ end
end
diff --git a/spec/policies/course_policy_spec.rb b/spec/policies/course_policy_spec.rb
index 2cc5c6de..61ba7fec 100644
--- a/spec/policies/course_policy_spec.rb
+++ b/spec/policies/course_policy_spec.rb
@@ -46,6 +46,37 @@
end
end
+ permissions :preview? do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:guest_user) { GuestUser.new(organization: organization) }
+ let(:user) { FactoryBot.create(:user, organization: organization) }
+ let(:admin) { FactoryBot.create(:user, :admin, organization: organization) }
+ let(:course) { FactoryBot.create(:course, organization: pla) }
+ let(:subsite_course) { FactoryBot.create(:course) }
+
+ context 'guest_user' do
+ it 'denies access' do
+ expect(subject).to_not permit(guest_user, course)
+ end
+ end
+
+ context 'subsite user' do
+ it 'denies access' do
+ expect(subject).to_not permit(user, course)
+ end
+ end
+
+ context 'subsite admin' do
+ it 'allows access to pla course' do
+ expect(subject).to permit(admin, course)
+ end
+
+ it 'denies access to non PLA courses' do
+ expect(subject).to_not permit(admin, subsite_course)
+ end
+ end
+ end
+
permissions :show? do
let(:guest_user) { GuestUser.new(organization: organization) }
let(:user) { FactoryBot.create(:user, organization: organization) }
@@ -145,4 +176,71 @@
end
end
end
+
+ describe 'permitted_attributes' do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:pla_course) { FactoryBot.create(:course, organization: pla) }
+ let(:admin) { FactoryBot.create(:user, :admin) }
+ let(:subsite) { admin.organization }
+ let(:guest_user) { GuestUser.new(organization: subsite) }
+ let(:user) { FactoryBot.create(:user, organization: subsite) }
+ let(:imported_subsite_course) { FactoryBot.create(:course, organization: subsite, parent: pla_course) }
+ let(:custom_subsite_course) { FactoryBot.create(:course, organization: subsite) }
+
+ context 'for a guest user' do
+ subject { CoursePolicy.new(guest_user, custom_subsite_course) }
+
+ it 'should be empty' do
+ expect(subject.permitted_attributes).to eq([])
+ end
+ end
+
+ context 'for a subsite user' do
+ subject { CoursePolicy.new(user, custom_subsite_course) }
+
+ it 'should be empty' do
+ expect(subject.permitted_attributes).to eq([])
+ end
+ end
+
+ context 'for a subsite admin editing an imported course' do
+ subject { CoursePolicy.new(admin, imported_subsite_course) }
+
+ it 'should contain appropriate attributes' do
+ expect(subject.permitted_attributes).to contain_exactly(:category_id,
+ :access_level,
+ :pub_status,
+ :notes,
+ category_attributes: %i[name organization_id],
+ attachments_attributes: %i[document title doc_type file_description _destroy])
+ end
+ end
+
+ context 'for a subsite admin creating or editing a custom course' do
+ subject { CoursePolicy.new(admin, custom_subsite_course) }
+
+ it 'should contain appropriate attributes' do
+ expect(subject.permitted_attributes).to contain_exactly(:title,
+ :seo_page_title,
+ :meta_desc,
+ :summary,
+ :description,
+ :contributor,
+ :pub_status,
+ :language_id,
+ :level,
+ :notes,
+ :delete_document,
+ :course_order,
+ :pub_date,
+ :format,
+ :access_level,
+ :category_id,
+ topic_ids: [],
+ course_topics_attributes: [topic_attributes: [:title]],
+ category_attributes: %i[name organization_id],
+ attachments_attributes: %i[document title doc_type file_description _destroy])
+ end
+ end
+ end
end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 70ddeb81..203d5a22 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -61,8 +61,8 @@
end
config.before(:each) do
- @english = create(:language)
- @spanish = create(:spanish_lang)
+ @english = FactoryBot.create(:language)
+ @spanish = FactoryBot.create(:spanish_lang)
I18n.locale = :en
end
diff --git a/spec/services/course_import_service_spec.rb b/spec/services/course_import_service_spec.rb
new file mode 100644
index 00000000..5d03ddb5
--- /dev/null
+++ b/spec/services/course_import_service_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe CourseImportService do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:topic) { FactoryBot.create(:topic) }
+ let(:category) { FactoryBot.create(:category, organization: pla) }
+ let(:pla_course) { FactoryBot.create(:course_with_lessons, organization: pla, topics: [topic], category: category) }
+
+ let(:document) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'testfile.pdf'), 'application/pdf') }
+ let!(:text_copy_attachment) { FactoryBot.create(:attachment, doc_type: 'text-copy', document: document, course: pla_course) }
+ let!(:additional_resource_attachment) { FactoryBot.create(:attachment, doc_type: 'additional-resource', document: document, course: pla_course) }
+
+ let(:subsite) { FactoryBot.create(:organization) }
+
+ subject { described_class.new(organization: subsite, course_id: pla_course.id) }
+
+ it 'should create a new course record for subsite' do
+ expect do
+ subject.import!
+ end.to change { subsite.courses.count }.by(1)
+ end
+
+ it 'should create new course as a child of original course' do
+ expect do
+ subject.import!
+ end.to change { Course.copied_from_course(pla_course).count }.by(1)
+ end
+
+ it 'should import course in draft status' do
+ expect do
+ subject.import!
+ end.to change { Course.where(pub_status: 'D').count }.by(1)
+ end
+
+ it 'should create new category on organization' do
+ expect do
+ subject.import!
+ end.to change { subsite.categories.count }.by(1)
+ end
+
+ it 'should copy lessons into organization' do
+ expect do
+ subject.import!
+ end.to change { subsite.lessons.count }.by(3)
+ end
+
+ it 'should copy additional-content attachments' do
+ expect do
+ subject.import!
+ end.to change { Attachment.where(doc_type: 'additional-resource').count }.by(1)
+ end
+
+ it 'should not copy text-copy attachments' do
+ expect do
+ subject.import!
+ end.to_not(change { Attachment.where(doc_type: 'text-copy').count })
+ end
+
+ it 'should create new course topic' do
+ expect do
+ subject.import!
+ end.to change(CourseTopic, :count).by(1)
+ end
+end
diff --git a/spec/services/course_propagation_service_spec.rb b/spec/services/course_propagation_service_spec.rb
new file mode 100644
index 00000000..7e78f8b2
--- /dev/null
+++ b/spec/services/course_propagation_service_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe CoursePropagationService do
+ let(:pla) { FactoryBot.create(:default_organization) }
+ let(:course) { FactoryBot.create(:course_with_lessons, organization: pla) }
+ let(:old_topic) { FactoryBot.create(:topic) }
+ let!(:child_course) { FactoryBot.create(:course, parent: course, topics: [old_topic]) }
+
+ describe '#propagate_course_changes' do
+ describe 'model attribute changes' do
+ let(:new_attributes) do
+ {
+ title: 'New Course Title',
+ contributor: 'New Contributor',
+ summary: 'New Summary',
+ description: 'New Description',
+ notes: 'New Notes',
+ language: @spanish,
+ format: 'M',
+ level: 'Advanced',
+ seo_page_title: 'New SEO Title',
+ meta_desc: 'New Meta Desc'
+ }
+ end
+
+ subject { described_class.new(course: course) }
+
+ it 'should update child course info' do
+ subject.propagate_course_changes(new_attributes)
+ child_course.reload
+ new_attributes.keys.each do |k|
+ expect(child_course.send(k)).to eq(new_attributes[k])
+ end
+ end
+ end
+
+ describe 'topic changes' do
+ let!(:topic) { FactoryBot.create(:topic) }
+
+ before do
+ course.update(topics: [topic])
+ end
+
+ subject { described_class.new(course: course) }
+
+ it 'should add correct topic to child course' do
+ subject.propagate_course_changes({})
+ expect(child_course.reload.topics).to contain_exactly(topic)
+ end
+ end
+ end
+end
diff --git a/spec/services/lesson_propagation_service_spec.rb b/spec/services/lesson_propagation_service_spec.rb
new file mode 100644
index 00000000..19b7d376
--- /dev/null
+++ b/spec/services/lesson_propagation_service_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe LessonPropagationService do
+ let(:parent_course) { FactoryBot.create(:course_with_lessons) }
+ let(:child_course) { FactoryBot.create(:course, parent: parent_course) }
+
+ let(:lesson) { parent_course.lessons.first }
+
+ subject { described_class.new(lesson: lesson) }
+
+ describe '#add_to_course!' do
+ it 'should create a new lesson' do
+ expect do
+ subject.add_to_course!(child_course)
+ end.to change { child_course.lessons.count }.by(1)
+ end
+ end
+
+ describe '#update_children!' do
+ let(:child_lesson) { FactoryBot.create(:lesson_without_story, parent: lesson) }
+ let(:new_story_line) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'BasicSearch1.zip'), 'application/zip') }
+
+ it 'should propagate lesson title' do
+ original_title = child_lesson.title
+
+ expect do
+ subject.update_children!
+ end.to change { child_lesson.reload.title }.from(original_title).to(lesson.title)
+ end
+
+ it 'should propagate assessment' do
+ lesson.update(is_assessment: true)
+
+ expect do
+ subject.update_children!
+ end.to change { child_lesson.reload.is_assessment? }.from(false).to(true)
+ end
+
+ it 'should not change lesson course' do
+ expect do
+ subject.update_children!
+ end.to_not(change { child_lesson.reload.course_id })
+ end
+
+ it 'should preserve lesson parent' do
+ expect do
+ subject.update_children!
+ end.to_not(change { child_lesson.reload.parent_id })
+ end
+
+ it 'should update child sort order' do
+ lesson.update(lesson_order: 4)
+
+ expect do
+ subject.update_children!
+ end.to(change { child_lesson.reload.lesson_order })
+ end
+
+ describe 'story_line propagation' do
+ before do
+ lesson.update!(story_line: new_story_line)
+ end
+
+ it 'should not propagate story_line' do
+ expect do
+ subject.update_children!
+ end.to_not(change { child_lesson.reload.story_line })
+ end
+
+ it 'should not propagate story_line filename' do
+ expect do
+ subject.update_children!
+ end.to_not(change { child_lesson.reload.story_line_file_name })
+ end
+
+ it 'should not propagate story_line file size' do
+ expect do
+ subject.update_children!
+ end.to_not(change { child_lesson.reload.story_line_file_size })
+ end
+ end
+ end
+end
diff --git a/spec/support/views/pundit_view_policy.rb b/spec/support/views/pundit_view_policy.rb
new file mode 100644
index 00000000..8e5e0de4
--- /dev/null
+++ b/spec/support/views/pundit_view_policy.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module PunditViewPolicy
+ extend ActiveSupport::Concern
+
+ included do
+ before do
+ controller.singleton_class.class_eval do
+ def policy(_instance)
+ Class.new do
+ def method_missing(*_args)
+ true
+ end
+ end.new
+ end
+ helper_method :policy
+ end
+ end
+ end
+end
+
+RSpec.configure do |config|
+ config.include PunditViewPolicy, type: :view
+end