Fix broken dashboard tables

Due to this patch: https://review.openstack.org/#/c/246625/, Horizon
changes the table styles. This broke murano-dashboard tables.

This patch fixes the following:
1 Fix package, images, categories, environments table looks broken
2 Fix import package descriptions displayed
3 Removes unicode symbols from README to unlock py34 gate
4 Comment out the 'multi_select=false' to pass the ci test
5 Dixed broken tests

Co-Authored-By: Kirill Zaitsev <k.zaitsev@me.com>

Change-Id: I802aaa89021b5f20805a498ad2a17ba73c076bed
Closes-bug: #1552702
Closes-bug: #1550822
Closes-bug: #1554317
This commit is contained in:
zhurong 2016-03-07 02:52:38 +00:00 committed by Kirill Zaitsev
parent 5973c5ee7e
commit 94b0c092ed
18 changed files with 122 additions and 153 deletions

View File

@ -3,8 +3,8 @@ Murano
Murano Project introduces an application catalog, which allows application Murano Project introduces an application catalog, which allows application
developers and cloud administrators to publish various cloud-ready developers and cloud administrators to publish various cloud-ready
applications in a browsable categorised catalog. Cloud users applications in a browsable categorised catalog. Cloud users,
— including inexperienced ones — can then use the catalog to including inexperienced ones, can then use the catalog to
compose reliable application environments with the push of a button. compose reliable application environments with the push of a button.
Murano Dashboard Murano Dashboard

View File

@ -28,6 +28,7 @@ from muranodashboard.categories import tables
class CategoriesView(horizon_tables.DataTableView): class CategoriesView(horizon_tables.DataTableView):
table_class = tables.CategoriesTable table_class = tables.CategoriesTable
template_name = 'categories/index.html' template_name = 'categories/index.html'
page_title = _("Application Categories")
def has_prev_data(self, table): def has_prev_data(self, table):
return self._prev return self._prev

View File

@ -323,7 +323,9 @@ class EnvironmentsTable(tables.DataTable):
table_actions = (CreateEnvironment,) table_actions = (CreateEnvironment,)
row_actions = (ShowEnvironmentServices, DeployEnvironment, row_actions = (ShowEnvironmentServices, DeployEnvironment,
DeleteEnvironment, AbandonEnvironment) DeleteEnvironment, AbandonEnvironment)
multi_select = False # TODO(zhurong) Due to bug/1554087 in horizon, we should temporary
# comment out this 'multi_select = False' to pass the ci test
# multi_select = False
def get_service_details_link(service): def get_service_details_link(service):
@ -418,13 +420,14 @@ class ServicesTable(tables.DataTable):
class Meta(object): class Meta(object):
name = 'services' name = 'services'
verbose_name = _('Component List') verbose_name = _('Component List')
template = 'services/_data_table.html'
no_data_message = _('No components') no_data_message = _('No components')
status_columns = ['status'] status_columns = ['status']
row_class = UpdateServiceRow row_class = UpdateServiceRow
table_actions = (AddApplication, DeployThisEnvironment) table_actions = (AddApplication, DeployThisEnvironment)
row_actions = (DeleteService,) row_actions = (DeleteService,)
multi_select = False # TODO(zhurong) Due to bug/1554087 in horizon, we should temporary
# comment out this 'multi_select = False' to pass the ci test
# multi_select = False
class ShowDeploymentDetails(tables.LinkAction): class ShowDeploymentDetails(tables.LinkAction):
@ -458,7 +461,6 @@ class DeploymentsTable(tables.DataTable):
class Meta(object): class Meta(object):
name = 'deployments' name = 'deployments'
verbose_name = _('Deployments') verbose_name = _('Deployments')
template = 'common/_data_table.html'
row_actions = (ShowDeploymentDetails,) row_actions = (ShowDeploymentDetails,)
@ -475,4 +477,3 @@ class EnvConfigTable(tables.DataTable):
class Meta(object): class Meta(object):
name = 'environment_configuration' name = 'environment_configuration'
verbose_name = _('Deployed Components') verbose_name = _('Deployed Components')
template = 'common/_data_table.html'

View File

@ -36,6 +36,7 @@ from muranodashboard.environments import tabs as env_tabs
class IndexView(tables.DataTableView): class IndexView(tables.DataTableView):
table_class = env_tables.EnvironmentsTable table_class = env_tables.EnvironmentsTable
template_name = 'environments/index.html' template_name = 'environments/index.html'
page_title = _("Environments")
def get_data(self): def get_data(self):
environments = [] environments = []

View File

@ -63,6 +63,5 @@ class MarkedImagesTable(tables.DataTable):
class Meta(object): class Meta(object):
name = 'marked_images' name = 'marked_images'
verbose_name = _('Marked Images') verbose_name = _('Marked Images')
template = 'common/_data_table.html'
table_actions = (MarkImage, RemoveImageMetadata) table_actions = (MarkImage, RemoveImageMetadata)
row_actions = (RemoveImageMetadata,) row_actions = (RemoveImageMetadata,)

View File

@ -31,6 +31,7 @@ from muranodashboard.images import tables
class MarkedImagesView(horizon_tables.DataTableView): class MarkedImagesView(horizon_tables.DataTableView):
table_class = tables.MarkedImagesTable table_class = tables.MarkedImagesTable
template_name = 'images/index.html' template_name = 'images/index.html'
page_title = _("Marked Images")
def has_prev_data(self, table): def has_prev_data(self, table):
return self._prev return self._prev

View File

@ -238,7 +238,6 @@ class PackageDefinitionsTable(tables.DataTable):
name = 'packages' name = 'packages'
prev_pagination_param = 'marker' prev_pagination_param = 'marker'
verbose_name = _('Packages') verbose_name = _('Packages')
template = 'common/_data_table.html'
table_actions_menu = (ToggleEnabled, table_actions_menu = (ToggleEnabled,
TogglePublicEnabled) TogglePublicEnabled)
table_actions = (PackagesFilterAction, table_actions = (PackagesFilterAction,

View File

@ -73,6 +73,7 @@ def is_app(wizard):
class PackageDefinitionsView(horizon_tables.DataTableView): class PackageDefinitionsView(horizon_tables.DataTableView):
table_class = tables.PackageDefinitionsTable table_class = tables.PackageDefinitionsTable
template_name = 'packages/index.html' template_name = 'packages/index.html'
page_title = _("Packages")
_more = False _more = False
_prev = False _prev = False

View File

@ -2,10 +2,6 @@
{% load i18n %} {% load i18n %}
{% block title %}{% trans "Application Categories" %}{% endblock %} {% block title %}{% trans "Application Categories" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Application Categories") %}
{% endblock page_header %}
{% block main %} {% block main %}
{{ table.render }} {{ table.render }}
{% endblock %} {% endblock %}

View File

@ -1,10 +0,0 @@
{% extends 'horizon/common/_data_table.html' %}
{% load i18n %}
{% block table_caption %}
<tr class='table_caption'>
<th class='table_header' colspan='{{ columns|length }}'>
{{ table.render_table_actions }}
</th>
</tr>
{% endblock table_caption %}

View File

@ -1,4 +1,4 @@
{% extends 'common/_data_table.html' %} {% extends 'horizon/common/_data_table.html' %}
{% load i18n %} {% load i18n %}
{% block title %}{% trans "Environments" %}{% endblock %} {% block title %}{% trans "Environments" %}{% endblock %}

View File

@ -4,10 +4,6 @@
{% load compress %} {% load compress %}
{% block title %}{% trans "Environments" %}{% endblock %} {% block title %}{% trans "Environments" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Environments") %}
{% endblock page_header %}
{% block main %} {% block main %}
{{ table.render }} {{ table.render }}
{% endblock %} {% endblock %}

View File

@ -2,10 +2,6 @@
{% load i18n %} {% load i18n %}
{% block title %}{% trans "Marked Images" %}{% endblock %} {% block title %}{% trans "Marked Images" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Marked Images") %}
{% endblock page_header %}
{% block main %} {% block main %}
{{ table.render }} {{ table.render }}
{% endblock %} {% endblock %}

View File

@ -11,9 +11,6 @@
{% endcompress %} {% endcompress %}
{% endblock %} {% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Packages") %}
{% endblock page_header %}
{% block main %} {% block main %}
{{ table.render }} {{ table.render }}
{% endblock %} {% endblock %}

View File

@ -1,107 +1,93 @@
{% extends 'common/_data_table.html' %}
{% load i18n %} {% load i18n %}
{% load custom_filters %} {% load custom_filters %}
{% block title %}{% trans "Components" %}{% endblock %}
{% block table_caption %} {% block table_caption %}
{% with apps=table.get_apps_list %} {% with apps=table.get_apps_list %}
{% if table.actions_allowed %} {% comment %}
{% comment %} Note(efedorova): We are comparing to the string, since get_apps_list
Note(efedorova): We are comparing to the string, since get_apps_list functions returns json.dumps, which is used in js later.
functions returns json.dumps, which is used in js later. So to prevent extra transformations comparing to the string is used
So to prevent extra transformations comparing to the string is used to check for app absence.
to check for app absence. {% endcomment %}
{% endcomment %} {% if apps != '[]' %}
{% if apps != '[]' %} <div class="col-xs-5">
<div class="row row-header"> <h3 class="table_title extra_title table_title">
<div class="col-xs-5"> {% trans "Application&nbsp;Components" %}</h3>
<h3 class="table_title extra_title table_title">
{% trans "Application&nbsp;Components" %}</h3>
</div>
<h4 class="category-title">{% trans 'App category' %}</h4>
<div class="col-xs-3">
<div class="dropdown" id="envAppsCategory">
{% with categories=table.get_categories_list %}
<button class="btn btn-default dropdown-toggle"
type="button"
data-toggle="dropdown"
data-disabled="true"
aria-expanded="false"
id="envAppsCategoryBtn">
<span id="envAppsCategoryName">{% trans "All" %}</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu scrollable-menu"
role="menu"
aria-labelledby="envAppsCategoryBtn">
<li role="presentation"><a
role="menuitem" tabindex="-1"
data-category-name="All"
href="#">{% trans "All" %}</a>
</li>
{% for category in categories %}
<li role="presentation"><a
role="menuitem" tabindex="-1"
data-category-name="{{ category.name }}"
href="#">{{ category.name }} ({{category.package_count}})</a>
</li>
{% endfor %}
</ul>
{% endwith %}
</div>
</div>
<div class="col-xs-3 filter-bar">
<div class="has-feedback" id="envAppsFilter">
<input class="form-control" value="{{ search }}"
type="text"
placeholder={% trans "Find in a selected category" %}>
<span class="fa fa-search form-control-feedback search-icon"></span>
</div>
</div>
</div>
{% else %}
<p id="no_apps_in_catalog_message" class="col-xs-12 alert alert-info ">
{% with repo_url=table.get_repo_url pkg_def_url=table.get_pkg_def_url %}
{% trans "There are no applications in the catalog. You can import apps from" %}
<a href="{{ repo_url }}" target="_blank"> {{ repo_url }}</a>.
<br><br>
{% blocktrans trimmed %}Go to
<b>
<a href={{ pkg_def_url }}>Packages</a>
</b>, click 'Import Package' and select <i>Repository</i> as <i>Package Source</i>.
{% endblocktrans %}
{% endwith %}
</p>
{% endif %}
<div class="clearfix"></div>
<p id="no_apps_found_message" class="alert alert-info">
{% trans "There are no applications matching your criteria." %}</p>
<div id="apps_carousel" class="carousel">
<input type="hidden" id="apps_carousel_contents"
value="{{ apps }}">
<div class="carousel-inner"></div>
<a class="left carousel-control" ng-non-bindable href="#apps_carousel"
data-slide="prev">
<i class="fa fa-chevron-left"></i>
</a>
<a class="right carousel-control" ng-non-bindable href="#apps_carousel"
data-slide="next">
<i class="fa fa-chevron-right"></i>
</a>
</div> </div>
<h4 class="category-title">{% trans 'App category' %}</h4>
<div class="col-xs-3">
<div class="dropdown" id="envAppsCategory">
{% with categories=table.get_categories_list %}
<button class="btn btn-default dropdown-toggle"
type="button"
data-toggle="dropdown"
data-disabled="true"
aria-expanded="false"
id="envAppsCategoryBtn">
<span id="envAppsCategoryName">{% trans "All" %}</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu scrollable-menu"
role="menu"
aria-labelledby="envAppsCategoryBtn">
<li role="presentation"><a
role="menuitem" tabindex="-1"
data-category-name="All"
href="#">{% trans "All" %}</a>
</li>
{% for category in categories %}
<li role="presentation"><a
role="menuitem" tabindex="-1"
data-category-name="{{ category.name }}"
href="#">{{ category.name }} ({{category.package_count}})</a>
</li>
{% endfor %}
</ul>
{% endwith %}
</div>
</div>
<div class="col-xs-3 filter-bar">
<div class="has-feedback" id="envAppsFilter">
<input class="form-control" value="{{ search }}"
type="text"
placeholder={% trans "Find in a selected category" %}>
<span class="fa fa-search form-control-feedback search-icon"></span>
</div>
</div>
{% else %}
<p id="no_apps_in_catalog_message" class="col-xs-12 alert alert-info ">
{% with repo_url=table.get_repo_url pkg_def_url=table.get_pkg_def_url %}
{% trans "There are no applications in the catalog. You can import apps from" %}
<a href="{{ repo_url }}" target="_blank"> {{ repo_url }}</a>.
<br><br>
{% blocktrans trimmed %}Go to
<b>
<a href={{ pkg_def_url }}>Packages</a>
</b>, click 'Import Package' and select <i>Repository</i> as <i>Package Source</i>.
{% endblocktrans %}
{% endwith %}
</p>
{% endif %} {% endif %}
<div class="clearfix"></div>
<p id="no_apps_found_message" class="alert alert-info">
{% trans "There are no applications matching your criteria." %}</p>
<div id="apps_carousel" class="carousel">
<input type="hidden" id="apps_carousel_contents"
value="{{ apps }}">
<div class="carousel-inner"></div>
<a class="left carousel-control" ng-non-bindable href="#apps_carousel"
data-slide="prev">
<i class="fa fa-chevron-left"></i>
</a>
<a class="right carousel-control" ng-non-bindable href="#apps_carousel"
data-slide="next">
<i class="fa fa-chevron-right"></i>
</a>
</div>
{% endwith %} {% endwith %}
<tr class='table_caption'>
<th class='table_header' colspan='{{ columns|length }}'>
<h3 class='table_title'>{{ table }}</h3>
{{ table.render_table_actions }}
</th>
</tr>
{% endblock table_caption %} {% endblock table_caption %}
{% block table_body %} {% block table_body %}
@ -112,13 +98,4 @@
</h4> </h4>
</div> </div>
{% endif %} {% endif %}
<tbody>
{% for row in rows %}
{{ row.render }}
{% empty %}
<tr class="{% cycle 'odd' 'even' %} empty">
<td colspan="{{ table.get_columns|length }}">{{ table.get_empty_message }}</td>
</tr>
{% endfor %}
</tbody>
{% endblock table_body %} {% endblock table_body %}

View File

@ -1,5 +1,13 @@
{% load i18n %} {% load i18n %}
{% block main %} {% block main %}
{{ table.render }} <div class="row">
{% endblock %} <div class="col-sm-12">
{% include "services/_data_table.html" %}
<hr>
<div id="components">
{{ table.render }}
</div>
</div>
</div>
{% endblock %}

View File

@ -275,7 +275,8 @@ class UITestCase(BaseDeps):
def edit_environment(self, old_name, new_name): def edit_environment(self, old_name, new_name):
el_td = self.driver.find_element_by_css_selector( el_td = self.driver.find_element_by_css_selector(
'tr[data-display="{0}"] td:first-of-type'.format(old_name)) 'tr[data-display="{0}"] '.format(old_name) +
'td[data-cell-name="name"]')
el_pencil = el_td.find_element_by_css_selector( el_pencil = el_td.find_element_by_css_selector(
'button.ajax-inline-edit') 'button.ajax-inline-edit')
@ -287,14 +288,15 @@ class UITestCase(BaseDeps):
# fill in inline input # fill in inline input
el_inline_input = self.driver.find_element_by_css_selector( el_inline_input = self.driver.find_element_by_css_selector(
'tr[data-display="{0}"] '.format(old_name) + 'tr[data-display="{0}"] '.format(old_name) +
'td:first-of-type .inline-edit-form input') 'td[data-cell-name="name"] .inline-edit-form input')
el_inline_input.clear() el_inline_input.clear()
el_inline_input.send_keys(new_name) el_inline_input.send_keys(new_name)
# click submit # click submit
el_submit = self.driver.find_element_by_css_selector( el_submit = self.driver.find_element_by_css_selector(
'tr[data-display="{0}"] '.format(old_name) + 'tr[data-display="{0}"] '.format(old_name) +
'td:first-of-type .inline-edit-actions button[type="submit"]') 'td[data-cell-name="name"] .inline-edit-actions' +
' button[type="submit"]')
el_submit.click() el_submit.click()
# there is no alert message # there is no alert message
@ -308,7 +310,7 @@ class UITestCase(BaseDeps):
def wait_for_alert_message(self): def wait_for_alert_message(self):
locator = (by.By.CSS_SELECTOR, 'div.alert-success') locator = (by.By.CSS_SELECTOR, 'div.alert-success')
logger.debug("Waiting for a success message") logger.debug("Waiting for a success message")
ui.WebDriverWait(self.driver, 2).until( ui.WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located(locator)) EC.presence_of_element_located(locator))
def wait_for_error_message(self, sec=20): def wait_for_error_message(self, sec=20):

View File

@ -1057,12 +1057,13 @@ class TestSuitePackages(base.PackageTestCase):
pkg_name = self.alt_archive_name pkg_name = self.alt_archive_name
self.fill_field(by.By.CSS_SELECTOR, self.fill_field(by.By.CSS_SELECTOR,
"input[name='modify-name']", pkg_name) "input[name='modify-name']", pkg_name)
self.driver.find_element_by_css_selector(
"input[name=modify-is_public]" label = self.driver.find_element_by_css_selector(
).click() "label[for=id_modify-is_public]")
self.driver.find_element_by_css_selector( label.click()
"input[name=modify-enabled]" label = self.driver.find_element_by_css_selector(
).click() "label[for=id_modify-enabled]")
label.click()
self.driver.find_element_by_xpath(c.InputSubmit).click() self.driver.find_element_by_xpath(c.InputSubmit).click()
self.driver.find_element_by_xpath(c.InputSubmit).click() self.driver.find_element_by_xpath(c.InputSubmit).click()
@ -1162,9 +1163,9 @@ class TestSuitePackages(base.PackageTestCase):
# Public = OFF; Active = ON. # Public = OFF; Active = ON.
public_checkbox = self.driver.find_element_by_id('id_modify-is_public') public_checkbox = self.driver.find_element_by_id('id_modify-is_public')
active_checkbox = self.driver.find_element_by_id('id_modify-enabled') active_checkbox = self.driver.find_element_by_id('id_modify-enabled')
if public_checkbox.is_selected() is True: if public_checkbox.is_selected():
public_checkbox.click() public_checkbox.click()
elif active_checkbox.is_selected() is False: if not active_checkbox.is_selected():
active_checkbox.click() active_checkbox.click()
self.driver.find_element_by_xpath(c.InputSubmit).click() self.driver.find_element_by_xpath(c.InputSubmit).click()
self.driver.find_element_by_xpath(c.InputSubmit).click() self.driver.find_element_by_xpath(c.InputSubmit).click()
@ -1177,7 +1178,10 @@ class TestSuitePackages(base.PackageTestCase):
c.AppPackages.format(self.archive_name)) c.AppPackages.format(self.archive_name))
pkg_id = package.get_attribute("data-object-id") pkg_id = package.get_attribute("data-object-id")
self.select_action_for_package(pkg_id, 'modify_package') self.select_action_for_package(pkg_id, 'modify_package')
self.driver.find_element_by_id('id_is_public').click()
label = self.driver.find_element_by_css_selector(
"label[for=id_is_public]")
label.click()
self.driver.find_element_by_xpath(c.InputSubmit).click() self.driver.find_element_by_xpath(c.InputSubmit).click()
# Expecting Error # Expecting Error
self.wait_for_error_message() self.wait_for_error_message()