blob: ad23d3199981204b74df70223dfb37efdfda20c2 [file] [log] [blame]
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import factory_common # pylint: disable=W0611
from cros.factory.test import factory
from cros.factory.test import test_ui
_UI_TEMPLATE_PATH = '/ui_templates'
class Option(object):
'''Utility class for generating and manipulating HTML option tag.
Args:
value: Text value of the option. This is the value inside option tag.
display: Displayed value of the option. This is the value shown on page.
selected: Boolean value indicating whether this option is selected.
'''
def __init__(self, value, display, selected=False):
self._value = value
self._display = display
self._selected = selected
def SetSelected(self, value):
'''Set selected attribute
Args:
value: A boolean value indicating the selected status.
'''
self._selected = value
def GenerateHTML(self):
"""Generate HTML tag."""
return '<option value="%s" %s>%s</option>' % (
self._value, 'selected' if self._selected else '', self._display)
class SelectBox(object):
'''Utility class for generating and manipulating HTML select box and options.
Args:
id: ID of the select box.
size: The size of the select box.
style: CSS style to apply on the select box.
'''
def __init__(self, element_id, size=10, style=None):
self._element_id = element_id
self._size = size
self._style = style
self._option_list = []
def InsertOption(self, value, display, index=None):
'''Inserts a option into the select box.
Args:
value: Text value of the option. This is the value inside option tag.
display: Displayed value of the option. This is the value shown on page.
'''
option = Option(value, display)
if index:
self._option_list.insert(index, option)
else:
self._option_list.append(option)
def SetSelectedIndex(self, index):
"""Set the given index as selected."""
if len(self._option_list) < index:
return
self._option_list[index].SetSelected(True)
def GenerateHTML(self):
"""Generate HTML tags."""
ele_list = ['<select id="%s" size=%d style="%s">' % (
self._element_id, self._size, self._style)]
for opt in self._option_list:
ele_list += [opt.GenerateHTML()]
ele_list += ['</select>']
return '\n'.join(ele_list)
class Table(object):
'''Utility class for generating HTML table.
This class allows us to easily set the content of each cell. For example:
table = Table(element_id='example_table', rows=2, cols=2)
for r in xrange(2):
for c in xrange(2):
table.SetContent(r, c, 'row %d col %d' % (r, c))
return table.GenerateHTML()
Args:
element_id: ID of the table.
rows: Number of rows.
cols: Number of columns.
style: CSS style to apply on the table.
'''
def __init__(self, element_id=None, rows=1, cols=1, style=None):
self._element_id = element_id or ''
self._style = style or ''
self._content = dict()
self.rows = rows
self.cols = cols
def SetContent(self, row, col, content):
"""Sets HTML content of specified row and column."""
self._content[(row, col)] = content
def GenerateHTML(self):
"""Generates HTML tags."""
html = ['<table id="%s" style="%s">' % (
self._element_id, self._style)]
for r in xrange(self.rows):
html.append('<tr>')
for c in xrange(self.cols):
html.append('<td>')
if (r, c) in self._content:
html.append(self._content[(r, c)])
html.append('</td>')
html.append('</tr>')
html.append('</table>')
return ''.join(html)
class BaseTemplate(object):
"""Base class for test UI template."""
def __init__(self, ui, template_name):
self._ui = ui
template_base = os.path.join(factory.FACTORY_PACKAGE_PATH,
'goofy/static/ui_templates')
html_file = os.path.join(template_base, template_name + '.html')
assert os.path.exists(html_file), \
'Template %s does not exist.' % template_name
# Load template HTML
self._ui.SetHTML(open(html_file).read())
# Load template JS if it exists
js_file = os.path.join(template_base, template_name + '.js')
if os.path.exists(js_file):
self._ui.RunJS(open(js_file).read())
metadata = factory.get_current_test_metadata()
self.SetTitle(test_ui.MakeLabel(metadata.get('label_en', ''),
metadata.get('label_zh', '')))
def SetTitle(self, html):
'''Sets the title of the test UI.
Args:
html: The html content to write.'''
self._ui.SetHTML(html, id='title')
def BindStandardKeys(self, bind_pass_key=True, bind_fail_key=True):
'''Binds standard pass and/or fail keys.
This causes the Space, and Enter, and P keys to pass the test; and
ESC and f to fail the test.
Also shows prompt at the bottom of the test area.
Args:
bind_pass_key: True to bind keys to pass the test.
bind_fail_key: True to bind keys to fail the test.
'''
self._ui.SetHTML(
test_ui.MakePassFailKeyLabel(pass_key=bind_pass_key,
fail_key=bind_fail_key),
id='prompt-pass-fail-keys')
self._ui.BindStandardKeys(bind_pass_keys=bind_pass_key,
bind_fail_keys=bind_fail_key)
class OneSection(BaseTemplate):
'''A simple template that has only one big section.
This is a simple template which is suitable for tests that do not
require showing much information.
This template provides the following sections:
* SetTitle: For the title of the test.
* SetState: For displaying the state of the test or instructions to
operator.
'''
def __init__(self, ui): # pylint: disable=W0231
super(OneSection, self).__init__(ui, 'template_one_section')
def SetState(self, html, append=False):
'''Sets the state section in the test UI.
Args:
html: The html to write.'''
self._ui.SetHTML(html, append=append, id='state')
class OneScrollableSection(BaseTemplate):
'''Like OneSection, but is used to show more info.
Instead of central-aligned state window, it shows state in a scrollable
element and state is left-aligned.
This template provides the following sections:
* SetTitle: For the title of the test.
* SetState: For displaying the state of the test.
'''
def __init__(self, ui): # pylint: disable=W0231
super(OneScrollableSection, self).__init__(
ui, 'template_one_scrollable_section')
def SetState(self, html, append=False, scroll_down=False):
'''Sets the state section in the test UI.
Args:
html: The html to write.
append: Append html at the end.
scroll_down: Scroll down if needed.
'''
self._ui.SetHTML(html, append=append, id='state')
if scroll_down:
self._ui.RunJS("var s = document.getElementById('state');"
's.scrollTop = s.scrollHeight;')
class TwoSections(BaseTemplate):
'''A template that consists of two sections.
The upper sections is for showing instructions to operators, and
has a progress bar that is hidden by default. The lower section
is for showing information regarding test state, like instructional
pictures, or texts that indicate the progress of the test.
This template provides the following methods:
* SetTitle: For the title of the test.
* SetInstruction: For displaying instructions to the operator.
* SetState: For visually displaying the test progress.
* DrawProgressBar, SetProgressBarValue: For showing information
regarding the progress or state of the test. The progress bar
is hidden by default.
'''
def __init__(self, ui): # pylint: disable=W0231
super(TwoSections, self).__init__(ui, 'template_two_sections')
def SetInstruction(self, html, append=False):
'''Sets the instruction to operator.
Args:
html: The html content to write.'''
self._ui.SetHTML(html, append=append, id='instruction')
def SetState(self, html, append=False):
'''Sets the state section in the test UI.
Args:
html: The html to write.'''
self._ui.SetHTML(html, append=append, id='state')
def DrawProgressBar(self):
'''Draw the progress bar and set it visible on the Chrome test UI.
Best practice is that if the operator needs to wait more than 5 seconds,
we should show the progress bar to indicate test progress.
'''
self._ui.CallJSFunction('DrawProgressBar')
def SetProgressBarValue(self, value):
'''Set the value of the progress bar.
Args:
value: A value between 0 and 100 to indicate test progress.
'''
self._ui.CallJSFunction('SetProgressBarValue', value)