|
24 | 24 | from ietf.person.models import Person |
25 | 25 | from ietf.group.models import Group |
26 | 26 | from ietf.group.factories import GroupFactory |
27 | | -from ietf.meeting.factories import SessionFactory, TimeSlotFactory |
| 27 | +from ietf.meeting.factories import MeetingFactory, SessionFactory, TimeSlotFactory |
28 | 28 | from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting |
29 | 29 | from ietf.meeting.models import (Schedule, SchedTimeSessAssignment, Session, |
30 | 30 | Room, TimeSlot, Constraint, ConstraintName, |
|
39 | 39 | from selenium.webdriver.common.by import By |
40 | 40 | from selenium.webdriver.support.ui import WebDriverWait |
41 | 41 | from selenium.webdriver.support import expected_conditions |
42 | | - from selenium.common.exceptions import NoSuchElementException |
| 42 | + from selenium.common.exceptions import NoSuchElementException, TimeoutException |
43 | 43 |
|
44 | 44 |
|
45 | 45 | @ifSeleniumEnabled |
@@ -225,6 +225,137 @@ def test_edit_meeting_schedule(self): |
225 | 225 |
|
226 | 226 | self.assertTrue(self.driver.find_elements_by_css_selector('#timeslot{} #session{}'.format(slot4.pk, s1.pk))) |
227 | 227 |
|
| 228 | + def test_unassigned_sessions_sort(self): |
| 229 | + """Unassigned session sorting should behave correctly |
| 230 | +
|
| 231 | + Sorting options and list of sort criteria |
| 232 | + name (name, duration, id) |
| 233 | + parent (parent, name, duration, id) |
| 234 | + duration (duration, parent, name, id) |
| 235 | + comments (presence of comments, parent, name, duration, id) |
| 236 | + """ |
| 237 | + # Define helpers |
| 238 | + def sort_by_position(driver, sessions): |
| 239 | + """Helper to sort sessions by the position of their session element in the unscheduled box""" |
| 240 | + def _sort_key(sess): |
| 241 | + elt = driver.find_element_by_id('session{}'.format(sess.pk)) |
| 242 | + return (elt.location['y'], elt.location['x']) |
| 243 | + return sorted(sessions, key=_sort_key) |
| 244 | + |
| 245 | + wait = WebDriverWait(self.driver, 2) |
| 246 | + |
| 247 | + def wait_for_order(sessions, expected_order, fail_message): |
| 248 | + """Helper to wait for sorting to complete""" |
| 249 | + try: |
| 250 | + wait.until( |
| 251 | + lambda driver: sort_by_position(driver, sessions) == expected_order, |
| 252 | + ) |
| 253 | + except TimeoutException: |
| 254 | + pass # Fall through to the assertion which will fail, don't throw a confusing timeout exception |
| 255 | + self.assertEqual(sort_by_position(self.driver, sessions), expected_order, fail_message) |
| 256 | + |
| 257 | + # Start the test here |
| 258 | + # set up several WGs in various areas, including no area. |
| 259 | + area_acronyms = ['A', 'B', 'C', 'D'] |
| 260 | + areas = [GroupFactory(type_id='area', acronym=acro) for acro in area_acronyms] |
| 261 | + |
| 262 | + # now create WGs with acronyms that sort differently than by area (g00, g01, g02...) |
| 263 | + num = 0 |
| 264 | + wgs = [] |
| 265 | + group_acro = lambda n: 'g{:02d}'.format(n) |
| 266 | + for _ in range(2): |
| 267 | + wgs.append(GroupFactory(acronym=group_acro(num), type_id='wg', parent=None)) |
| 268 | + num += 1 |
| 269 | + for area in areas: |
| 270 | + wgs.append(GroupFactory(acronym=group_acro(num), type_id='wg', parent=area)) |
| 271 | + num += 1 |
| 272 | + |
| 273 | + # Create an IETF meeting... |
| 274 | + meeting = MeetingFactory(type_id='ietf') |
| 275 | + |
| 276 | + # ...and sessions for the groups. Use durations that are in a different order than |
| 277 | + # area or name. The wgs list is in ascending acronym order, so use descending durations. |
| 278 | + sessions = [] |
| 279 | + for n, wg in enumerate(wgs[::-1]): |
| 280 | + sessions.append( |
| 281 | + SessionFactory( |
| 282 | + meeting=meeting, |
| 283 | + group=wg, |
| 284 | + requested_duration=datetime.timedelta(minutes=30 + 5 * n), |
| 285 | + status_id='schedw', |
| 286 | + add_to_schedule=False, |
| 287 | + ) |
| 288 | + ) |
| 289 | + |
| 290 | + # Finally, assign comments to some sessions. Assign every 3rd until we reach the end. |
| 291 | + # This should be a different sort than any of the other axes. |
| 292 | + for sess in sessions[::3]: |
| 293 | + sess.comments = 'special request' |
| 294 | + sess.save() |
| 295 | + |
| 296 | + url = self.absreverse('ietf.meeting.views.edit_meeting_schedule', kwargs=dict(num=meeting.number)) |
| 297 | + self.login('secretary') |
| 298 | + self.driver.get(url) |
| 299 | + |
| 300 | + |
| 301 | + select = self.driver.find_element_by_name('sort_unassigned') |
| 302 | + options = { |
| 303 | + opt.get_attribute('value'): opt |
| 304 | + for opt in select.find_elements_by_tag_name('option') |
| 305 | + } |
| 306 | + |
| 307 | + # check sorting by name |
| 308 | + options['name'].click() |
| 309 | + self.assertEqual(select.get_attribute('value'), 'name') |
| 310 | + expected_order = sorted( |
| 311 | + sessions, |
| 312 | + key=lambda s: ( |
| 313 | + s.group.acronym, |
| 314 | + s.requested_duration, |
| 315 | + ) |
| 316 | + ) |
| 317 | + wait_for_order(sessions, expected_order, 'Failed to sort by name') |
| 318 | + |
| 319 | + # check sorting by parent |
| 320 | + options['parent'].click() |
| 321 | + self.assertEqual(select.get_attribute('value'), 'parent') |
| 322 | + expected_order = sorted( |
| 323 | + sessions, |
| 324 | + key=lambda s: ( |
| 325 | + s.group.parent.acronym if s.group.parent else '', |
| 326 | + s.group.acronym, |
| 327 | + s.requested_duration, |
| 328 | + ) |
| 329 | + ) |
| 330 | + wait_for_order(sessions, expected_order, 'Failed to sort by parent') |
| 331 | + |
| 332 | + # check sorting by duration |
| 333 | + options['duration'].click() |
| 334 | + self.assertEqual(select.get_attribute('value'), 'duration') |
| 335 | + expected_order = sorted( |
| 336 | + sessions, |
| 337 | + key=lambda s: ( |
| 338 | + s.requested_duration, |
| 339 | + s.group.parent.acronym if s.group.parent else '', |
| 340 | + s.group.acronym, |
| 341 | + ) |
| 342 | + ) |
| 343 | + wait_for_order(sessions, expected_order, 'Failed to sort by duration') |
| 344 | + |
| 345 | + # check sorting by comments |
| 346 | + options['comments'].click() |
| 347 | + self.assertEqual(select.get_attribute('value'), 'comments') |
| 348 | + expected_order = sorted( |
| 349 | + sessions, |
| 350 | + key=lambda s: ( |
| 351 | + 0 if len(s.comments) > 0 else 1, |
| 352 | + s.group.parent.acronym if s.group.parent else '', |
| 353 | + s.group.acronym, |
| 354 | + s.requested_duration, |
| 355 | + ) |
| 356 | + ) |
| 357 | + wait_for_order(sessions, expected_order, 'Failed to sort by comments') |
| 358 | + |
228 | 359 | def test_unassigned_sessions_drop_target_visible_when_empty(self): |
229 | 360 | """The drop target for unassigned sessions should not collapse to 0 size |
230 | 361 |
|
|
0 commit comments