Skip to content

Commit 044293b

Browse files
Fix broken meeting materials button on upcoming meetings page. Fixes ietf-tools#3278. Commit ready for merge.
- Legacy-Id: 19164
1 parent 058f007 commit 044293b

10 files changed

Lines changed: 164 additions & 129 deletions

File tree

ietf/doc/tests_js.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,15 @@
66
from ietf.doc.factories import WgDraftFactory, DocumentAuthorFactory
77
from ietf.person.factories import PersonFactory
88
from ietf.person.models import Person
9-
from ietf.utils.jstest import IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled
9+
from ietf.utils.jstest import ( IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled,
10+
presence_of_element_child_by_css_selector )
1011

1112
if selenium_enabled():
1213
from selenium.webdriver.common.by import By
1314
from selenium.webdriver.support.ui import WebDriverWait
1415
from selenium.webdriver.support import expected_conditions
1516

1617

17-
class presence_of_element_child_by_css_selector:
18-
"""Wait for presence of a child of a WebElement matching a CSS selector
19-
20-
This is a condition class for use with WebDriverWait.
21-
"""
22-
def __init__(self, element, child_selector):
23-
self.element = element
24-
self.child_selector = child_selector
25-
26-
def __call__(self, driver):
27-
child = self.element.find_element_by_css_selector(self.child_selector)
28-
return child if child is not None else False
29-
3018
@ifSeleniumEnabled
3119
class EditAuthorsTests(IetfSeleniumTestCase):
3220
def setUp(self):

ietf/meeting/tests_js.py

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
Meeting, SchedulingEvent, SessionStatusName)
3232
from ietf.meeting.utils import add_event_info_to_session_qs
3333
from ietf.utils.test_utils import assert_ical_response_is_valid
34-
from ietf.utils.jstest import IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled
34+
from ietf.utils.jstest import ( IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled,
35+
presence_of_element_child_by_css_selector )
3536
from ietf import settings
3637

3738
if selenium_enabled():
@@ -1572,6 +1573,7 @@ def setUp(self):
15721573
sg_sess.save()
15731574
sg_slot.save()
15741575

1576+
self.wait = WebDriverWait(self.driver, 2)
15751577

15761578
def tearDown(self):
15771579
settings.AGENDA_PATH = self.saved_agenda_path
@@ -1647,7 +1649,7 @@ def assert_upcoming_meeting_visibility(self, visible_meetings=None):
16471649
def assert_upcoming_meeting_calendar(self, visible_meetings=None):
16481650
"""Assert that correct items are sent to the calendar"""
16491651
def advance_month():
1650-
button = WebDriverWait(self.driver, 2).until(
1652+
button = self.wait.until(
16511653
expected_conditions.element_to_be_clickable(
16521654
(By.CSS_SELECTOR, 'div#calendar button.fc-next-button')))
16531655
self.scroll_to_element(button)
@@ -1850,8 +1852,6 @@ def test_upcoming_view_filter_whitespace(self):
18501852
self.do_upcoming_view_filter_test('?show=mars , ames &hide= ames', meetings)
18511853

18521854
def test_upcoming_view_time_zone_selection(self):
1853-
wait = WebDriverWait(self.driver, 2)
1854-
18551855
def _assert_interim_tz_correct(sessions, tz):
18561856
zone = pytz.timezone(tz)
18571857
for session in sessions:
@@ -1897,7 +1897,7 @@ def _assert_ietf_tz_correct(meetings, tz):
18971897
# wait for the select box to be updated - look for an arbitrary time zone to be in
18981898
# its options list to detect this
18991899
arbitrary_tz = 'America/Halifax'
1900-
arbitrary_tz_opt = wait.until(
1900+
arbitrary_tz_opt = self.wait.until(
19011901
expected_conditions.presence_of_element_located(
19021902
(By.CSS_SELECTOR, '#timezone-select > option[value="%s"]' % arbitrary_tz)
19031903
)
@@ -1923,7 +1923,7 @@ def _assert_ietf_tz_correct(meetings, tz):
19231923

19241924
# click 'utc' button
19251925
utc_tz_link.click()
1926-
wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
1926+
self.wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
19271927
self.assertFalse(local_tz_opt.is_selected())
19281928
self.assertFalse(local_tz_bottom_opt.is_selected())
19291929
self.assertFalse(arbitrary_tz_opt.is_selected())
@@ -1935,7 +1935,7 @@ def _assert_ietf_tz_correct(meetings, tz):
19351935

19361936
# click back to 'local'
19371937
local_tz_link.click()
1938-
wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
1938+
self.wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
19391939
self.assertTrue(local_tz_opt.is_selected())
19401940
self.assertTrue(local_tz_bottom_opt.is_selected())
19411941
self.assertFalse(arbitrary_tz_opt.is_selected())
@@ -1947,7 +1947,7 @@ def _assert_ietf_tz_correct(meetings, tz):
19471947

19481948
# Now select a different item from the select input
19491949
arbitrary_tz_opt.click()
1950-
wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
1950+
self.wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
19511951
self.assertFalse(local_tz_opt.is_selected())
19521952
self.assertFalse(local_tz_bottom_opt.is_selected())
19531953
self.assertTrue(arbitrary_tz_opt.is_selected())
@@ -1960,7 +1960,7 @@ def _assert_ietf_tz_correct(meetings, tz):
19601960
# Now repeat those tests using the widgets at the bottom of the page
19611961
# click 'utc' button
19621962
utc_tz_bottom_link.click()
1963-
wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
1963+
self.wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
19641964
self.assertFalse(local_tz_opt.is_selected())
19651965
self.assertFalse(local_tz_bottom_opt.is_selected())
19661966
self.assertFalse(arbitrary_tz_opt.is_selected())
@@ -1972,7 +1972,7 @@ def _assert_ietf_tz_correct(meetings, tz):
19721972

19731973
# click back to 'local'
19741974
local_tz_bottom_link.click()
1975-
wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
1975+
self.wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
19761976
self.assertTrue(local_tz_opt.is_selected())
19771977
self.assertTrue(local_tz_bottom_opt.is_selected())
19781978
self.assertFalse(arbitrary_tz_opt.is_selected())
@@ -1984,7 +1984,7 @@ def _assert_ietf_tz_correct(meetings, tz):
19841984

19851985
# Now select a different item from the select input
19861986
arbitrary_tz_bottom_opt.click()
1987-
wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
1987+
self.wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
19881988
self.assertFalse(local_tz_opt.is_selected())
19891989
self.assertFalse(local_tz_bottom_opt.is_selected())
19901990
self.assertTrue(arbitrary_tz_opt.is_selected())
@@ -1994,6 +1994,52 @@ def _assert_ietf_tz_correct(meetings, tz):
19941994
_assert_interim_tz_correct(sessions, arbitrary_tz)
19951995
_assert_ietf_tz_correct(ietf_meetings, arbitrary_tz)
19961996

1997+
def test_upcoming_materials_modal(self):
1998+
"""Test opening and closing a materals modal
1999+
2000+
This does not test dynamic reloading of the meeting materials - it relies on the main
2001+
agenda page testing that. If the materials modal handling diverges between here and
2002+
there, this should be updated to include that test.
2003+
"""
2004+
url = self.absreverse('ietf.meeting.views.upcoming')
2005+
self.driver.get(url)
2006+
2007+
interim = self.displayed_interims(['mars'])[0]
2008+
session = interim.session_set.first()
2009+
assignment = session.official_timeslotassignment()
2010+
slug = assignment.slug()
2011+
2012+
# modal should start hidden
2013+
modal_div = self.driver.find_element_by_css_selector('div#modal-%s' % slug)
2014+
self.assertFalse(modal_div.is_displayed())
2015+
2016+
# Click the 'materials' button
2017+
open_modal_button = self.wait.until(
2018+
expected_conditions.element_to_be_clickable(
2019+
(By.CSS_SELECTOR, '[data-target="#modal-%s"]' % slug)
2020+
),
2021+
'Modal open button not found or not clickable',
2022+
)
2023+
open_modal_button.click()
2024+
self.wait.until(
2025+
expected_conditions.visibility_of(modal_div),
2026+
'Modal did not become visible after clicking open button',
2027+
)
2028+
2029+
# Now close the modal
2030+
close_modal_button = self.wait.until(
2031+
presence_of_element_child_by_css_selector(
2032+
modal_div,
2033+
'.modal-footer button[data-dismiss="modal"]',
2034+
),
2035+
'Modal close button not found or not clickable',
2036+
)
2037+
close_modal_button.click()
2038+
self.wait.until(
2039+
expected_conditions.invisibility_of_element(modal_div),
2040+
'Modal was not hidden after clicking close button',
2041+
)
2042+
19972043

19982044
# The following are useful debugging tools
19992045

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright The IETF Trust 2021, All Rights Reserved
2+
3+
/*
4+
Javascript support for the materials modal rendered by session_agenda_include.html
5+
6+
Requires jquery be loaded
7+
*/
8+
9+
var agenda_materials; // public interface
10+
11+
(function() {
12+
'use strict';
13+
/**
14+
* Retrieve and display materials for a session
15+
*
16+
* If output_elt exists and has a "data-src" attribute, retrieves the document
17+
* from that URL and displays under output_elt. Handles text/plain, text/markdown,
18+
* and text/html.
19+
*
20+
* @param output_elt Element, probably a div, to hold the output
21+
*/
22+
function retrieve_session_materials(output_elt) {
23+
if (!output_elt) {return;}
24+
output_elt = $(output_elt);
25+
var data_src = output_elt.attr("data-src");
26+
if (!data_src) {
27+
output_elt.html("<p>Error: missing data-src attribute</p>");
28+
} else {
29+
output_elt.html("<p>Loading " + data_src + "...</p>");
30+
var outer_xhr = $.get(data_src)
31+
outer_xhr.done(function(data, status, xhr) {
32+
var t = xhr.getResponseHeader("content-type");
33+
if (!t) {
34+
data = "<p>Error retrieving " + data_src
35+
+ ": Missing content-type in response header</p>";
36+
} else if (t.indexOf("text/plain") > -1) {
37+
data = "<pre class='agenda'>" + data + "</pre>";
38+
} else if (t.indexOf("text/markdown") > -1) {
39+
data = "<pre class='agenda'>" + data + "</pre>";
40+
} else if(t.indexOf("text/html") > -1) {
41+
// nothing to do here
42+
} else {
43+
data = "<p>Unknown type: " + xhr.getResponseHeader("content-type") + "</p>";
44+
}
45+
output_elt.html(data);
46+
}).fail(function() {
47+
output_elt.html("<p>Error retrieving " + data_src
48+
+ ": (" + outer_xhr.status.toString() + ") "
49+
+ outer_xhr.statusText + "</p>");
50+
})
51+
}
52+
}
53+
54+
/**
55+
* Retrieve contents of a session materials modal
56+
*
57+
* Expects output_elt to exist and have a "data-src" attribute. Retrieves the
58+
* contents of that URL, then attempts to populate the .agenda-frame and
59+
* .minutes-frame elements.
60+
*
61+
* @param output_elt Element, probably a div, to hold the output
62+
*/
63+
function retrieve_session_modal(output_elt) {
64+
if (!output_elt) {return;}
65+
output_elt = $(output_elt);
66+
var data_src = output_elt.attr("data-src");
67+
if (!data_src) {
68+
output_elt.html("<p>Error: missing data-src attribute</p>");
69+
} else {
70+
output_elt.html("<p>Loading...</p>");
71+
$.get(data_src).done(function(data) {
72+
output_elt.html(data);
73+
retrieve_session_materials(output_elt.find(".agenda-frame"));
74+
retrieve_session_materials(output_elt.find(".minutes-frame"));
75+
});
76+
}
77+
}
78+
79+
$(document).ready(function() {
80+
$(".modal").on("show.bs.modal", function () {
81+
retrieve_session_modal($(this).find(".session-materials"));
82+
});
83+
})
84+
})();

ietf/templates/meeting/agenda.html

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -469,80 +469,11 @@ <h2>
469469
update_ical_links(filter_params)
470470
}
471471

472-
/**
473-
* Retrieve and display materials for a session
474-
*
475-
* If output_elt exists and has a "data-src" attribute, retrieves the document
476-
* from that URL and displays under output_elt. Handles text/plain, text/markdown,
477-
* and text/html.
478-
*
479-
* @param output_elt Element, probably a div, to hold the output
480-
*/
481-
function retrieve_session_materials(output_elt) {
482-
if (!output_elt) {return;}
483-
output_elt = $(output_elt);
484-
var data_src = output_elt.attr("data-src");
485-
if (!data_src) {
486-
output_elt.html("<p>Error: missing data-src attribute</p>");
487-
} else {
488-
output_elt.html("<p>Loading " + data_src + "...</p>");
489-
outer_xhr = $.get(data_src)
490-
outer_xhr.done(function(data, status, xhr) {
491-
var t = xhr.getResponseHeader("content-type");
492-
if (!t) {
493-
data = "<p>Error retrieving " + data_src
494-
+ ": Missing content-type in response header</p>";
495-
} else if (t.indexOf("text/plain") > -1) {
496-
data = "<pre class='agenda'>" + data + "</pre>";
497-
} else if (t.indexOf("text/markdown") > -1) {
498-
data = "<pre class='agenda'>" + data + "</pre>";
499-
} else if(t.indexOf("text/html") > -1) {
500-
// nothing to do here
501-
} else {
502-
data = "<p>Unknown type: " + xhr.getResponseHeader("content-type") + "</p>";
503-
}
504-
output_elt.html(data);
505-
}).fail(function() {
506-
output_elt.html("<p>Error retrieving " + data_src
507-
+ ": (" + outer_xhr.status.toString() + ") "
508-
+ outer_xhr.statusText + "</p>");
509-
})
510-
}
511-
}
512-
513-
/**
514-
* Retrieve contents of a session materials modal
515-
*
516-
* Expects output_elt to exist and have a "data-src" attribute. Retrieves the
517-
* contents of that URL, then attempts to populate the .agenda-frame and
518-
* .minutes-frame elements.
519-
*
520-
* @param output_elt Element, probably a div, to hold the output
521-
*/
522-
function retrieve_session_modal(output_elt) {
523-
if (!output_elt) {return;}
524-
output_elt = $(output_elt);
525-
var data_src = output_elt.attr("data-src");
526-
if (!data_src) {
527-
output_elt.html("<p>Error: missing data-src attribute</p>");
528-
} else {
529-
output_elt.html("<p>Loading...</p>");
530-
$.get(data_src).done(function(data) {
531-
output_elt.html(data);
532-
retrieve_session_materials(output_elt.find(".agenda-frame"));
533-
retrieve_session_materials(output_elt.find(".minutes-frame"));
534-
});
535-
}
536-
}
537-
538-
$(".modal").on("show.bs.modal", function () {
539-
retrieve_session_modal($(this).find(".session-materials"));
540-
});
541-
542472
</script>
543473
<script src="{% static 'moment/min/moment.min.js' %}"></script>
544474
<script src="{% static 'moment-timezone/builds/moment-timezone-with-data-10-year-range.min.js' %}"></script>
545475
<script src="{% static 'ietf/js/agenda/timezone.js' %}"></script>
476+
<script src="{% static 'ietf/js/agenda/agenda_materials.js' %}"></script>
546477
<script src="{% static 'ietf/js/agenda/agenda_timezone.js' %}"></script>
547478
<script>
548479

ietf/templates/meeting/interim_session_buttons.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
{% origin %}
66
{% with item=session.official_timeslotassignment acronym=session.historic_group.acronym %}
77
{% if session.agenda and show_agenda %}
8-
{% include "meeting/session_agenda_include.html" %}
8+
{# Note: if called with show_agenda=True, calling template must load agenda_materials.js, needed by session_agenda_include.html #}
9+
{% include "meeting/session_agenda_include.html" with slug=item.slug session=session timeslot=item.timeslot only %}
910
<!-- agenda pop-up button -->
1011
<a class="" data-toggle="modal" data-target="#modal-{{item.slug}}" title="Show meeting materials"><span class="fa fa-fw fa-arrows-alt"></span></a>
1112
<!-- materials tar file -->

ietf/templates/meeting/session_agenda_include.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{# Copyright The IETF Trust 2015, All Rights Reserved #}
2-
{# expects slug, session, and timeslot to be in the context #}
2+
{# expects slug, session, and timeslot to be in the context. Calling template must load the agenda_materials.js script. #}
33
{% load origin %}{% origin %}
44
{% load static %}
55
{% load textfilters %}

ietf/templates/meeting/session_buttons_include.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<span id="session-buttons-{{session.pk}}" class="text-nowrap">
99
{% with acronym=session.historic_group.acronym %}
1010
{% if session.agenda and show_agenda %}
11+
{# Note: if called with show_agenda=True, calling template must load agenda_materials.js, needed by session_agenda_include.html #}
1112
{% include "meeting/session_agenda_include.html" with slug=slug session=session timeslot=timeslot only %}
1213
<!-- agenda pop-up button -->
1314
<a class="" data-toggle="modal" data-target="#modal-{{slug}}" title="Show meeting materials"><span class="fa fa-fw fa-arrows-alt"></span></a>

ietf/templates/meeting/session_details_panel.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ <h2 class="anchor-target" id="session_{{session.pk}}">{% if sessions|length > 1
88
{% if session.name %} : {{ session.name }}{% endif %}
99
{% if not session.cancelled %}
1010
<span class="regular pull-right">
11+
{# see note in the included templates re: show_agenda parameter and required JS import #}
1112
{% if meeting.type.slug == 'interim' %}
1213
{% include "meeting/interim_session_buttons.html" with show_agenda=False show_empty=False %}
1314
{% else %}
1415
{% with schedule=meeting.schedule %}
15-
{% include "meeting/session_buttons_include.html" %}
16+
{% include "meeting/session_buttons_include.html" with show_agenda=False %}
1617
{% endwith %}
1718
{% endif %}
1819
</span>

0 commit comments

Comments
 (0)