blob: 550f4153b5bad60aa992cf1983d7eb2ae8d82a3d [file] [log] [blame]
.. _test-ui-api:
Test UI API
===========
The Test UI API allows tests to interact with an operator, or to
display their status to the operator while they are running.
To use the test UI, inherit from
:py:class:`cros.factory.test.test_ui.TestCaseWithUI` instead of
:py:class:`unittest.TestCase` for the unittest. You can then call various
methods on ``self.ui`` (which is of type
:py:class:`cros.factory.test.test_ui.StandardUI`) to interact with the browser
(see `Test UI Class Reference`_).
For example, this test displays "Hello, world" in the UI, waits
five seconds, and then passes::
import time
from cros.factory.test import test_ui
class MyTest(test_ui.TestCaseWithUI):
def runTest(self):
self.template.SetState('Hello, world!')
self.Sleep(5)
The following document assumes that the test inherit from
:py:class:`cros.factory.test.test_ui.TestCaseWithUI`.
Including static resources
--------------------------
Often you will want to include static resources, such as images, HTML,
and JavaScript code, in your UI. Where to put static resources
depends on how you have laid out your test directory.
Simple layout
`````````````
If your test is called :samp:`{mytest}` and your test code is in the
file :samp:`py/test/pytests/{mytest}.py`, you may include static
resources in a directory called
:samp:`py/test/pytests/{mytest}_static`
In addition, these files will be automatically loaded if they exist:
* :samp:`py/test/pytests/{mytest}_static/{mytest}.html`
* :samp:`py/test/pytests/{mytest}_static/{mytest}.js`
* :samp:`py/test/pytests/{mytest}_static/{mytest}.css`
Nested layout
`````````````
If your test has its own subdirectory (i.e., your test code is in
:samp:`py/test/pytests/{mytest}/{mytest}.py`), then you can create a
``static`` directory within your test's subdirectory, and put static
resources there:
:samp:`py/test/pytests/{mytest}/static`
In addition, these files will be automatically loaded if they exist:
* :samp:`py/test/pytests/{mytest}/static/{mytest}.html`
* :samp:`py/test/pytests/{mytest}/static/{mytest}.js`
* :samp:`py/test/pytests/{mytest}/static/{mytest}.css`
Referring to static resources
`````````````````````````````
Within your UI, you can refer to static resources with relative
paths. For instance, if you have a file called ``foo.png`` in your
static directory:
:samp:`py/test/pytests/{mytest}/static/foo.png`
You may simply use this HTML to refer to the image:
``<img src="foo.png">``
Alternatively, you can use an absolute path:
:samp:`<img src="/tests/{mytest}/foo.png">`
Pass or fail the test
---------------------
To fail the test, do one of the followings:
* Calls ``window.test.fail(msg)`` in JavaScript implementation.
* Raises exception in either ``runTest`` or in the event handlers (Can be
exception raised by calling various ``self.assert*`` methods from
``unittest.TestCase``).
* Calls ``self.FailTask(error_msg)``, which is exactly the same as ``raise
type_utils.TestFailure(error_msg)``.
To pass the test, do one of the followings:
* Calls ``window.test.pass()`` in JavaScript implementation.
* Calls ``self.PassTask()`` in either ``runTest`` or in the event handlers.
* If nothing fails the test, the test is **automatically passed after**
``runTest`` **return**. To wait for either pass or fail is explictly called,
call ``self.WaitTaskEnd()`` at the end of ``runTest``, and the test would
wait for one of the conditions above is achieved.
When the test inherits from ``TestCaseWithUI``, the ``runTest`` method is run
in a **background** thread, while the UI event loop run in the main thread.
The ``PassTask``, ``FailTask`` and raises exception to fail test only works in
either event handlers or in the ``runTest`` thread. To achieve same behavior on
other threads, wrap the function with ``self.event_loop.CatchException``. For
example::
def BackgroundWork(self):
# Background works that have to be done in another thread.
def runTest(self):
thread = process_utils.StartDaemonThread(
target=self.event_loop.CatchException(self.BackgroundWork))
# Do some other things in parallel with BackgroundWork.
Test UI Templates
-----------------
There are default templates for test to keep look and feel consistent across
tests. To access the template, use methods on
:py:class:`cros.factory.test.test_ui.StandardUI`. For example::
class MyTest(test_ui.TestCaseWithUI):
def runTest(self):
self.ui.SetTitle('My Test')
self.ui.SetState('Hello, world!')
...
To change what UI class is used for ``self.ui``, set the ``ui_class`` for the
test class. For example::
class MyTest(test_ui.TestCaseWithUI):
ui_class = test_ui.ScrollableLogUI
def runTest(self):
self.ui.AppendLog('some log')
Test UI Class Reference
-----------------------
.. py:module:: cros.factory.test.test_ui
.. autoclass:: UI
:members:
.. autoclass:: StandardUI
:members: