
With this commit, we refactor the overall score handling of CodeOcean. Previously, "Score" and "Submit" were two distinct actions, requiring users to confirm the LTI transmission of their score (after assessing their submission). This yielded many questions and was unnecessary, since LTI parameters are no longer expiring after each use. Therefore, we can now transmit the current grade on each score run with the very same LTI parameters. As a consequence, the LTI consumer gets a more detailed history of the scores, enabling further analytical insights. For users, the previous "Submit" button got replaced with a notification that is shown as soon as the full score got reached. Then, learners can decide to "finalize" their work on the given exercise, which will initiate a redirect to a follow-up action (as defined in the RedirectBehavior). This RedirectBehavior has also been unified and simplified for better readability. As part of this refactoring, we rephrased the notifications and UX workflow of a) the LTI transmission, b) the finalization of an exercise (measured by reaching the full score) and c) the deadline handling (on time, within grace period, too late). Those information are now separately shown, potentially resulting in multiple notifications. As a side effect, they are much better maintainable, and the LTI transmission is more decoupled from this notification handling.
113 lines
3.4 KiB
Ruby
113 lines
3.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
RSpec.describe 'Editor', :js do
|
|
let(:exercise) { create(:audio_video, description: Forgery(:lorem_ipsum).sentence) }
|
|
let(:scoring_response) do
|
|
[{
|
|
status: :ok,
|
|
stdout: '',
|
|
stderr: '',
|
|
waiting_for_container_time: 0,
|
|
container_execution_time: 0,
|
|
file_role: 'teacher_defined_test',
|
|
count: 1,
|
|
failed: 0,
|
|
error_messages: [],
|
|
passed: 1,
|
|
score: 1.0,
|
|
filename: 'index.html_spec.rb',
|
|
message: 'Well done.',
|
|
weight: 2.0,
|
|
}]
|
|
end
|
|
let(:contributor) { create(:teacher) }
|
|
let(:exercise_without_test) { create(:tdd) }
|
|
|
|
before do
|
|
visit(sign_in_path)
|
|
fill_in('email', with: contributor.email)
|
|
fill_in('password', with: attributes_for(:teacher)[:password])
|
|
click_button(I18n.t('sessions.new.link'))
|
|
allow_any_instance_of(LtiHelper).to receive(:lti_outcome_service?).and_return(true)
|
|
visit(implement_exercise_path(exercise))
|
|
end
|
|
|
|
it 'displays the exercise title' do
|
|
expect(page).to have_content(exercise.title)
|
|
end
|
|
|
|
it 'displays the exercise description' do
|
|
expect(page).to have_content(exercise.description)
|
|
end
|
|
|
|
it 'displays all visible files in a file tree' do
|
|
within('#files') do
|
|
exercise.files.select(&:visible).each do |file|
|
|
expect(page).to have_content(file.name_with_extension)
|
|
end
|
|
end
|
|
end
|
|
|
|
it "displays the main file's code" do
|
|
expect(page).to have_css(".frame[data-filename='#{exercise.files.detect(&:main_file?).name_with_extension}']")
|
|
end
|
|
|
|
context 'when selecting a file' do
|
|
before do
|
|
within('#files') { click_link(file.name_with_extension) }
|
|
end
|
|
|
|
context 'when selecting a binary file' do
|
|
context 'when selecting an audio file' do
|
|
let(:file) { exercise.files.detect {|file| file.file_type.audio? } }
|
|
|
|
it 'contains an <audio> tag' do
|
|
expect(page).to have_css("audio[src='#{file.native_file.url}']")
|
|
end
|
|
end
|
|
|
|
context 'when selecting an image file' do
|
|
let(:file) { exercise.files.detect {|file| file.file_type.image? } }
|
|
|
|
it 'contains an <img> tag' do
|
|
expect(page).to have_css("img[src='#{file.native_file.url}']")
|
|
end
|
|
end
|
|
|
|
context 'when selecting a video file' do
|
|
let(:file) { exercise.files.detect {|file| file.file_type.video? } }
|
|
|
|
it 'contains a <video> tag' do
|
|
expect(page).to have_css("video[src='#{file.native_file.url}']")
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when selecting a non-binary file' do
|
|
let(:file) { exercise.files.detect {|file| !file.file_type.binary? && !file.hidden? } }
|
|
|
|
it "displays the file's code" do
|
|
expect(page).to have_css(".frame[data-filename='#{file.name_with_extension}']")
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when an exercise has one or more teacher-defined assessments' do
|
|
it 'displays the score button' do
|
|
visit(implement_exercise_path(exercise))
|
|
expect(page).to have_content(exercise.title)
|
|
expect(page).to have_content(I18n.t('exercises.editor.score'))
|
|
end
|
|
end
|
|
|
|
context 'when an exercise has no teacher-defined assessment' do
|
|
it 'disables the score button' do
|
|
visit(implement_exercise_path(exercise_without_test))
|
|
expect(page).to have_content(exercise_without_test.title)
|
|
expect(page).not_to have_content(I18n.t('exercises.editor.score'))
|
|
end
|
|
end
|
|
end
|