Merged in [19164] from jennifer@painless-security.com:
Fix broken meeting materials button on upcoming meetings page. Fixes #3278.
- Legacy-Id: 19170
Note: SVN reference [19164] has been migrated to Git commit 044293b4a9
This commit is contained in:
commit
37a0dc5879
|
@ -6,7 +6,8 @@ import debug # pyflakes:ignore
|
|||
from ietf.doc.factories import WgDraftFactory, DocumentAuthorFactory
|
||||
from ietf.person.factories import PersonFactory
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.jstest import IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled
|
||||
from ietf.utils.jstest import ( IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled,
|
||||
presence_of_element_child_by_css_selector )
|
||||
|
||||
if selenium_enabled():
|
||||
from selenium.webdriver.common.by import By
|
||||
|
@ -14,19 +15,6 @@ if selenium_enabled():
|
|||
from selenium.webdriver.support import expected_conditions
|
||||
|
||||
|
||||
class presence_of_element_child_by_css_selector:
|
||||
"""Wait for presence of a child of a WebElement matching a CSS selector
|
||||
|
||||
This is a condition class for use with WebDriverWait.
|
||||
"""
|
||||
def __init__(self, element, child_selector):
|
||||
self.element = element
|
||||
self.child_selector = child_selector
|
||||
|
||||
def __call__(self, driver):
|
||||
child = self.element.find_element_by_css_selector(self.child_selector)
|
||||
return child if child is not None else False
|
||||
|
||||
@ifSeleniumEnabled
|
||||
class EditAuthorsTests(IetfSeleniumTestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -31,7 +31,8 @@ from ietf.meeting.models import (Schedule, SchedTimeSessAssignment, Session,
|
|||
Meeting, SchedulingEvent, SessionStatusName)
|
||||
from ietf.meeting.utils import add_event_info_to_session_qs
|
||||
from ietf.utils.test_utils import assert_ical_response_is_valid
|
||||
from ietf.utils.jstest import IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled
|
||||
from ietf.utils.jstest import ( IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled,
|
||||
presence_of_element_child_by_css_selector )
|
||||
from ietf import settings
|
||||
|
||||
if selenium_enabled():
|
||||
|
@ -1572,6 +1573,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
sg_sess.save()
|
||||
sg_slot.save()
|
||||
|
||||
self.wait = WebDriverWait(self.driver, 2)
|
||||
|
||||
def tearDown(self):
|
||||
settings.AGENDA_PATH = self.saved_agenda_path
|
||||
|
@ -1647,7 +1649,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
def assert_upcoming_meeting_calendar(self, visible_meetings=None):
|
||||
"""Assert that correct items are sent to the calendar"""
|
||||
def advance_month():
|
||||
button = WebDriverWait(self.driver, 2).until(
|
||||
button = self.wait.until(
|
||||
expected_conditions.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, 'div#calendar button.fc-next-button')))
|
||||
self.scroll_to_element(button)
|
||||
|
@ -1850,8 +1852,6 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
self.do_upcoming_view_filter_test('?show=mars , ames &hide= ames', meetings)
|
||||
|
||||
def test_upcoming_view_time_zone_selection(self):
|
||||
wait = WebDriverWait(self.driver, 2)
|
||||
|
||||
def _assert_interim_tz_correct(sessions, tz):
|
||||
zone = pytz.timezone(tz)
|
||||
for session in sessions:
|
||||
|
@ -1897,7 +1897,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
# wait for the select box to be updated - look for an arbitrary time zone to be in
|
||||
# its options list to detect this
|
||||
arbitrary_tz = 'America/Halifax'
|
||||
arbitrary_tz_opt = wait.until(
|
||||
arbitrary_tz_opt = self.wait.until(
|
||||
expected_conditions.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, '#timezone-select > option[value="%s"]' % arbitrary_tz)
|
||||
)
|
||||
|
@ -1923,7 +1923,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
|
||||
# click 'utc' button
|
||||
utc_tz_link.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
|
||||
self.wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
|
@ -1935,7 +1935,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
|
||||
# click back to 'local'
|
||||
local_tz_link.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
|
||||
self.wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
|
||||
self.assertTrue(local_tz_opt.is_selected())
|
||||
self.assertTrue(local_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
|
@ -1947,7 +1947,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
|
||||
# Now select a different item from the select input
|
||||
arbitrary_tz_opt.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
|
||||
self.wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_bottom_opt.is_selected())
|
||||
self.assertTrue(arbitrary_tz_opt.is_selected())
|
||||
|
@ -1960,7 +1960,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
# Now repeat those tests using the widgets at the bottom of the page
|
||||
# click 'utc' button
|
||||
utc_tz_bottom_link.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
|
||||
self.wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
|
@ -1972,7 +1972,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
|
||||
# click back to 'local'
|
||||
local_tz_bottom_link.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
|
||||
self.wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
|
||||
self.assertTrue(local_tz_opt.is_selected())
|
||||
self.assertTrue(local_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
|
@ -1984,7 +1984,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
|
||||
# Now select a different item from the select input
|
||||
arbitrary_tz_bottom_opt.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
|
||||
self.wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_bottom_opt.is_selected())
|
||||
self.assertTrue(arbitrary_tz_opt.is_selected())
|
||||
|
@ -1994,6 +1994,52 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
_assert_interim_tz_correct(sessions, arbitrary_tz)
|
||||
_assert_ietf_tz_correct(ietf_meetings, arbitrary_tz)
|
||||
|
||||
def test_upcoming_materials_modal(self):
|
||||
"""Test opening and closing a materals modal
|
||||
|
||||
This does not test dynamic reloading of the meeting materials - it relies on the main
|
||||
agenda page testing that. If the materials modal handling diverges between here and
|
||||
there, this should be updated to include that test.
|
||||
"""
|
||||
url = self.absreverse('ietf.meeting.views.upcoming')
|
||||
self.driver.get(url)
|
||||
|
||||
interim = self.displayed_interims(['mars'])[0]
|
||||
session = interim.session_set.first()
|
||||
assignment = session.official_timeslotassignment()
|
||||
slug = assignment.slug()
|
||||
|
||||
# modal should start hidden
|
||||
modal_div = self.driver.find_element_by_css_selector('div#modal-%s' % slug)
|
||||
self.assertFalse(modal_div.is_displayed())
|
||||
|
||||
# Click the 'materials' button
|
||||
open_modal_button = self.wait.until(
|
||||
expected_conditions.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, '[data-target="#modal-%s"]' % slug)
|
||||
),
|
||||
'Modal open button not found or not clickable',
|
||||
)
|
||||
open_modal_button.click()
|
||||
self.wait.until(
|
||||
expected_conditions.visibility_of(modal_div),
|
||||
'Modal did not become visible after clicking open button',
|
||||
)
|
||||
|
||||
# Now close the modal
|
||||
close_modal_button = self.wait.until(
|
||||
presence_of_element_child_by_css_selector(
|
||||
modal_div,
|
||||
'.modal-footer button[data-dismiss="modal"]',
|
||||
),
|
||||
'Modal close button not found or not clickable',
|
||||
)
|
||||
close_modal_button.click()
|
||||
self.wait.until(
|
||||
expected_conditions.invisibility_of_element(modal_div),
|
||||
'Modal was not hidden after clicking close button',
|
||||
)
|
||||
|
||||
|
||||
# The following are useful debugging tools
|
||||
|
||||
|
|
84
ietf/static/ietf/js/agenda/agenda_materials.js
Normal file
84
ietf/static/ietf/js/agenda/agenda_materials.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright The IETF Trust 2021, All Rights Reserved
|
||||
|
||||
/*
|
||||
Javascript support for the materials modal rendered by session_agenda_include.html
|
||||
|
||||
Requires jquery be loaded
|
||||
*/
|
||||
|
||||
var agenda_materials; // public interface
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
/**
|
||||
* Retrieve and display materials for a session
|
||||
*
|
||||
* If output_elt exists and has a "data-src" attribute, retrieves the document
|
||||
* from that URL and displays under output_elt. Handles text/plain, text/markdown,
|
||||
* and text/html.
|
||||
*
|
||||
* @param output_elt Element, probably a div, to hold the output
|
||||
*/
|
||||
function retrieve_session_materials(output_elt) {
|
||||
if (!output_elt) {return;}
|
||||
output_elt = $(output_elt);
|
||||
var data_src = output_elt.attr("data-src");
|
||||
if (!data_src) {
|
||||
output_elt.html("<p>Error: missing data-src attribute</p>");
|
||||
} else {
|
||||
output_elt.html("<p>Loading " + data_src + "...</p>");
|
||||
var outer_xhr = $.get(data_src)
|
||||
outer_xhr.done(function(data, status, xhr) {
|
||||
var t = xhr.getResponseHeader("content-type");
|
||||
if (!t) {
|
||||
data = "<p>Error retrieving " + data_src
|
||||
+ ": Missing content-type in response header</p>";
|
||||
} else if (t.indexOf("text/plain") > -1) {
|
||||
data = "<pre class='agenda'>" + data + "</pre>";
|
||||
} else if (t.indexOf("text/markdown") > -1) {
|
||||
data = "<pre class='agenda'>" + data + "</pre>";
|
||||
} else if(t.indexOf("text/html") > -1) {
|
||||
// nothing to do here
|
||||
} else {
|
||||
data = "<p>Unknown type: " + xhr.getResponseHeader("content-type") + "</p>";
|
||||
}
|
||||
output_elt.html(data);
|
||||
}).fail(function() {
|
||||
output_elt.html("<p>Error retrieving " + data_src
|
||||
+ ": (" + outer_xhr.status.toString() + ") "
|
||||
+ outer_xhr.statusText + "</p>");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve contents of a session materials modal
|
||||
*
|
||||
* Expects output_elt to exist and have a "data-src" attribute. Retrieves the
|
||||
* contents of that URL, then attempts to populate the .agenda-frame and
|
||||
* .minutes-frame elements.
|
||||
*
|
||||
* @param output_elt Element, probably a div, to hold the output
|
||||
*/
|
||||
function retrieve_session_modal(output_elt) {
|
||||
if (!output_elt) {return;}
|
||||
output_elt = $(output_elt);
|
||||
var data_src = output_elt.attr("data-src");
|
||||
if (!data_src) {
|
||||
output_elt.html("<p>Error: missing data-src attribute</p>");
|
||||
} else {
|
||||
output_elt.html("<p>Loading...</p>");
|
||||
$.get(data_src).done(function(data) {
|
||||
output_elt.html(data);
|
||||
retrieve_session_materials(output_elt.find(".agenda-frame"));
|
||||
retrieve_session_materials(output_elt.find(".minutes-frame"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(".modal").on("show.bs.modal", function () {
|
||||
retrieve_session_modal($(this).find(".session-materials"));
|
||||
});
|
||||
})
|
||||
})();
|
|
@ -469,80 +469,11 @@
|
|||
update_ical_links(filter_params)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve and display materials for a session
|
||||
*
|
||||
* If output_elt exists and has a "data-src" attribute, retrieves the document
|
||||
* from that URL and displays under output_elt. Handles text/plain, text/markdown,
|
||||
* and text/html.
|
||||
*
|
||||
* @param output_elt Element, probably a div, to hold the output
|
||||
*/
|
||||
function retrieve_session_materials(output_elt) {
|
||||
if (!output_elt) {return;}
|
||||
output_elt = $(output_elt);
|
||||
var data_src = output_elt.attr("data-src");
|
||||
if (!data_src) {
|
||||
output_elt.html("<p>Error: missing data-src attribute</p>");
|
||||
} else {
|
||||
output_elt.html("<p>Loading " + data_src + "...</p>");
|
||||
outer_xhr = $.get(data_src)
|
||||
outer_xhr.done(function(data, status, xhr) {
|
||||
var t = xhr.getResponseHeader("content-type");
|
||||
if (!t) {
|
||||
data = "<p>Error retrieving " + data_src
|
||||
+ ": Missing content-type in response header</p>";
|
||||
} else if (t.indexOf("text/plain") > -1) {
|
||||
data = "<pre class='agenda'>" + data + "</pre>";
|
||||
} else if (t.indexOf("text/markdown") > -1) {
|
||||
data = "<pre class='agenda'>" + data + "</pre>";
|
||||
} else if(t.indexOf("text/html") > -1) {
|
||||
// nothing to do here
|
||||
} else {
|
||||
data = "<p>Unknown type: " + xhr.getResponseHeader("content-type") + "</p>";
|
||||
}
|
||||
output_elt.html(data);
|
||||
}).fail(function() {
|
||||
output_elt.html("<p>Error retrieving " + data_src
|
||||
+ ": (" + outer_xhr.status.toString() + ") "
|
||||
+ outer_xhr.statusText + "</p>");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve contents of a session materials modal
|
||||
*
|
||||
* Expects output_elt to exist and have a "data-src" attribute. Retrieves the
|
||||
* contents of that URL, then attempts to populate the .agenda-frame and
|
||||
* .minutes-frame elements.
|
||||
*
|
||||
* @param output_elt Element, probably a div, to hold the output
|
||||
*/
|
||||
function retrieve_session_modal(output_elt) {
|
||||
if (!output_elt) {return;}
|
||||
output_elt = $(output_elt);
|
||||
var data_src = output_elt.attr("data-src");
|
||||
if (!data_src) {
|
||||
output_elt.html("<p>Error: missing data-src attribute</p>");
|
||||
} else {
|
||||
output_elt.html("<p>Loading...</p>");
|
||||
$.get(data_src).done(function(data) {
|
||||
output_elt.html(data);
|
||||
retrieve_session_materials(output_elt.find(".agenda-frame"));
|
||||
retrieve_session_materials(output_elt.find(".minutes-frame"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(".modal").on("show.bs.modal", function () {
|
||||
retrieve_session_modal($(this).find(".session-materials"));
|
||||
});
|
||||
|
||||
</script>
|
||||
<script src="{% static 'moment/min/moment.min.js' %}"></script>
|
||||
<script src="{% static 'moment-timezone/builds/moment-timezone-with-data-10-year-range.min.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/agenda/timezone.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/agenda/agenda_materials.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/agenda/agenda_timezone.js' %}"></script>
|
||||
<script>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
{% origin %}
|
||||
{% with item=session.official_timeslotassignment acronym=session.historic_group.acronym %}
|
||||
{% if session.agenda and show_agenda %}
|
||||
{% include "meeting/session_agenda_include.html" %}
|
||||
{# Note: if called with show_agenda=True, calling template must load agenda_materials.js, needed by session_agenda_include.html #}
|
||||
{% include "meeting/session_agenda_include.html" with slug=item.slug session=session timeslot=item.timeslot only %}
|
||||
<!-- agenda pop-up button -->
|
||||
<a class="" data-toggle="modal" data-target="#modal-{{item.slug}}" title="Show meeting materials"><span class="fa fa-fw fa-arrows-alt"></span></a>
|
||||
<!-- materials tar file -->
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{# expects slug, session, and timeslot to be in the context #}
|
||||
{# expects slug, session, and timeslot to be in the context. Calling template must load the agenda_materials.js script. #}
|
||||
{% load origin %}{% origin %}
|
||||
{% load static %}
|
||||
{% load textfilters %}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<span id="session-buttons-{{session.pk}}" class="text-nowrap">
|
||||
{% with acronym=session.historic_group.acronym %}
|
||||
{% if session.agenda and show_agenda %}
|
||||
{# Note: if called with show_agenda=True, calling template must load agenda_materials.js, needed by session_agenda_include.html #}
|
||||
{% include "meeting/session_agenda_include.html" with slug=slug session=session timeslot=timeslot only %}
|
||||
<!-- agenda pop-up button -->
|
||||
<a class="" data-toggle="modal" data-target="#modal-{{slug}}" title="Show meeting materials"><span class="fa fa-fw fa-arrows-alt"></span></a>
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
{% if session.name %} : {{ session.name }}{% endif %}
|
||||
{% if not session.cancelled %}
|
||||
<span class="regular pull-right">
|
||||
{# see note in the included templates re: show_agenda parameter and required JS import #}
|
||||
{% if meeting.type.slug == 'interim' %}
|
||||
{% include "meeting/interim_session_buttons.html" with show_agenda=False show_empty=False %}
|
||||
{% else %}
|
||||
{% with schedule=meeting.schedule %}
|
||||
{% include "meeting/session_buttons_include.html" %}
|
||||
{% include "meeting/session_buttons_include.html" with show_agenda=False %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</span>
|
||||
|
|
|
@ -180,6 +180,7 @@
|
|||
<script src="{% static 'moment/min/moment.min.js' %}"></script>
|
||||
<script src="{% static 'moment-timezone/builds/moment-timezone-with-data-10-year-range.min.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/agenda/agenda_filter.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/agenda/agenda_materials.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/agenda/timezone.js' %}"></script>
|
||||
<script>
|
||||
// List of all events with meta-info needed for filtering
|
||||
|
@ -338,37 +339,6 @@
|
|||
agenda_filter.set_update_callback(update_view);
|
||||
agenda_filter.enable();
|
||||
|
||||
$(".modal").on("show.bs.modal", function () {
|
||||
var i = $(this).find(".frame");
|
||||
if ($(i).data("src")) {
|
||||
$.get($(i).data("src"), function (data, status, xhr) {
|
||||
var t = xhr.getResponseHeader("content-type");
|
||||
if (t.indexOf("text/plain") > -1) {
|
||||
data = "<pre class='agenda'>" + data + "</pre>";
|
||||
} else if(t.indexOf("text/html") > -1) {
|
||||
// nothing to do here
|
||||
} else {
|
||||
data = "<p>Unknown type: " + xhr.getResponseHeader("content-type") + "</p>";
|
||||
}
|
||||
$(i).html(data);
|
||||
});
|
||||
}
|
||||
var j = $(this).find(".frame2");
|
||||
if ($(j).data("src")) {
|
||||
$.get($(j).data("src"), function (data, status, xhr) {
|
||||
var t = xhr.getResponseHeader("content-type");
|
||||
if (t.indexOf("text/plain") > -1) {
|
||||
data = "<pre class='agenda'>" + data + "</pre>";
|
||||
} else if(t.indexOf("text/html") > -1) {
|
||||
// nothing to do here
|
||||
} else {
|
||||
data = "<p>Unknown type: " + xhr.getResponseHeader("content-type") + "</p>";
|
||||
}
|
||||
$(j).html(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function format_session_time(session_elt, tz) {
|
||||
var start = moment.utc($(session_elt).attr('data-start-utc'));
|
||||
var end = moment.utc($(session_elt).attr('data-end-utc'));
|
||||
|
|
|
@ -78,3 +78,16 @@ class IetfSeleniumTestCase(IetfLiveServerTestCase):
|
|||
actions = ActionChains(self.driver)
|
||||
actions.move_to_element(element).perform()
|
||||
|
||||
|
||||
class presence_of_element_child_by_css_selector:
|
||||
"""Wait for presence of a child of a WebElement matching a CSS selector
|
||||
|
||||
This is a condition class for use with WebDriverWait.
|
||||
"""
|
||||
def __init__(self, element, child_selector):
|
||||
self.element = element
|
||||
self.child_selector = child_selector
|
||||
|
||||
def __call__(self, driver):
|
||||
child = self.element.find_element_by_css_selector(self.child_selector)
|
||||
return child if child is not None else False
|
||||
|
|
Loading…
Reference in a new issue