From 796ff79ac3ff0caea6ef196d7842a52e61461292 Mon Sep 17 00:00:00 2001 From: Andrew Kvalheim Date: Wed, 31 May 2023 14:49:39 -0700 Subject: [PATCH 1/2] Test drag-and-drop schedule editor --- spec/features/schedule_spec.rb | 70 ++++++++++++++++++++++++++++++++ spec/spec_helper.rb | 2 + spec/support/bootstrap_macros.rb | 11 +++++ spec/support/jquery_macros.rb | 14 +++++++ 4 files changed, 97 insertions(+) create mode 100644 spec/features/schedule_spec.rb create mode 100644 spec/support/bootstrap_macros.rb create mode 100644 spec/support/jquery_macros.rb diff --git a/spec/features/schedule_spec.rb b/spec/features/schedule_spec.rb new file mode 100644 index 0000000000..cb7bb20b3a --- /dev/null +++ b/spec/features/schedule_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'spec_helper' + +def expect_scheduled(event, time) + visit admin_conference_program_event_path(event.conference.short_title, event) + if time + date = event.conference.start_date.strftime('%F') + expect(page).to have_text("Scheduled time #{date} #{time} #{event.conference.timezone}") + else + expect(page).to have_no_text('Scheduled') + end +end + +def move(event, to:) + draggable = find("#event-#{event.id}.ui-draggable") + droppable = find("#schedule-room-#{to[0].guid}-#{to[1]}-#{to[2]}.ui-droppable") + wait_for_ajax { draggable.drag_to droppable } +end + +def remove(event) + button = find("#event-#{event.id} .schedule-event-delete-button") + wait_for_ajax { button.click } +end + +feature Schedule do + let(:venue) { create(:venue) } + let(:conference) { create(:conference, venue: venue) } + let!(:room) { create(:room, venue: venue) } + let!(:event) { create(:event, program: conference.program, state: 'confirmed') } + + context 'as an organizer' do + let(:organizer) { create(:organizer, resource: conference) } + + before :each do + sign_in organizer + end + + scenario 'create a schedule', js: true do + expect_scheduled event, nil + + visit admin_conference_schedules_path(conference) + click_on 'Add Schedule' + move event, to: [room, 9, 30] + wait_for_ajax { switch conference.short_title, to: true } + + expect_scheduled event, '09:30' + end + + scenario 'reschedule an event', js: true do + create(:event_schedule, event: event, room: room) + expect_scheduled event, '09:00' + + visit admin_conference_schedule_path(conference, conference.program.selected_schedule) + move event, to: [room, 12, 15] + + expect_scheduled event, '12:15' + end + + scenario 'unschedule an event', js: true do + create(:event_schedule, event: event, room: room) + expect_scheduled event, '09:00' + + visit admin_conference_schedule_path(conference, conference.program.selected_schedule) + remove event + + expect_scheduled event, nil + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d5330db5be..2972068b64 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -92,6 +92,8 @@ config.include FactoryBot::Syntax::Methods config.include OmniauthMacros config.include Devise::Test::ControllerHelpers, type: :controller + config.include BootstrapMacros, type: :feature + config.include JQueryMacros, type: :feature config.include LoginMacros, type: :feature config.include Flash, type: :feature config.include Sidebar, type: :view diff --git a/spec/support/bootstrap_macros.rb b/spec/support/bootstrap_macros.rb new file mode 100644 index 0000000000..62d05e743c --- /dev/null +++ b/spec/support/bootstrap_macros.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module BootstrapMacros + # Interact with Bootstrap Switch + def switch(locator, to:, **options) + checkbox = find(:checkbox, locator, **options, visible: :hidden) + + checkbox.ancestor('.bootstrap-switch').click unless checkbox.checked? == to + expect(page).to have_selector(:checkbox, locator, **options, visible: :hidden, checked: to) + end +end diff --git a/spec/support/jquery_macros.rb b/spec/support/jquery_macros.rb new file mode 100644 index 0000000000..391953cc89 --- /dev/null +++ b/spec/support/jquery_macros.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module JQueryMacros + # Wait for one jQuery Ajax request during the block + def wait_for_ajax + flag = "window.ajax_#{SecureRandom.alphanumeric(16)}" + page.execute_script "#{flag} = false; $(document).one('ajaxComplete', () => #{flag} = true);" + + yield + + Timeout.timeout(Capybara.default_max_wait_time) { loop until page.evaluate_script(flag) } + page.execute_script "delete #{flag};" + end +end From 65e69c4b7ebc867b354e71ec4a770de581210738 Mon Sep 17 00:00:00 2001 From: Andrew Kvalheim Date: Wed, 31 May 2023 14:51:47 -0700 Subject: [PATCH 2/2] Fix bug in initial scheduling of events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves inability to schedule a nonscheduled event: ActionController::RoutingError: No route matches [PUT] "/admin/conferences/…/event_schedules" `event_schedule_id` represents the absence of a schedule as either an empty string or undefined, not as null. --- app/assets/javascripts/osem-schedule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/osem-schedule.js b/app/assets/javascripts/osem-schedule.js index aeb76ae374..5f1e772891 100644 --- a/app/assets/javascripts/osem-schedule.js +++ b/app/assets/javascripts/osem-schedule.js @@ -46,7 +46,7 @@ var Schedule = { room_id: new_parent.attr("room_id"), start_time: (new_parent.attr("date") + ' ' + new_parent.attr("hour")) }}; - if(event_schedule_id != null){ + if(event_schedule_id){ type = 'PUT'; my_url += ('/' + event_schedule_id); }