Handle groups without area when sorting unscheduled sessions in schedule editor. Fixes #3173. Commit ready for merge.

- Legacy-Id: 19073
This commit is contained in:
Jennifer Richards 2021-06-03 18:55:34 +00:00
parent bfad845662
commit 0ade3f789a
2 changed files with 144 additions and 13 deletions

View file

@ -24,7 +24,7 @@ from ietf.group import colors
from ietf.person.models import Person
from ietf.group.models import Group
from ietf.group.factories import GroupFactory
from ietf.meeting.factories import SessionFactory, TimeSlotFactory
from ietf.meeting.factories import MeetingFactory, SessionFactory, TimeSlotFactory
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting
from ietf.meeting.models import (Schedule, SchedTimeSessAssignment, Session,
Room, TimeSlot, Constraint, ConstraintName,
@ -39,7 +39,7 @@ if selenium_enabled():
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoSuchElementException, TimeoutException
@ifSeleniumEnabled
@ -225,6 +225,137 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
self.assertTrue(self.driver.find_elements_by_css_selector('#timeslot{} #session{}'.format(slot4.pk, s1.pk)))
def test_unassigned_sessions_sort(self):
"""Unassigned session sorting should behave correctly
Sorting options and list of sort criteria
name (name, duration, id)
parent (parent, name, duration, id)
duration (duration, parent, name, id)
comments (presence of comments, parent, name, duration, id)
"""
# Define helpers
def sort_by_position(driver, sessions):
"""Helper to sort sessions by the position of their session element in the unscheduled box"""
def _sort_key(sess):
elt = driver.find_element_by_id('session{}'.format(sess.pk))
return (elt.location['y'], elt.location['x'])
return sorted(sessions, key=_sort_key)
wait = WebDriverWait(self.driver, 2)
def wait_for_order(sessions, expected_order, fail_message):
"""Helper to wait for sorting to complete"""
try:
wait.until(
lambda driver: sort_by_position(driver, sessions) == expected_order,
)
except TimeoutException:
pass # Fall through to the assertion which will fail, don't throw a confusing timeout exception
self.assertEqual(sort_by_position(self.driver, sessions), expected_order, fail_message)
# Start the test here
# set up several WGs in various areas, including no area.
area_acronyms = ['A', 'B', 'C', 'D']
areas = [GroupFactory(type_id='area', acronym=acro) for acro in area_acronyms]
# now create WGs with acronyms that sort differently than by area (g00, g01, g02...)
num = 0
wgs = []
group_acro = lambda n: 'g{:02d}'.format(n)
for _ in range(2):
wgs.append(GroupFactory(acronym=group_acro(num), type_id='wg', parent=None))
num += 1
for area in areas:
wgs.append(GroupFactory(acronym=group_acro(num), type_id='wg', parent=area))
num += 1
# Create an IETF meeting...
meeting = MeetingFactory(type_id='ietf')
# ...and sessions for the groups. Use durations that are in a different order than
# area or name. The wgs list is in ascending acronym order, so use descending durations.
sessions = []
for n, wg in enumerate(wgs[::-1]):
sessions.append(
SessionFactory(
meeting=meeting,
group=wg,
requested_duration=datetime.timedelta(minutes=30 + 5 * n),
status_id='schedw',
add_to_schedule=False,
)
)
# Finally, assign comments to some sessions. Assign every 3rd until we reach the end.
# This should be a different sort than any of the other axes.
for sess in sessions[::3]:
sess.comments = 'special request'
sess.save()
url = self.absreverse('ietf.meeting.views.edit_meeting_schedule', kwargs=dict(num=meeting.number))
self.login('secretary')
self.driver.get(url)
select = self.driver.find_element_by_name('sort_unassigned')
options = {
opt.get_attribute('value'): opt
for opt in select.find_elements_by_tag_name('option')
}
# check sorting by name
options['name'].click()
self.assertEqual(select.get_attribute('value'), 'name')
expected_order = sorted(
sessions,
key=lambda s: (
s.group.acronym,
s.requested_duration,
)
)
wait_for_order(sessions, expected_order, 'Failed to sort by name')
# check sorting by parent
options['parent'].click()
self.assertEqual(select.get_attribute('value'), 'parent')
expected_order = sorted(
sessions,
key=lambda s: (
s.group.parent.acronym if s.group.parent else '',
s.group.acronym,
s.requested_duration,
)
)
wait_for_order(sessions, expected_order, 'Failed to sort by parent')
# check sorting by duration
options['duration'].click()
self.assertEqual(select.get_attribute('value'), 'duration')
expected_order = sorted(
sessions,
key=lambda s: (
s.requested_duration,
s.group.parent.acronym if s.group.parent else '',
s.group.acronym,
)
)
wait_for_order(sessions, expected_order, 'Failed to sort by duration')
# check sorting by comments
options['comments'].click()
self.assertEqual(select.get_attribute('value'), 'comments')
expected_order = sorted(
sessions,
key=lambda s: (
0 if len(s.comments) > 0 else 1,
s.group.parent.acronym if s.group.parent else '',
s.group.acronym,
s.requested_duration,
)
)
wait_for_order(sessions, expected_order, 'Failed to sort by comments')
def test_unassigned_sessions_drop_target_visible_when_empty(self):
"""The drop target for unassigned sessions should not collapse to 0 size

View file

@ -385,11 +385,13 @@ jQuery(document).ready(function () {
}
function extractName(e) {
return e.querySelector(".session-label").innerHTML;
let labelElement = e.querySelector(".session-label");
return labelElement ? labelElement.innerHTML : '';
}
function extractParent(e) {
return e.querySelector(".session-parent").innerHTML;
let parentElement = e.querySelector(".session-parent");
return parentElement ? parentElement.innerHTML : '';
}
function extractDuration(e) {
@ -400,15 +402,13 @@ jQuery(document).ready(function () {
return e.querySelector(".session-info .comments") ? 0 : 1;
}
let keyFunctions = [];
if (sortBy == "name")
keyFunctions = [extractName, extractDuration, extractId];
else if (sortBy == "parent")
keyFunctions = [extractParent, extractName, extractDuration, extractId];
else if (sortBy == "duration")
keyFunctions = [extractDuration, extractParent, extractName, extractId];
else if (sortBy == "comments")
keyFunctions = [extractComments, extractParent, extractName, extractDuration, extractId];
const keyFunctionMap = {
name: [extractName, extractDuration, extractId],
parent: [extractParent, extractName, extractDuration, extractId],
duration: [extractDuration, extractParent, extractName, extractId],
comments: [extractComments, extractParent, extractName, extractDuration, extractId]
};
let keyFunctions = keyFunctionMap[sortBy];
let unassignedSessionsContainer = content.find(".unassigned-sessions .drop-target");