#include «chrome/browser/ui/test/test_browser_ui.h«
TestBrowserUi
(and convenience class TestBrowserDialog
) provide ways to
register an InProcessBrowserTest
testing harness with a framework that invokes
Chrome browser UI in a consistent way. They optionally provide a way to invoke
UI «interactively». This allows screenshots to be generated easily, with the
same test data, to assist with UI review. TestBrowserUi
also provides a UI
registry so pieces of UI can be systematically checked for subtle changes and
regressions.
[TOC]
If registering existing UI, there’s a chance it already has a test harness
inheriting, using, or with typedef InProcessBrowserTest
(or a descendant of
it). If so, using TestBrowserDialog
(for a dialog) is straightforward, and
TestBrowserUi
(for other types of UI) relatively so. Assume the existing
InProcessBrowserTest
is in foo_browsertest.cc
:
class FooUiTest : public InProcessBrowserTest { ...
Change this to inherit from DialogBrowserTest
(for dialogs) or UiBrowserTest
(for non-dialogs), and override ShowUi(std::string)
. For non-dialogs, also
override VerifyUi()
and WaitForUserDismissal()
. See
Advanced Usage for details.
class FooUiTest : public UiBrowserTest {
public:
..
// UiBrowserTest:
void ShowUi(const std::string& name) override {
/* Show Ui attached to browser() and leave it open. */
}
// These next two are not necessary if subclassing DialogBrowserTest.
bool VerifyUi() override {
/* Return true if the UI was successfully shown. */
}
void WaitForUserDismissal() override {
/* Block until the UI has been dismissed. */
}
..
};
Finally, add test invocations using the usual GTest macros, in
foo_browsertest.cc
:
IN_PROC_BROWSER_TEST_F(FooUiTest, InvokeUi_default) {
ShowAndVerifyUi();
}
Notes:
ShowAndVerifyUi();
».default
» is the std::string
passed to ShowUi()
and can be
customized. See
Testing additional UI «styles».default
(in this case) must always be «InvokeUi_
».List the available pieces of UI with
$ ./browser_tests --gtest_filter=BrowserUiTest.Invoke
E.g. FooUiTest.InvokeUi_default
should be listed. To show the UI
interactively, run
$ ./browser_tests --gtest_filter=BrowserUiTest.Invoke \
--test-launcher-interactive --ui=FooUiTest.InvokeUi_default
BrowserUiTest.Invoke
searches for gtests that have «InvokeUi_
» in their
names, so they can be collected in a list. Providing a --ui
argument will
invoke that test case in a subprocess. Including --test-launcher-interactive
will set up an environment for that subprocess that allows interactivity, e.g.,
to take screenshots. The test ends once the UI is dismissed.
The FooUiTest.InvokeUi_default
test case will still be run in the usual
browser_tests test suite. Ensure it passes, and isn’t flaky. This will
give your UI some regression test coverage. ShowAndVerifyUi()
checks to ensure
UI is actually created when it invokes ShowUi("default")
.
This is also run in browser_tests but, when run that way, the test case just lists the registered test harnesses (it does not iterate over them). A subprocess is never created unless –ui is passed on the command line.
If your test harness inherits from a descendant of InProcessBrowserTest
(one
example: ExtensionBrowserTest) then the SupportsTestUi<>
and
SupportsTestDialog
templates are provided. E.g.
class ExtensionInstallDialogViewTestBase : public ExtensionBrowserTest { ...
becomes
class ExtensionInstallDialogViewTestBase :
public SupportsTestDialog<ExtensionBrowserTest> { ...
If you need to do any setup before ShowUi()
is called, or any teardown in the
non-interactive case, you can override the PreShow()
and `DismissUi()
methods.
Add additional test cases, with a different string after «InvokeUi_
».
Example:
IN_PROC_BROWSER_TEST_F(CardUnmaskViewBrowserTest, InvokeUi_expired) {
ShowAndVerifyUi();
}
IN_PROC_BROWSER_TEST_F(CardUnmaskViewBrowserTest, InvokeUi_valid) {
ShowAndVerifyUi();
}
The strings «expired
» or “valid
” will be given as arguments to
ShowUi(std::string)
.
Bug reference: Issue 654151.
Chrome has a lot of browser UI; often for obscure use-cases and often hard to invoke. It has traditionally been difficult to be systematic while checking UI for possible regressions. For example, to investigate changes to shared layout parameters which are testable only with visual inspection.
For Chrome UI review, screenshots need to be taken. Iterating over all the «styles» that UI may appear with is fiddly. E.g. a login or particular web server setup may be required. It’s important to provide a consistent “look” for UI review (e.g. same test data, same browser size, anchoring position, etc.).
Some UI lacks tests. Some UI has zero coverage on the bots. UI elements can have tricky lifetimes and common mistakes are repeated. TestBrowserUi runs simple «Show UI» regression tests and can be extended to do more.
Even discovering the full set of UI present for each platform in Chrome is difficult.
browser_tests
already provides a browser()->window()
of a consistent
size that can be used as a dialog anchor and to take screenshots for UI
review.
Some UI already has a test harness with appropriate setup (e.g. test data) running in browser_tests.
BrowserUiTest
should require minimal setup and minimal
ongoing maintenance.An alternative is to maintain a working end-to-end build target executable to do this, but this has additional costs (and is hard).
InitializeGLOneOffPlatform()
, etc.).Why not chrome.exe?
Opt in more UI!
DialogBrowserTest
for every descendant of views::DialogDelegate
.Automatically generate screenshots (for each platform, in various languages)
(maybe) Try removing the subprocess
An automated test suite for UI
DialogClientView::AcceptWindow
(and CancelWindow
)Widget::Close
Widget::CloseNow
Find obscure workflows for invoking UI that has no test coverage and causes crashes (e.g. http://crrev.com/426302)
Find memory leaks, e.g. http://crrev.com/432320
$ ./out/gn_Debug/browser_tests –gtest_filter=BrowserUiTest.Invoke
Note: Google Test filter = BrowserUiTest.Invoke
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from BrowserUiTest
[ RUN ] BrowserUiTest.Invoke
[26879:775:0207/134949.118352:30434675...:INFO:browser_ui_browsertest.cc(46)
Pass one of the following after --ui=
AppInfoDialogBrowserTest.InvokeUi_default
AskGoogleForSuggestionsDialogTest.DISABLED_InvokeUi_default
BluetoothChooserBrowserTest.InvokeUi_ConnectedBubble
BluetoothChooserBrowserTest.InvokeUi_ConnectedModal
/* and many more */
[ OK ] BrowserUiTest.Invoke (0 ms)
[----------] 1 test from BrowserUiTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1 ms total)
[ PASSED ] 1 test.
[1/1] BrowserUiTest.Invoke (334 ms)
SUCCESS: all tests passed.
$ ./out/gn_Debug/browser_tests –gtest_filter=BrowserUiTest.Invoke –ui=CardUnmaskPromptViewBrowserTest.InvokeUi_expired
Note: Google Test filter = BrowserUiTest.Invoke
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from BrowserUiTest
[ RUN ] BrowserUiTest.Invoke
Note: Google Test filter = CardUnmaskPromptViewBrowserTest.InvokeDefault
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from CardUnmaskPromptViewBrowserTest, where TypeParam =
[ RUN ] CardUnmaskPromptViewBrowserTest.InvokeUi_expired
/* 7 lines of uninteresting log spam */
[ OK ] CardUnmaskPromptViewBrowserTest.InvokeUi_expired (1324 ms)
[----------] 1 test from CardUnmaskPromptViewBrowserTest (1324 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1325 ms total)
[ PASSED ] 1 test.
[ OK ] BrowserUiTest.Invoke (1642 ms)
[----------] 1 test from BrowserUiTest (1642 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1642 ms total)
[ PASSED ] 1 test.
[1/1] BrowserUiTest.Invoke (2111 ms)
SUCCESS: all tests passed.
$ ./out/gn_Debug/browser_tests –gtest_filter=BrowserUiTest.Invoke –ui=CardUnmaskPromptViewBrowserTest.InvokeUi_expired –test-launcher-interactive
/*
* Output as above, except the test are not interleaved, and the browser window
* should remain open until the UI is dismissed
*/