diff --git a/spec/system/exercises_system_spec.rb b/spec/system/exercises_system_spec.rb new file mode 100644 index 000000000..9614a174f --- /dev/null +++ b/spec/system/exercises_system_spec.rb @@ -0,0 +1,162 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Exercise creation', :js do + let!(:ruby) { create(:ruby) } + let(:teacher) { create(:teacher) } + + before do + visit(sign_in_path) + fill_in('email', with: teacher.email) + fill_in('password', with: attributes_for(:teacher)[:password]) + click_button(I18n.t('sessions.new.link')) + wait_for_ajax + end + + context 'when an exercise is created' do + let(:submission_deadline) { 3.months.from_now.beginning_of_minute } + let(:late_submission_deadline) { submission_deadline + 1.week } + let(:title) { 'Ruby challenge' } + let(:internal_title) { 'Project Ruby: Table flip' } + + let(:code) do + <<~RUBY + def self.❨╯°□°❩╯︵┻━┻ + puts "Calm down, bro" + end + RUBY + end + + let(:description) do + <<~TEXT + Ruby challenge + + Do something with Ruby. + TEXT + end + + before do + visit(exercises_path) + click_on I18n.t('shared.new_model', model: Exercise.model_name.human) + wait_for_ajax + end + + it 'creates an exercise with nested data' do + fill_in Exercise.human_attribute_name(:title), with: title + fill_in Exercise.human_attribute_name(:internal_title), with: internal_title + + # description + within('.markdown-editor__wrapper') do + find('.ProseMirror').set(description) + end + + chosenjs_select_helper(Exercise.human_attribute_name(:execution_environment), ruby.name) + + chosenjs_date_time_select_helper(Exercise.human_attribute_name(:submission_deadline), submission_deadline) + + chosenjs_date_time_select_helper(Exercise.human_attribute_name(:late_submission_deadline), late_submission_deadline) + + check Exercise.human_attribute_name(:public) + + click_on I18n.t('exercises.form.add_file') + + within(find_by_id('files').all('li').last) do + fill_in CodeOcean::File.human_attribute_name(:name), with: 'main' + + chosenjs_select_helper(CodeOcean::File.human_attribute_name(:file_type), ruby.file_type.name) + chosenjs_select_helper(CodeOcean::File.human_attribute_name(:role), I18n.t('code_ocean/files.roles.main_file')) + + check(CodeOcean::File.human_attribute_name(:read_only)) + + find_by_id('editor-edit').click + send_keys code.strip + end + + click_button I18n.t('shared.create', model: Exercise.model_name.human) + + expect(page).to have_text \ + I18n.t('shared.object_created', model: Exercise.model_name.human) + + # Exercise is created with expected attributes + expect(page).to have_text(title) + expect(page).to have_text(internal_title) + expect(page).to have_text(submission_deadline.to_s) + expect(page).to have_text(late_submission_deadline.to_s) + expect(page).to have_text(ruby.name) + + description.lines.each do |line| + expect(page).to have_text(line.delete("\n")) + end + + # Exercise includes the code + find('span', text: 'main.rb').click + + code.lines.each do |code_line| + expect(page).to have_text(code_line.delete("\n")) + end + end + end + + context 'when an exercise is updated' do + let!(:exercise) { create(:fibonacci, user: teacher) } + let(:deleted_file_name) { 'reference.rb' } + let(:updated_file_name) { 'exercise.rb' } + + before do + visit(exercises_path) + end + + it 'updates an exercise with nested data' do + click_on exercise.title + click_on I18n.t('shared.edit') + + fill_in Exercise.human_attribute_name(:difficulty), with: 5 + + find('span', text: updated_file_name).click + + within('.card', text: updated_file_name) do + fill_in CodeOcean::File.human_attribute_name(:name), with: 'main_exercise' + end + + find('span', text: deleted_file_name).click + + within('.card', text: deleted_file_name) do + accept_confirm do + find('div.btn', text: I18n.t('shared.destroy')).click + end + end + + click_button I18n.t('shared.update', model: Exercise.model_name.human) + + expect(page).to have_text("#{Exercise.human_attribute_name(:difficulty)}\n5") + expect(page).to have_text('main_exercise.rb') + expect(page).to have_no_text(deleted_file_name) + end + end + + def chosenjs_select_helper(name, value) + id = first('label', text: name)[:for] + + set_value_for_chosenjs_element(id, value) + end + + def chosenjs_date_time_select_helper(name, date) + id = first('label', text: name)[:for] + + set_value_for_chosenjs_element("#{id}_1i", date.year.to_s) + set_value_for_chosenjs_element("#{id}_2i", date.strftime('%B')) + set_value_for_chosenjs_element("#{id}_3i", date.day.to_s) + set_value_for_chosenjs_element("#{id}_4i", date.hour.to_s) + set_value_for_chosenjs_element("#{id}_5i", date.min.to_s) + end + + def set_value_for_chosenjs_element(id, value) + element = find_by_id("#{id}_chosen") + element.click + + within(element) do + first('.chosen-results li', text: value).click + end + end +end