diff --git a/spec/selenium/conversations_inbox_opt_out_spec.rb b/spec/selenium/conversations_inbox_opt_out_spec.rb new file mode 100644 index 00000000000..e653778ed69 --- /dev/null +++ b/spec/selenium/conversations_inbox_opt_out_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path(File.dirname(__FILE__) + '/helpers/conversations_common') + +describe "conversations new" do + include_examples "in-process server selenium tests" + + before do + conversation_setup + end + + describe 'conversations inbox opt-out option' do + it "should be hidden a feature flag" do + get "/profile/settings" + expect(ff('#disable_inbox').count).to eq 0 + end + + it "should reveal when the feature flag is set" do + @course.root_account.enable_feature!(:allow_opt_out_of_inbox) + get "/profile/settings" + expect(ff('#disable_inbox').count).to eq 1 + end + + context "when activated" do + it "should set the notification preferences for conversations to ASAP, and hide those options" do + @course.root_account.enable_feature!(:allow_opt_out_of_inbox) + expect(@teacher.reload.disabled_inbox?).to be_falsey + notification = Notification.create!(workflow_state: "active", name: "Conversation Message", + category: "Conversation Message", delay_for: 0) + policy = NotificationPolicy.create!(notification_id: notification.id, communication_channel_id: @teacher.email_channel.id, broadcast: true, frequency: "weekly") + @teacher.update_attribute(:unread_conversations_count, 3) + sleep 0.5 + + get '/profile/communication' + expect(ff('td[data-category="conversation_message"]').count).to eq 1 + expect(ff('.unread-messages-count').count).to eq 1 + + get "/profile/settings" + f('#disable_inbox').click + sleep 0.5 + + expect(@teacher.reload.disabled_inbox?).to be_truthy + + get '/profile/communication' + expect(ff('td[data-category="conversation_message"]').count).to eq 0 + expect(policy.reload.frequency).to eq "immediately" + expect(ff('.unread-messages-count').count).to eq 0 + end + end + end +end diff --git a/spec/selenium/conversations_message_sending_spec.rb b/spec/selenium/conversations_message_sending_spec.rb new file mode 100644 index 00000000000..4635df2d481 --- /dev/null +++ b/spec/selenium/conversations_message_sending_spec.rb @@ -0,0 +1,207 @@ +require File.expand_path(File.dirname(__FILE__) + '/helpers/conversations_common') + +describe "conversations new" do + include_examples "in-process server selenium tests" + + before do + conversation_setup + @s1 = user(name: "first student") + @s2 = user(name: "second student") + [@s1, @s2].each { |s| @course.enroll_student(s).update_attribute(:workflow_state, 'active') } + cat = @course.group_categories.create(:name => "the groups") + @group = cat.groups.create(:name => "the group", :context => @course) + @group.users = [@s1, @s2] + end + + describe "message sending" do + it "should start a group conversation when there is only one recipient" do + get_conversations + compose course: @course, to: [@s1], subject: 'single recipient', body: 'hallo!' + c = @s1.conversations.last.conversation + expect(c.subject).to eq('single recipient') + expect(c.private?).to be_falsey + end + + it "should start a group conversation when there is more than one recipient" do + get_conversations + compose course: @course, to: [@s1, @s2], subject: 'multiple recipients', body: 'hallo!' + c = @s1.conversations.last.conversation + expect(c.subject).to eq('multiple recipients') + expect(c.private?).to be_falsey + expect(c.conversation_participants.collect(&:user_id).sort).to eq([@teacher, @s1, @s2].collect(&:id).sort) + end + + it "should allow admins to send a message without picking a context" do + user = account_admin_user + user_logged_in({:user => user}) + get_conversations + compose to: [@s1], subject: 'context-free', body: 'hallo!' + c = @s1.conversations.last.conversation + expect(c.subject).to eq 'context-free' + expect(c.context).to eq Account.default + end + + it "should not allow non-admins to send a message without picking a context" do + get_conversations + fj('#compose-btn').click + wait_for_animations + expect(fj('#compose-new-message .ac-input')).to have_attribute(:disabled, 'true') + end + + it "should allow non-admins to send a message to an account-level group" do + @group = Account.default.groups.create(:name => "the group") + @group.add_user(@s1) + @group.add_user(@s2) + @group.save + user_logged_in({:user => @s1}) + get_conversations + fj('#compose-btn').click + wait_for_ajaximations + select_message_course(@group, true) + add_message_recipient @s2 + end + + it "should allow admins to message users from their profiles" do + user = account_admin_user + user_logged_in({:user => user}) + get "/accounts/#{Account.default.id}/users" + wait_for_ajaximations + f('li.user a').click + wait_for_ajaximations + f('.icon-email').click + wait_for_ajaximations + expect(f('.ac-token')).not_to be_nil + end + + it "should allow selecting multiple recipients in one search" do + get_conversations + fj('#compose-btn').click + wait_for_ajaximations + select_message_course(@course) + get_message_recipients_input.send_keys('student') + driver.action.key_down(modifier).perform + keep_trying_until { fj(".ac-result:contains('first student')") }.click + driver.action.key_up(modifier).perform + fj(".ac-result:contains('second student')").click + expect(ff('.ac-token').count).to eq 2 + end + + it "should not send the message on shift-enter" do + get_conversations + compose course: @course, to: [@s1], subject: 'context-free', body: 'hallo!', send: false + driver.action.key_down(:shift).perform + get_message_body_input.send_keys(:enter) + driver.action.key_up(:shift).perform + expect(fj('#compose-new-message:visible')).not_to be_nil + end + + context "user notes" do + before(:each) do + @course.account.update_attribute(:enable_user_notes, true) + user_session(@teacher) + get_conversations + end + + it "should be allowed on new private conversations with students" do + compose course: @course, to: [@s1, @s2], body: 'hallo!', send: false + + checkbox = f(".user_note") + expect(checkbox).to be_displayed + checkbox.click + + count1 = @s1.user_notes.count + count2 = @s2.user_notes.count + click_send + expect(@s1.user_notes.reload.count).to eq count1 + 1 + expect(@s2.user_notes.reload.count).to eq count2 + 1 + end + + it "should be allowed with student groups" do + compose course: @course, to: [@group], body: 'hallo!', send: false + + checkbox = f(".user_note") + expect(checkbox).to be_displayed + checkbox.click + + count1 = @s1.user_notes.count + click_send + expect(@s1.user_notes.reload.count).to eq count1 + 1 + end + + it "should not be allowed if disabled" do + @course.account.update_attribute(:enable_user_notes, false) + get_conversations + compose course: @course, to: [@s1], body: 'hallo!', send: false + expect(f(".user_note")).not_to be_displayed + end + + it "should not be allowed for students" do + user_session(@s1) + get_conversations + compose course: @course, to: [@s2], body: 'hallo!', send: false + expect(f(".user_note")).not_to be_displayed + end + + it "should not be allowed with non-student recipient" do + compose course: @course, to: [@teacher], body: 'hallo!', send: false + expect(f(".user_note")).not_to be_displayed + end + end + end + + describe "replying" do + before do + cp = conversation(@s1, @teacher, @s2, workflow_state: 'unread') + @convo = cp.conversation + @convo.update_attribute(:subject, 'homework') + @convo.add_message(@s1, "What's this week's homework?") + @convo.add_message(@s2, "I need the homework too.") + end + + it "should maintain context and subject" do + get_conversations + conversation_elements[0].click + wait_for_ajaximations + fj('#reply-btn').click + expect(fj('#compose-message-course')).to have_attribute(:disabled, 'true') + expect(fj('#compose-message-course')).to have_value(@course.id.to_s) + expect(fj('#compose-message-subject')).to have_attribute(:disabled, 'true') + expect(fj('#compose-message-subject')).not_to be_displayed + expect(fj('#compose-message-subject')).to have_value(@convo.subject) + expect(fj('.message_subject_ro')).to be_displayed + expect(fj('.message_subject_ro').text).to eq @convo.subject + end + + it "should address replies to the most recent author by default" do + get_conversations + conversation_elements[0].click + wait_for_ajaximations + fj('#reply-btn').click + expect(ffj('input[name="recipients[]"]').length).to eq 1 + expect(fj('input[name="recipients[]"]')).to have_value(@s2.id.to_s) + end + + it "should add new messages to the conversation" do + get_conversations + initial_message_count = @convo.conversation_messages.length + conversation_elements[0].click + wait_for_ajaximations + fj('#reply-btn').click + set_message_body('Read chapters five and six.') + click_send + wait_for_ajaximations + expect(ffj('.message-item-view').length).to eq initial_message_count + 1 + @convo.reload + expect(@convo.conversation_messages.length).to eq initial_message_count + 1 + end + + it "should not allow adding recipients to private messages" do + @convo.update_attribute(:private_hash, '12345') + get_conversations + conversation_elements[0].click + wait_for_ajaximations + fj('#reply-btn').click + expect(fj('.compose_form .ac-input-box.disabled')).not_to be_nil + end + end +end \ No newline at end of file diff --git a/spec/selenium/conversations_multi_select_spec.rb b/spec/selenium/conversations_multi_select_spec.rb new file mode 100644 index 00000000000..d51b13cdd38 --- /dev/null +++ b/spec/selenium/conversations_multi_select_spec.rb @@ -0,0 +1,105 @@ +require File.expand_path(File.dirname(__FILE__) + '/helpers/conversations_common') + +describe "conversations new" do + include_examples "in-process server selenium tests" + + before do + conversation_setup + @s1 = user(name: "first student") + @s2 = user(name: "second student") + [@s1, @s2].each { |s| @course.enroll_student(s).update_attribute(:workflow_state, 'active') } + cat = @course.group_categories.create(:name => "the groups") + @group = cat.groups.create(:name => "the group", :context => @course) + @group.users = [@s1, @s2] + end + + describe "search" do + before do + @conv1 = conversation(@teacher, @s1) + @conv2 = conversation(@teacher, @s2) + end + + it "should allow finding messages by recipient" do + get_conversations + name = @s2.name + f('[role=main] header [role=search] input').send_keys(name) + keep_trying_until { fj(".ac-result:contains('#{name}')") }.click + expect(conversation_elements.length).to eq 1 + end + end + + describe "multi-select" do + before(:each) do + @conversations = [conversation(@teacher, @s1, @s2, workflow_state: 'read'), + conversation(@teacher, @s1, @s2, workflow_state: 'read')] + end + + def select_all_conversations + driver.action.key_down(modifier).perform + ff('.messages li').each do |message| + message.click + end + driver.action.key_up(modifier).perform + end + + it "should select multiple conversations" do + get_conversations + select_all_conversations + expect(ff('.messages li.active').count).to eq 2 + end + + it "should select all conversations" do + get_conversations + driver.action.key_down(modifier) + .send_keys('a') + .key_up(modifier) + .perform + expect(ff('.messages li.active').count).to eq 2 + end + + it "should archive multiple conversations" do + get_conversations + select_all_conversations + f('#archive-btn').click + wait_for_ajaximations + expect(conversation_elements.count).to eq 0 + run_progress_job + @conversations.each { |c| expect(c.reload).to be_archived } + end + + it "should delete multiple conversations" do + get_conversations + select_all_conversations + f('#delete-btn').click + driver.switch_to.alert.accept + wait_for_ajaximations + expect(conversation_elements.count).to eq 0 + end + + it "should mark multiple conversations as unread" do + skip('breaks b/c jenkins is weird') + get_conversations + select_all_conversations + click_unread_toggle_menu_item + keep_trying_until { expect(ffj('.read-state[aria-checked=false]').count).to eq 2 } + end + + it "should mark multiple conversations as unread" do + skip('breaks b/c jenkins is weird') + get_conversations + select_all_conversations + click_read_toggle_menu_item + keep_trying_until { expect(ffj('.read-state[aria-checked=true]').count).to eq 2 } + end + + it "should star multiple conversations" do + skip('breaks b/c jenkins is weird') + get_conversations + select_all_conversations + click_star_toggle_menu_item + run_progress_job + keep_trying_until { expect(ff('.star-btn.active').count).to eq 2 } + @conversations.each { |c| expect(c.reload).to be_starred } + end + end +end \ No newline at end of file diff --git a/spec/selenium/conversations_new_spec.rb b/spec/selenium/conversations_new_spec.rb deleted file mode 100644 index e9ced2fd8e7..00000000000 --- a/spec/selenium/conversations_new_spec.rb +++ /dev/null @@ -1,706 +0,0 @@ -require File.expand_path(File.dirname(__FILE__) + '/helpers/conversations_common') - -describe "conversations new" do - include_examples "in-process server selenium tests" - - def conversations_url - "/conversations" - end - - def get_conversations - get conversations_url - wait_for_ajaximations - end - - def conversation_elements - ff('.messages > li') - end - - def get_view_filter - f('.type-filter.bootstrap-select') - end - - def get_course_filter - skip('course filter selector fails intermittently (stale element reference), probably due to dynamic loading and refreshing') - #try to make it load the courses first so it doesn't randomly refresh - selector = '.course-filter.bootstrap-select' - driver.execute_script(%{$('#{selector}').focus();}) - wait_for_ajaximations - f(selector) - end - - def get_message_course - fj('.message_course.bootstrap-select') - end - - def get_message_recipients_input - fj('.compose_form #compose-message-recipients') - end - - def get_message_subject_input - fj('#compose-message-subject') - end - - def get_message_body_input - fj('.conversation_body') - end - - def get_bootstrap_select_value(element) - f('.selected .text', element).attribute('data-value') - end - - def set_bootstrap_select_value(element, new_value) - f('.dropdown-toggle', element).click() - f(%{.text[data-value="#{new_value}"]}, element).click() - end - - def select_view(new_view) - set_bootstrap_select_value(get_view_filter, new_view) - wait_for_ajaximations - end - - def select_course(new_course) - set_bootstrap_select_value(get_course_filter, new_course) - wait_for_ajaximations - end - - def click_star_toggle_menu_item - keep_trying_until do - driver.execute_script(%q{$('#admin-btn').hover().click()}) - sleep 1 - driver.execute_script(%q{$('#star-toggle-btn').hover().click()}) - wait_for_ajaximations - end - end - - def click_unread_toggle_menu_item - keep_trying_until do - driver.execute_script(%q{$('#admin-btn').hover().click()}) - sleep 1 - driver.execute_script(%q{$('#mark-unread-btn').hover().click()}) - wait_for_ajaximations - end - end - - def click_read_toggle_menu_item - keep_trying_until do - driver.execute_script(%q{$('#admin-btn').hover().click()}) - sleep 1 - driver.execute_script(%q{$('#mark-read-btn').hover().click()}) - wait_for_ajaximations - end - end - - def select_message_course(new_course, is_group = false) - new_course = new_course.name if new_course.respond_to? :name - fj('.dropdown-toggle', get_message_course).click - if is_group - wait_for_ajaximations - fj("a:contains('Groups')", get_message_course).click - end - fj("a:contains('#{new_course}')", get_message_course).click - end - - def add_message_recipient(to) - synthetic = !(to.instance_of?(User) || to.instance_of?(String)) - to = to.name if to.respond_to?(:name) - get_message_recipients_input.send_keys(to) - keep_trying_until { fj(".ac-result:contains('#{to}')") }.click - return unless synthetic - keep_trying_until { fj(".ac-result:contains('All in #{to}')") }.click - end - - def set_message_subject(subject) - get_message_subject_input.send_keys(subject) - end - - def set_message_body(body) - get_message_body_input.send_keys(body) - end - - def click_send - f('.compose-message-dialog .send-message').click - wait_for_ajaximations - end - - def compose(options={}) - fj('#compose-btn').click - wait_for_ajaximations - select_message_course(options[:course]) if options[:course] - (options[:to] || []).each {|recipient| add_message_recipient recipient} - set_message_subject(options[:subject]) if options[:subject] - set_message_body(options[:body]) if options[:body] - click_send if options[:send].nil? || options[:send] - end - - def run_progress_job - return unless progress = Progress.where(tag: 'conversation_batch_update').first - job = Delayed::Job.find(progress.delayed_job_id) - job.invoke_job - end - - let :modifier do - if driver.execute_script('return !!window.navigator.userAgent.match(/Macintosh/)') - :meta - else - :control - end - end - - before do - conversation_setup - @s1 = user(name: "first student") - @s2 = user(name: "second student") - [@s1, @s2].each { |s| @course.enroll_student(s).update_attribute(:workflow_state, 'active') } - cat = @course.group_categories.create(:name => "the groups") - @group = cat.groups.create(:name => "the group", :context => @course) - @group.users = [@s1, @s2] - end - - describe "message sending" do - it "should start a group conversation when there is only one recipient" do - get_conversations - compose course: @course, to: [@s1], subject: 'single recipient', body: 'hallo!' - c = @s1.conversations.last.conversation - expect(c.subject).to eq('single recipient') - expect(c.private?).to be_falsey - end - - it "should start a group conversation when there is more than one recipient" do - get_conversations - compose course: @course, to: [@s1, @s2], subject: 'multiple recipients', body: 'hallo!' - c = @s1.conversations.last.conversation - expect(c.subject).to eq('multiple recipients') - expect(c.private?).to be_falsey - expect(c.conversation_participants.collect(&:user_id).sort).to eq([@teacher, @s1, @s2].collect(&:id).sort) - end - - it "should allow admins to send a message without picking a context" do - user = account_admin_user - user_logged_in({:user => user}) - get_conversations - compose to: [@s1], subject: 'context-free', body: 'hallo!' - c = @s1.conversations.last.conversation - expect(c.subject).to eq 'context-free' - expect(c.context).to eq Account.default - end - - it "should not allow non-admins to send a message without picking a context" do - get_conversations - fj('#compose-btn').click - wait_for_animations - expect(fj('#compose-new-message .ac-input')).to have_attribute(:disabled, 'true') - end - - it "should allow non-admins to send a message to an account-level group" do - @group = Account.default.groups.create(:name => "the group") - @group.add_user(@s1) - @group.add_user(@s2) - @group.save - user_logged_in({:user => @s1}) - get_conversations - fj('#compose-btn').click - wait_for_ajaximations - select_message_course(@group, true) - add_message_recipient @s2 - end - - it "should allow admins to message users from their profiles" do - user = account_admin_user - user_logged_in({:user => user}) - get "/accounts/#{Account.default.id}/users" - wait_for_ajaximations - f('li.user a').click - wait_for_ajaximations - f('.icon-email').click - wait_for_ajaximations - expect(f('.ac-token')).not_to be_nil - end - - it "should allow selecting multiple recipients in one search" do - get_conversations - fj('#compose-btn').click - wait_for_ajaximations - select_message_course(@course) - get_message_recipients_input.send_keys('student') - driver.action.key_down(modifier).perform - keep_trying_until { fj(".ac-result:contains('first student')") }.click - driver.action.key_up(modifier).perform - fj(".ac-result:contains('second student')").click - expect(ff('.ac-token').count).to eq 2 - end - - it "should not send the message on shift-enter" do - get_conversations - compose course: @course, to: [@s1], subject: 'context-free', body: 'hallo!', send: false - driver.action.key_down(:shift).perform - get_message_body_input.send_keys(:enter) - driver.action.key_up(:shift).perform - expect(fj('#compose-new-message:visible')).not_to be_nil - end - - context "user notes" do - before(:each) do - @course.account.update_attribute(:enable_user_notes, true) - user_session(@teacher) - get_conversations - end - - it "should be allowed on new private conversations with students" do - compose course: @course, to: [@s1, @s2], body: 'hallo!', send: false - - checkbox = f(".user_note") - expect(checkbox).to be_displayed - checkbox.click - - count1 = @s1.user_notes.count - count2 = @s2.user_notes.count - click_send - expect(@s1.user_notes.reload.count).to eq count1 + 1 - expect(@s2.user_notes.reload.count).to eq count2 + 1 - end - - it "should be allowed with student groups" do - compose course: @course, to: [@group], body: 'hallo!', send: false - - checkbox = f(".user_note") - expect(checkbox).to be_displayed - checkbox.click - - count1 = @s1.user_notes.count - click_send - expect(@s1.user_notes.reload.count).to eq count1 + 1 - end - - it "should not be allowed if disabled" do - @course.account.update_attribute(:enable_user_notes, false) - get_conversations - compose course: @course, to: [@s1], body: 'hallo!', send: false - expect(f(".user_note")).not_to be_displayed - end - - it "should not be allowed for students" do - user_session(@s1) - get_conversations - compose course: @course, to: [@s2], body: 'hallo!', send: false - expect(f(".user_note")).not_to be_displayed - end - - it "should not be allowed with non-student recipient" do - compose course: @course, to: [@teacher], body: 'hallo!', send: false - expect(f(".user_note")).not_to be_displayed - end - end - end - - describe "replying" do - before do - cp = conversation(@s1, @teacher, @s2, workflow_state: 'unread') - @convo = cp.conversation - @convo.update_attribute(:subject, 'homework') - @convo.add_message(@s1, "What's this week's homework?") - @convo.add_message(@s2, "I need the homework too.") - end - - it "should maintain context and subject" do - get_conversations - conversation_elements[0].click - wait_for_ajaximations - fj('#reply-btn').click - expect(fj('#compose-message-course')).to have_attribute(:disabled, 'true') - expect(fj('#compose-message-course')).to have_value(@course.id.to_s) - expect(fj('#compose-message-subject')).to have_attribute(:disabled, 'true') - expect(fj('#compose-message-subject')).not_to be_displayed - expect(fj('#compose-message-subject')).to have_value(@convo.subject) - expect(fj('.message_subject_ro')).to be_displayed - expect(fj('.message_subject_ro').text).to eq @convo.subject - end - - it "should address replies to the most recent author by default" do - get_conversations - conversation_elements[0].click - wait_for_ajaximations - fj('#reply-btn').click - expect(ffj('input[name="recipients[]"]').length).to eq 1 - expect(fj('input[name="recipients[]"]')).to have_value(@s2.id.to_s) - end - - it "should add new messages to the conversation" do - get_conversations - initial_message_count = @convo.conversation_messages.length - conversation_elements[0].click - wait_for_ajaximations - fj('#reply-btn').click - set_message_body('Read chapters five and six.') - click_send - wait_for_ajaximations - expect(ffj('.message-item-view').length).to eq initial_message_count + 1 - @convo.reload - expect(@convo.conversation_messages.length).to eq initial_message_count + 1 - end - - it "should not allow adding recipients to private messages" do - @convo.update_attribute(:private_hash, '12345') - get_conversations - conversation_elements[0].click - wait_for_ajaximations - fj('#reply-btn').click - expect(fj('.compose_form .ac-input-box.disabled')).not_to be_nil - end - end - - describe "view filter" do - before do - conversation(@teacher, @s1, @s2, workflow_state: 'unread') - conversation(@teacher, @s1, @s2, workflow_state: 'read', starred: true) - conversation(@teacher, @s1, @s2, workflow_state: 'archived', starred: true) - end - - it "should default to inbox view" do - get_conversations - selected = expect(get_bootstrap_select_value(get_view_filter)).to eq 'inbox' - expect(conversation_elements.size).to eq 2 - end - - it "should have an unread view" do - get_conversations - select_view('unread') - expect(conversation_elements.size).to eq 1 - end - - it "should have an starred view" do - get_conversations - select_view('starred') - expect(conversation_elements.size).to eq 2 - end - - it "should have an sent view" do - get_conversations - select_view('sent') - expect(conversation_elements.size).to eq 3 - end - - it "should have an archived view" do - get_conversations - select_view('archived') - expect(conversation_elements.size).to eq 1 - end - - it "should default to all courses view" do - get_conversations - selected = expect(get_bootstrap_select_value(get_course_filter)).to eq '' - expect(conversation_elements.size).to eq 2 - end - - it "should truncate long course names" do - @course.name = "this is a very long course name that will be truncated" - @course.save! - get_conversations - select_course(@course.id) - button_text = f('.filter-option', get_course_filter).text - expect(button_text).not_to eq @course.name - expect(button_text[0...5]).to eq @course.name[0...5] - expect(button_text[-5..-1]).to eq @course.name[-5..-1] - end - - it "should filter by course" do - get_conversations - select_course(@course.id) - expect(conversation_elements.size).to eq 2 - end - - it "should filter by course plus view" do - get_conversations - select_course(@course.id) - select_view('unread') - expect(conversation_elements.size).to eq 1 - end - - it "should hide the spinner after deleting the last conversation" do - get_conversations - select_view('archived') - expect(conversation_elements.size).to eq 1 - conversation_elements[0].click - wait_for_ajaximations - fj('#delete-btn').click - driver.switch_to.alert.accept - wait_for_ajaximations - expect(conversation_elements.size).to eq 0 - expect(ffj('.message-list .paginatedLoadingIndicator:visible').length).to eq 0 - expect(ffj('.actions .btn-group button:disabled').size).to eq 4 - end - end - - describe "starred" do - before do - @conv_unstarred = conversation(@teacher, @s1, @s2) - @conv_starred = conversation(@teacher, @s1, @s2) - @conv_starred.starred = true - @conv_starred.save! - end - - it "should star via star icon" do - get_conversations - unstarred_elt = conversation_elements[1] - # make star button visible via mouse over - driver.mouse.move_to(unstarred_elt) - wait_for_ajaximations - star_btn = f('.star-btn', unstarred_elt) - expect(star_btn).to be_present - expect(f('.active', unstarred_elt)).to be_nil - - star_btn.click - wait_for_ajaximations - expect(f('.active', unstarred_elt)).to be_present - expect(@conv_unstarred.reload.starred).to be_truthy - end - - it "should unstar via star icon" do - get_conversations - starred_elt = conversation_elements[0] - star_btn = f('.star-btn', starred_elt) - expect(star_btn).to be_present - expect(f('.active', starred_elt)).to be_present - - star_btn.click - wait_for_ajaximations - expect(f('.active', starred_elt)).to be_nil - expect(@conv_starred.reload.starred).to be_falsey - end - - it "should star via gear menu" do - get_conversations - unstarred_elt = conversation_elements[1] - unstarred_elt.click - wait_for_ajaximations - click_star_toggle_menu_item - expect(f('.active', unstarred_elt)).to be_present - run_progress_job - expect(@conv_unstarred.reload.starred).to be_truthy - end - - it "should unstar via gear menu" do - get_conversations - starred_elt = conversation_elements[0] - starred_elt.click - wait_for_ajaximations - click_star_toggle_menu_item - expect(f('.active', starred_elt)).to be_nil - run_progress_job - expect(@conv_starred.reload.starred).to be_falsey - end - end - - describe "search" do - before do - @conv1 = conversation(@teacher, @s1) - @conv2 = conversation(@teacher, @s2) - end - - it "should allow finding messages by recipient" do - get_conversations - name = @s2.name - f('[role=main] header [role=search] input').send_keys(name) - keep_trying_until { fj(".ac-result:contains('#{name}')") }.click - expect(conversation_elements.length).to eq 1 - end - end - - describe "multi-select" do - before(:each) do - @conversations = [conversation(@teacher, @s1, @s2, workflow_state: 'read'), - conversation(@teacher, @s1, @s2, workflow_state: 'read')] - end - - def select_all_conversations - driver.action.key_down(modifier).perform - ff('.messages li').each do |message| - message.click - end - driver.action.key_up(modifier).perform - end - - it "should select multiple conversations" do - get_conversations - select_all_conversations - expect(ff('.messages li.active').count).to eq 2 - end - - it "should select all conversations" do - get_conversations - driver.action.key_down(modifier) - .send_keys('a') - .key_up(modifier) - .perform - expect(ff('.messages li.active').count).to eq 2 - end - - it "should archive multiple conversations" do - get_conversations - select_all_conversations - f('#archive-btn').click - wait_for_ajaximations - expect(conversation_elements.count).to eq 0 - run_progress_job - @conversations.each { |c| expect(c.reload).to be_archived } - end - - it "should delete multiple conversations" do - get_conversations - select_all_conversations - f('#delete-btn').click - driver.switch_to.alert.accept - wait_for_ajaximations - expect(conversation_elements.count).to eq 0 - end - - it "should mark multiple conversations as unread" do - skip('breaks b/c jenkins is weird') - get_conversations - select_all_conversations - click_unread_toggle_menu_item - keep_trying_until { expect(ffj('.read-state[aria-checked=false]').count).to eq 2 } - end - - it "should mark multiple conversations as unread" do - skip('breaks b/c jenkins is weird') - get_conversations - select_all_conversations - click_read_toggle_menu_item - keep_trying_until { expect(ffj('.read-state[aria-checked=true]').count).to eq 2 } - end - - it "should star multiple conversations" do - skip('breaks b/c jenkins is weird') - get_conversations - select_all_conversations - click_star_toggle_menu_item - run_progress_job - keep_trying_until { expect(ff('.star-btn.active').count).to eq 2 } - @conversations.each { |c| expect(c.reload).to be_starred } - end - end - - describe 'conversations inbox opt-out option' do - it "should be hidden a feature flag" do - get "/profile/settings" - expect(ff('#disable_inbox').count).to eq 0 - end - - it "should reveal when the feature flag is set" do - @course.root_account.enable_feature!(:allow_opt_out_of_inbox) - get "/profile/settings" - expect(ff('#disable_inbox').count).to eq 1 - end - - context "when activated" do - it "should set the notification preferences for conversations to ASAP, and hide those options" do - @course.root_account.enable_feature!(:allow_opt_out_of_inbox) - expect(@teacher.reload.disabled_inbox?).to be_falsey - notification = Notification.create!(workflow_state: "active", name: "Conversation Message", - category: "Conversation Message", delay_for: 0) - policy = NotificationPolicy.create!(notification_id: notification.id, communication_channel_id: @teacher.email_channel.id, broadcast: true, frequency: "weekly") - @teacher.update_attribute(:unread_conversations_count, 3) - sleep 0.5 - - get '/profile/communication' - expect(ff('td[data-category="conversation_message"]').count).to eq 1 - expect(ff('.unread-messages-count').count).to eq 1 - - get "/profile/settings" - f('#disable_inbox').click - sleep 0.5 - - expect(@teacher.reload.disabled_inbox?).to be_truthy - - get '/profile/communication' - expect(ff('td[data-category="conversation_message"]').count).to eq 0 - expect(policy.reload.frequency).to eq "immediately" - expect(ff('.unread-messages-count').count).to eq 0 - end - end - end - - context 'submission comment stream items' do - before do - @course1 = @course - @course2 = course(active_course: true) - teacher_in_course(user: @teacher, course: @course2, active_all: true) - student_in_course(user: @s1, active_all: true, course: @course1) - student_in_course(user: @s2, active_all: true, course: @course2) - - def assignment_with_submission_comments(title, student, course) - assignment = course.assignments.create!(:title => title, :description => 'hai', :points_possible => '14.2', :submission_types => 'online_text_entry') - sub = assignment.grade_student(student, { :grade => '12', :grader => @teacher}).first - sub.workflow_state = 'submitted' - sub.submission_comments.create!(:comment => 'c1', :author => @teacher, :recipient_id => student.id) - sub.submission_comments.create!(:comment => 'c2', :author => student, :recipient_id => @teacher.id) - sub.save! - sub - end - - assignment_with_submission_comments('assignment 1', @s1, @course1) - @submission = assignment_with_submission_comments('assignment 2', @s2, @course2) - end - - describe 'view filter' do - it 'shows submission comments' do - get_conversations - select_view('submission_comments') - expect(conversation_elements.size).to eq 2 - end - - it 'filters by course' do - get_conversations - select_view('submission_comments') - select_course(@course1.id) - expect(conversation_elements.size).to eq 1 - end - - it 'filters by submitter' do - get_conversations - select_view('submission_comments') - name = @s2.name - f('[role=main] header [role=search] input').send_keys(name) - keep_trying_until { fj(".ac-result:contains('#{name}')") }.click - expect(conversation_elements.length).to eq 1 - end - end - - it 'adds new messages to the view' do - get_conversations - select_view('submission_comments') - initial_message_count = @submission.submission_comments.count - conversation_elements[0].click - wait_for_ajaximations - fj('#submission-reply-btn').click - fj('.reply_body').send_keys('c3') - fj('.submission-comment-reply-dialog .send-message').click - wait_for_ajaximations - expect(ffj('.message-item-view').length).to eq (initial_message_count + 1) - expect(@submission.reload.submission_comments.count).to eq (initial_message_count + 1) - end - - it 'marks unread on click' do - expect(@submission.read?(@teacher)).to be_falsey - get_conversations - select_view('submission_comments') - conversation_elements[0].click - wait_for_ajaximations - expect(@submission.read?(@teacher)).to be_truthy - end - - it 'marks an read/unread' do - expect(@submission.read?(@teacher)).to be_falsey - get_conversations - select_view('submission_comments') - toggle = fj('.read-state', conversation_elements[0]) - toggle.click - wait_for_ajaximations - expect(@submission.read?(@teacher)).to be_truthy - toggle.click - wait_for_ajaximations - expect(@submission.read?(@teacher)).to be_falsey - end - end -end diff --git a/spec/selenium/conversations_spec.rb b/spec/selenium/conversations_spec.rb new file mode 100644 index 00000000000..ca15f9160ec --- /dev/null +++ b/spec/selenium/conversations_spec.rb @@ -0,0 +1,157 @@ +require File.expand_path(File.dirname(__FILE__) + '/helpers/conversations_common') + +describe "conversations new" do + include_examples "in-process server selenium tests" + + before do + conversation_setup + @s1 = user(name: "first student") + @s2 = user(name: "second student") + [@s1, @s2].each { |s| @course.enroll_student(s).update_attribute(:workflow_state, 'active') } + cat = @course.group_categories.create(:name => "the groups") + @group = cat.groups.create(:name => "the group", :context => @course) + @group.users = [@s1, @s2] + end + + describe "view filter" do + before do + conversation(@teacher, @s1, @s2, workflow_state: 'unread') + conversation(@teacher, @s1, @s2, workflow_state: 'read', starred: true) + conversation(@teacher, @s1, @s2, workflow_state: 'archived', starred: true) + end + + it "should default to inbox view" do + get_conversations + selected = expect(get_bootstrap_select_value(get_view_filter)).to eq 'inbox' + expect(conversation_elements.size).to eq 2 + end + + it "should have an unread view" do + get_conversations + select_view('unread') + expect(conversation_elements.size).to eq 1 + end + + it "should have an starred view" do + get_conversations + select_view('starred') + expect(conversation_elements.size).to eq 2 + end + + it "should have an sent view" do + get_conversations + select_view('sent') + expect(conversation_elements.size).to eq 3 + end + + it "should have an archived view" do + get_conversations + select_view('archived') + expect(conversation_elements.size).to eq 1 + end + + it "should default to all courses view" do + get_conversations + selected = expect(get_bootstrap_select_value(get_course_filter)).to eq '' + expect(conversation_elements.size).to eq 2 + end + + it "should truncate long course names" do + @course.name = "this is a very long course name that will be truncated" + @course.save! + get_conversations + select_course(@course.id) + button_text = f('.filter-option', get_course_filter).text + expect(button_text).not_to eq @course.name + expect(button_text[0...5]).to eq @course.name[0...5] + expect(button_text[-5..-1]).to eq @course.name[-5..-1] + end + + it "should filter by course" do + get_conversations + select_course(@course.id) + expect(conversation_elements.size).to eq 2 + end + + it "should filter by course plus view" do + get_conversations + select_course(@course.id) + select_view('unread') + expect(conversation_elements.size).to eq 1 + end + + it "should hide the spinner after deleting the last conversation" do + get_conversations + select_view('archived') + expect(conversation_elements.size).to eq 1 + conversation_elements[0].click + wait_for_ajaximations + fj('#delete-btn').click + driver.switch_to.alert.accept + wait_for_ajaximations + expect(conversation_elements.size).to eq 0 + expect(ffj('.message-list .paginatedLoadingIndicator:visible').length).to eq 0 + expect(ffj('.actions .btn-group button:disabled').size).to eq 4 + end + end + + describe "starred" do + before do + @conv_unstarred = conversation(@teacher, @s1, @s2) + @conv_starred = conversation(@teacher, @s1, @s2) + @conv_starred.starred = true + @conv_starred.save! + end + + it "should star via star icon" do + get_conversations + unstarred_elt = conversation_elements[1] + # make star button visible via mouse over + driver.mouse.move_to(unstarred_elt) + wait_for_ajaximations + star_btn = f('.star-btn', unstarred_elt) + expect(star_btn).to be_present + expect(f('.active', unstarred_elt)).to be_nil + + star_btn.click + wait_for_ajaximations + expect(f('.active', unstarred_elt)).to be_present + expect(@conv_unstarred.reload.starred).to be_truthy + end + + it "should unstar via star icon" do + get_conversations + starred_elt = conversation_elements[0] + star_btn = f('.star-btn', starred_elt) + expect(star_btn).to be_present + expect(f('.active', starred_elt)).to be_present + + star_btn.click + wait_for_ajaximations + expect(f('.active', starred_elt)).to be_nil + expect(@conv_starred.reload.starred).to be_falsey + end + + it "should star via gear menu" do + get_conversations + unstarred_elt = conversation_elements[1] + unstarred_elt.click + wait_for_ajaximations + click_star_toggle_menu_item + expect(f('.active', unstarred_elt)).to be_present + run_progress_job + expect(@conv_unstarred.reload.starred).to be_truthy + end + + it "should unstar via gear menu" do + get_conversations + starred_elt = conversation_elements[0] + starred_elt.click + wait_for_ajaximations + click_star_toggle_menu_item + expect(f('.active', starred_elt)).to be_nil + run_progress_job + expect(@conv_starred.reload.starred).to be_falsey + end + end +end diff --git a/spec/selenium/conversations_submission_comments_spec.rb b/spec/selenium/conversations_submission_comments_spec.rb new file mode 100644 index 00000000000..85e9248413c --- /dev/null +++ b/spec/selenium/conversations_submission_comments_spec.rb @@ -0,0 +1,98 @@ +require File.expand_path(File.dirname(__FILE__) + '/helpers/conversations_common') + +describe "conversations new" do + include_examples "in-process server selenium tests" + + before do + conversation_setup + @s1 = user(name: "first student") + @s2 = user(name: "second student") + [@s1, @s2].each { |s| @course.enroll_student(s).update_attribute(:workflow_state, 'active') } + cat = @course.group_categories.create(:name => "the groups") + @group = cat.groups.create(:name => "the group", :context => @course) + @group.users = [@s1, @s2] + end + + context 'submission comment stream items' do + before do + @course1 = @course + @course2 = course(active_course: true) + teacher_in_course(user: @teacher, course: @course2, active_all: true) + student_in_course(user: @s1, active_all: true, course: @course1) + student_in_course(user: @s2, active_all: true, course: @course2) + + def assignment_with_submission_comments(title, student, course) + assignment = course.assignments.create!(:title => title, :description => 'hai', :points_possible => '14.2', :submission_types => 'online_text_entry') + sub = assignment.grade_student(student, { :grade => '12', :grader => @teacher}).first + sub.workflow_state = 'submitted' + sub.submission_comments.create!(:comment => 'c1', :author => @teacher, :recipient_id => student.id) + sub.submission_comments.create!(:comment => 'c2', :author => student, :recipient_id => @teacher.id) + sub.save! + sub + end + + assignment_with_submission_comments('assignment 1', @s1, @course1) + @submission = assignment_with_submission_comments('assignment 2', @s2, @course2) + end + + describe 'view filter' do + it 'shows submission comments' do + get_conversations + select_view('submission_comments') + expect(conversation_elements.size).to eq 2 + end + + it 'filters by course' do + get_conversations + select_view('submission_comments') + select_course(@course1.id) + expect(conversation_elements.size).to eq 1 + end + + it 'filters by submitter' do + get_conversations + select_view('submission_comments') + name = @s2.name + f('[role=main] header [role=search] input').send_keys(name) + keep_trying_until { fj(".ac-result:contains('#{name}')") }.click + expect(conversation_elements.length).to eq 1 + end + end + + it 'adds new messages to the view' do + get_conversations + select_view('submission_comments') + initial_message_count = @submission.submission_comments.count + conversation_elements[0].click + wait_for_ajaximations + fj('#submission-reply-btn').click + fj('.reply_body').send_keys('c3') + fj('.submission-comment-reply-dialog .send-message').click + wait_for_ajaximations + expect(ffj('.message-item-view').length).to eq (initial_message_count + 1) + expect(@submission.reload.submission_comments.count).to eq (initial_message_count + 1) + end + + it 'marks unread on click' do + expect(@submission.read?(@teacher)).to be_falsey + get_conversations + select_view('submission_comments') + conversation_elements[0].click + wait_for_ajaximations + expect(@submission.read?(@teacher)).to be_truthy + end + + it 'marks an read/unread' do + expect(@submission.read?(@teacher)).to be_falsey + get_conversations + select_view('submission_comments') + toggle = fj('.read-state', conversation_elements[0]) + toggle.click + wait_for_ajaximations + expect(@submission.read?(@teacher)).to be_truthy + toggle.click + wait_for_ajaximations + expect(@submission.read?(@teacher)).to be_falsey + end + end +end \ No newline at end of file diff --git a/spec/selenium/helpers/conversations_common.rb b/spec/selenium/helpers/conversations_common.rb index 08052fab28e..07800048c85 100644 --- a/spec/selenium/helpers/conversations_common.rb +++ b/spec/selenium/helpers/conversations_common.rb @@ -1,5 +1,23 @@ require File.expand_path(File.dirname(__FILE__) + '/../common') + +def modifier + @_modifier ||= determine_modifier +end + +def determine_modifier + if driver.execute_script('return !!window.navigator.userAgent.match(/Macintosh/)') + :meta + else + :control + end +end + +def get_conversations + get conversations_path + wait_for_ajaximations +end + def conversation_setup course_with_teacher_logged_in @@ -13,213 +31,129 @@ def conversation_setup @user.save end -def new_conversation(reload=true) - if reload - get "/conversations" - keep_trying_until { fj("#create_message_form form:visible") } - else - f("#action_compose_message").click - wait_for_ajaximations - end - - - @input = fj("#create_message_form input:visible") - @browser = fj("#create_message_form .browser:visible") - @level = 1 - @elements = nil +def conversation_elements + ff('.messages > li') end -def add_recipient(search, input_selector=".recipients") - input = driver.execute_script("return $('#{input_selector}').data('token_input').$input[0]") - input.send_keys(search) - keep_trying_until { driver.execute_script("return $('#{input_selector}').data('token_input').selector.list.query.search") == search } +def get_view_filter + f('.type-filter.bootstrap-select') +end + +def get_course_filter + skip('course filter selector fails intermittently (stale element reference), probably due to dynamic loading and refreshing') + #try to make it load the courses first so it doesn't randomly refresh + selector = '.course-filter.bootstrap-select' + driver.execute_script(%{$('#{selector}').focus();}) wait_for_ajaximations - input.send_keys(:return) + f(selector) end -def browse_menu - @browser.click - wait_for_ajaximations(500) - keep_trying_until { expect(ffj('.autocomplete_menu:visible .list').size).to eq @level } - wait_for_ajaximations(500) +def get_message_course + fj('.message_course.bootstrap-select') end -def browse(*names) - name = names.shift - @level += 1 - prev_elements = elements - element = prev_elements.detect { |e| e.last == name } or raise "menu item does not exist" - element.first.click - wait_for_ajaximations(500) - keep_trying_until { expect(ffj('.autocomplete_menu:visible .list').size).to eq @level } - @elements = nil - - if names.present? - browse(*names, &Proc.new) - else - yield - end - - @level -= 1 - @elements = nil - @input.send_keys(:arrow_left) unless ffj('.autocomplete_menu:visible .list').empty? - sleep 1 +def get_message_recipients_input + fj('.compose_form #compose-message-recipients') end -def elements - wait_for_js - @elements = ffj(".autocomplete_menu:visible .list:last ul:last li").map { |e| - [e, (e.find_element(:tag_name, :b).text rescue e.text)] - } +def get_message_subject_input + fj('#compose-message-subject') end -def menu - elements.map(&:last) +def get_message_body_input + fj('.conversation_body') end -def toggleable - with_class("toggleable") +def get_bootstrap_select_value(element) + f('.selected .text', element).attribute('data-value') end -def toggled - with_class("on") +def set_bootstrap_select_value(element, new_value) + f('.dropdown-toggle', element).click() + f(%{.text[data-value="#{new_value}"]}, element).click() end -def with_class(klass) - elements.select { |e| e.first.attribute('class') =~ /(\A| )#{klass}(\z| )/ }.map(&:last) -end - -def click(name) - element = elements.detect { |e| e.last == name } or raise "menu item does not exist" - element.first.click -end - -def toggle(name) - element = elements.detect { |e| e.last == name } or raise "menu item does not exist" - element.first.find_element(:class, 'toggle').click -end - -def tokens - ffj("#create_message_form .token_input li div").map(&:text) -end - -def search(text, input_selector=".recipients") - @input.send_keys(text) - keep_trying_until { driver.execute_script("return $('#{input_selector}').data('token_input').selector.list.query.search") == text } +def select_view(new_view) + set_bootstrap_select_value(get_view_filter, new_view) wait_for_ajaximations - @elements = nil - yield - @elements = nil - if input_selector == ".recipients" - @input.send_keys(*@input.attribute('value').size.times.map { :backspace }) - keep_trying_until do - driver.execute_script("return $('.autocomplete_menu:visible').toArray();").size == 0 || driver.execute_script("return $('#{input_selector}').data('token_input').selector.list.query.search") == '' - end +end + +def select_course(new_course) + set_bootstrap_select_value(get_course_filter, new_course) + wait_for_ajaximations +end + +def click_star_toggle_menu_item + keep_trying_until do + driver.execute_script(%q{$('#admin-btn').hover().click()}) + sleep 1 + driver.execute_script(%q{$('#star-toggle-btn').hover().click()}) wait_for_ajaximations end end -def submit_message_form(opts={}) - opts[:message] ||= "Test Message" - opts[:attachments] ||= [] - opts[:add_recipient] = true unless opts.has_key?(:add_recipient) - opts[:group_conversation] = true unless opts.has_key?(:group_conversation) - opts[:existing_conversation] = false unless opts.has_key?(:existing_conversation) - - if opts[:add_recipient] && browser = fj("#create_message_form .browser:visible") - browser.click - wait_for_ajaximations(500) - fj('.autocomplete_menu .selectable:visible').click - wait_for_ajaximations(500) - fj('.autocomplete_menu .toggleable:visible .toggle').click - wait_for_ajaximations(500) - expect(ff('.token_input ul li').length).to be > 0 - fj("#create_message_form input:visible").send_keys("\t") - end - - fj("#create_message_form textarea").send_keys(opts[:message]) - - opts[:attachments].each_with_index do |fullpath, i| - f(".action_add_attachment").click - - keep_trying_until { ffj("#create_message_form .file_input:visible")[i] }.send_keys(fullpath) - end - - if opts[:media_comment] - driver.execute_script <<-JS - $("#create_message_form input[name=media_comment_id]").val(#{opts[:media_comment].first.inspect}) - $("#create_message_form input[name=media_comment_type]").val(#{opts[:media_comment].last.inspect}) - $("#create_message_form .media_comment").show() - $("#create_message_form .action_media_comment").hide() - JS - end - - group_conversation_link = f(".group_conversation") - group_conversation_link.click if group_conversation_link && group_conversation_link.displayed? && opts[:group_conversation] - - expect { - submit_form('#create_message_form form') - # file uploads can trigger multiple ajax requests, so we just wait for the - # sent notification - assert_message_status("sent", opts[:message][0, 10]) - }.to change(ConversationMessage, :count).by_at_least(opts[:group_conversation] ? 1 : ff('.token_input li').size) - - @elements = nil - - if opts[:group_conversation] - message = ConversationMessage.last - # whether the message should be visible depends on whether we were appending to an already visible conversation - if opts[:existing_conversation] - expect(f("#message_#{message.id}")).not_to be_nil - else - expect(f("#message_#{message.id}")).to be_nil - end - message +def click_unread_toggle_menu_item + keep_trying_until do + driver.execute_script(%q{$('#admin-btn').hover().click()}) + sleep 1 + driver.execute_script(%q{$('#mark-unread-btn').hover().click()}) + wait_for_ajaximations end end -def assert_message_status(status = "sent", text = '') +def click_read_toggle_menu_item + keep_trying_until do + driver.execute_script(%q{$('#admin-btn').hover().click()}) + sleep 1 + driver.execute_script(%q{$('#mark-read-btn').hover().click()}) + wait_for_ajaximations + end +end + +def select_message_course(new_course, is_group = false) + new_course = new_course.name if new_course.respond_to? :name + fj('.dropdown-toggle', get_message_course).click + if is_group + wait_for_ajaximations + fj("a:contains('Groups')", get_message_course).click + end + fj("a:contains('#{new_course}')", get_message_course).click +end + +def add_message_recipient(to) + synthetic = !(to.instance_of?(User) || to.instance_of?(String)) + to = to.name if to.respond_to?(:name) + get_message_recipients_input.send_keys(to) + keep_trying_until { fj(".ac-result:contains('#{to}')") }.click + return unless synthetic + keep_trying_until { fj(".ac-result:contains('All in #{to}')") }.click +end + +def set_message_subject(subject) + get_message_subject_input.send_keys(subject) +end + +def set_message_body(body) + get_message_body_input.send_keys(body) +end + +def click_send + f('.compose-message-dialog .send-message').click wait_for_ajaximations - keep_trying_until { - e = ff('#message_status li').last - expect(e.text.downcase).to include("#{status} #{text.downcase}") #rescue false - } end -def get_messages(load_convo = true, keep_trying = true) - if load_convo - get "/conversations" - get_conversations.first.click - end - elements = nil - keep_trying_until do - elements = ff("div#messages > ul.messages > li") - elements.size > 0 - end - elements -end - -def get_conversations(keep_trying = true) - elements = nil - keep_trying_until do - elements = driver.execute_script("return $('#conversations .conversations > ul > li').not('.scrollable-list-item-loading,.scrollable-list-item-deleting,.scrollable-list-item-moving').toArray();") - return elements unless keep_trying - elements.size > 0 - end - elements -end - -def delete_selected_messages(confirm_conversation_deleted = true) - orig_size = get_conversations.size - - wait_for_ajaximations(500) - delete = f('#action_delete') - expect(delete).to be_displayed - delete.click - driver.switch_to.alert.accept - - if confirm_conversation_deleted - keep_trying_until { expect(get_conversations(false).size).to eq orig_size - 1 } - end +def compose(options={}) + fj('#compose-btn').click + wait_for_ajaximations + select_message_course(options[:course]) if options[:course] + (options[:to] || []).each {|recipient| add_message_recipient recipient} + set_message_subject(options[:subject]) if options[:subject] + set_message_body(options[:body]) if options[:body] + click_send if options[:send].nil? || options[:send] end +def run_progress_job + return unless progress = Progress.where(tag: 'conversation_batch_update').first + job = Delayed::Job.find(progress.delayed_job_id) + job.invoke_job +end \ No newline at end of file