Split out conversations new spec

Fixes CNVS-16487

Test Plan:

Ensure tests still pass

Change-Id: If8107a47c498030de4b119db932bcf2f27ec26d1
Reviewed-on: https://gerrit.instructure.com/43457
Reviewed-by: Mark Severson <markse@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
Product-Review: Matthew Wheeler <mwheeler@instructure.com>
QA-Review: Matthew Wheeler <mwheeler@instructure.com>
This commit is contained in:
Matt Wheeler 2014-10-28 15:11:23 -06:00 committed by Matthew Wheeler
parent 217ae3b60b
commit 2aa7668f0c
7 changed files with 730 additions and 886 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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