bmc_hub/tests/test_workflow_design.py

92 lines
3.8 KiB
Python

"""Render real workflow templates to verify permissions and common page structure."""
from html.parser import HTMLParser
from types import SimpleNamespace
from jinja2 import ChoiceLoader, DictLoader, Environment, FileSystemLoader
class Elements(HTMLParser):
def __init__(self, html):
super().__init__()
self.ids = {}
self.links = []
self.fieldsets = []
self.fieldset_stack = []
self.disabled_ancestors = {}
self.feed(html)
def handle_starttag(self, tag, attributes):
attrs = dict(attributes)
if 'id' in attrs:
self.ids[attrs['id']] = attrs
self.disabled_ancestors[attrs['id']] = any('disabled' in parent for parent in self.fieldset_stack)
if tag == 'a':
self.links.append(attrs.get('href'))
if tag == 'fieldset':
self.fieldsets.append(attrs)
self.fieldset_stack.append(attrs)
def handle_endtag(self, tag):
if tag == 'fieldset' and self.fieldset_stack:
self.fieldset_stack.pop()
def render(name, permissions=None):
env = Environment(loader=ChoiceLoader([
DictLoader({'shared/frontend/base.html': '{% block extra_css %}{% endblock %}{% block content %}{% endblock %}{% block extra_js %}{% endblock %}'}),
FileSystemLoader('app'),
]), autoescape=True)
return env.get_template('workflows/frontend/'+name+'.html').render(
workflow_permissions=permissions or {}, time_id=12,
request=SimpleNamespace(url=SimpleNamespace(path='/workflows')))
def test_viewer_can_browse_without_write_controls_or_admin_links():
html = render('builder', {'view': True})
page = Elements(html)
assert 'disabled' in page.fieldsets[0]
for key in ('new', 'save', 'copy', 'disable', 'test', 'activate'):
assert 'hidden' in page.ids['wf-'+key]
assert '/workflows/operations' in page.links
assert '/workflows/resources' not in page.links
assert '/tags/classification' not in page.links
assert 'Henter workflows' in html
def test_manage_and_activation_are_separate_from_system_admin_navigation():
html = render('builder', {'view': True, 'manage': True, 'activate': True})
page = Elements(html)
assert 'disabled' not in page.fieldsets[0]
assert 'hidden' not in page.ids['wf-save']
assert 'hidden' not in page.ids['wf-activate']
assert '/workflows/resources' in page.links
assert '/tags/classification' not in page.links
assert '/tags/classification' in Elements(render('builder', {'system_admin': True})).links
def test_override_does_not_expose_reset_without_activation_permission():
page = Elements(render('decision', {'override': True}))
assert 'hidden' in page.ids['decision-reset']
assert 'hidden' not in Elements(render('decision', {'override': True, 'activate': True})).ids['decision-reset']
def test_activation_only_user_can_deactivate_without_editing_definition():
page = Elements(render('builder', {'view': True, 'activate': True}))
assert 'hidden' not in page.ids['wf-disable']
assert not page.disabled_ancestors['wf-disable']
assert 'hidden' in page.ids['wf-save']
def test_ops_permission_flags_are_rendered_as_booleans_for_dynamic_buttons():
readonly = render('operations', {'view': True})
assert 'const canManage=false,canExport=false;' in readonly
assert 'const canManage=true,canExport=true;' in render('operations', {'manage': True, 'economic_export': True})
def test_pages_share_theme_tokens_and_responsive_scope():
for name in ('builder', 'decision', 'resources', 'operations'):
html = render(name, {'view': True})
assert 'container-fluid workflow-page' in html
assert 'workflow-heading' in html
assert 'var(--bg-card' in html
assert 'var(--text-secondary' in html
assert '@media(max-width:576px)' in html