.. _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_case.TestCase` 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_case.TestCase): 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_case.TestCase`. See :ref:`test-i18n-api` on how to display localized texts on UI. 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: ```` Alternatively, you can use an absolute path: :samp:`` 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 ``test_case.TestCase``, 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_case.TestCase): 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_case.TestCase): 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: