2016-08-18 22:31:42 +02:00
|
|
|
# Web frontend black-box casperjs tests
|
|
|
|
|
|
|
|
These live in `frontend_tests/casper_tests/`. This is a "black box"
|
2017-02-10 08:22:13 +01:00
|
|
|
integration test; we load the frontend in a real (headless) browser,
|
|
|
|
from a real (development) server, and simulate UI interactions like
|
|
|
|
sending messages, narrowing, etc., by actually clicking around the UI
|
|
|
|
and waiting for things to change before doing the next step. These
|
|
|
|
tasks are fantastic for ensuring the overall health of the project,
|
|
|
|
but are also costly to maintain and keep free of nondeterministic
|
|
|
|
failures, so we usually prefer to write a Node test instead when
|
|
|
|
possible.
|
|
|
|
|
|
|
|
Since the Casper tests interact with a real dev server, they can often
|
|
|
|
catch backend bugs as well.
|
|
|
|
|
|
|
|
You can run the casper tests with `./tools/test-js-with-casper` or as
|
2016-08-18 22:31:42 +02:00
|
|
|
`./tools/test-js-with-casper 06-settings.js` to run a single test file
|
|
|
|
from `frontend_tests/casper_tests/`.
|
|
|
|
|
|
|
|
## Debugging Casper.JS
|
|
|
|
|
|
|
|
Casper.js (via PhantomJS) has support for remote debugging. However, it
|
|
|
|
is not perfect. Here are some steps for using it and gotchas you might
|
2017-02-10 08:22:13 +01:00
|
|
|
want to know; you'll likely also want to read the section on writing
|
|
|
|
tests (below) if you get stuck, since the advice on how to write
|
|
|
|
correct Casper selectors will likely be relevant.
|
|
|
|
|
|
|
|
The first thing to do when debugging Casper tests is to check the
|
|
|
|
additional debug output that our framework provides:
|
|
|
|
* You can check the screenshots of what the UI looked like at the time
|
|
|
|
of failures at `var/casper/casper-failure*.png`.
|
|
|
|
* If it's possible there's a backend exception involved,
|
|
|
|
`var/casper/server.log` will contain the server logs from the casper
|
|
|
|
run; it's worth looking there for tracebacks if you get stuck.
|
|
|
|
|
|
|
|
### Print debugging
|
|
|
|
|
|
|
|
If you need to use print debugging in casper, you can do using
|
|
|
|
`casper.log`; see <http://docs.casperjs.org/en/latest/logging.html> for
|
|
|
|
details.
|
|
|
|
|
|
|
|
An additional debugging technique is to enable verbose mode in the
|
|
|
|
Casper tests; you can do this by adding to the top of the relevant test
|
|
|
|
file the following:
|
|
|
|
|
|
|
|
> var casper = require('casper').create({
|
|
|
|
> verbose: true,
|
|
|
|
> logLevel: "debug"
|
|
|
|
> });
|
|
|
|
|
|
|
|
This can sometimes give insight into exactly what's happening.
|
|
|
|
|
|
|
|
### Remote debugging
|
|
|
|
|
|
|
|
This is a pain to setup with Vagrant because port `7777` and `9981`
|
|
|
|
aren't forwarded to the host by default, but can be pretty useful in
|
|
|
|
rare difficult cases.
|
2016-08-18 22:31:42 +02:00
|
|
|
|
|
|
|
To turn on remote debugging, pass `--remote-debug` to the
|
|
|
|
`./frontend_tests/run-casper` script. This will run the tests with port
|
|
|
|
`7777` open for remote debugging. You can now connect to
|
|
|
|
`localhost:7777` in a Webkit browser. Somewhat recent versions of Chrome
|
|
|
|
or Safari might be required.
|
|
|
|
|
|
|
|
- When connecting to the remote debugger, you will see a list of
|
|
|
|
pages, probably 2. One page called `about:blank` is the headless
|
|
|
|
page in which the CasperJS test itself is actually running in. This
|
|
|
|
is where your test code is.
|
|
|
|
- The other page, probably `localhost:9981`, is the Zulip page that
|
|
|
|
the test is testing---that is, the page running our app that our
|
|
|
|
test is exercising.
|
|
|
|
|
|
|
|
Since the tests are now running, you can open the `about:blank` page,
|
|
|
|
switch to the Scripts tab, and open the running `0x-foo.js` test. If you
|
|
|
|
set a breakpoint and it is hit, the inspector will pause and you can do
|
|
|
|
your normal JS debugging. You can also put breakpoints in the Zulip
|
|
|
|
webpage itself if you wish to inspect the state of the Zulip frontend.
|
|
|
|
|
2017-03-05 03:22:32 +01:00
|
|
|
### Reproducing races only seen in Travis CI
|
|
|
|
|
|
|
|
We've sometimes found it useful for reproducing Casper race conditions
|
|
|
|
in Casper tests that mostly only happen in Travis CI with really cheap
|
|
|
|
VPS servers (e.g. Scaleway's 2GB x86). This works because an ultra
|
|
|
|
slow machine is more likely to have things happen in an order similar
|
|
|
|
to what happens in Travis CI's very slow containers.
|
|
|
|
|
2016-08-18 22:31:42 +02:00
|
|
|
## Writing Casper tests
|
|
|
|
|
|
|
|
Probably the easiest way to learn how to write Casper tests is to study
|
|
|
|
some of the existing test files. There are a few tips that can be useful
|
|
|
|
for writing Casper tests in addition to the debugging notes below:
|
|
|
|
|
|
|
|
- Run just the file containing your new tests as described above to
|
|
|
|
have a fast debugging cycle.
|
2017-02-10 08:22:13 +01:00
|
|
|
- With frontend tests in general, it's very important to write your
|
2016-08-18 22:31:42 +02:00
|
|
|
code to wait for the right events. Before essentially every action
|
2017-02-10 08:22:13 +01:00
|
|
|
you take on the page, you'll want to use `waitUntilVisible`,
|
|
|
|
`waitWhileVisible`, or a similar function to make sure the page
|
|
|
|
or elemant is ready before you interact with it. For instance, if
|
|
|
|
you want to click a button that you can select via `#btn-submit`,
|
|
|
|
and then check that it causes `success-elt` to appear, you'll want
|
|
|
|
to write something like:
|
|
|
|
|
|
|
|
casper.waitUntilVisible("#btn-submit", function () {
|
2016-08-18 22:31:42 +02:00
|
|
|
casper.click('#btn-submit')
|
|
|
|
casper.test.assertExists("#success-elt");
|
|
|
|
});
|
|
|
|
|
2017-02-10 08:22:13 +01:00
|
|
|
In many cases, you will actually need to wait for the UI to update
|
|
|
|
clicking the button before doing asserts or the next step. This
|
|
|
|
will ensure that the UI has finished updating from the previous
|
|
|
|
step before Casper attempts to next step. The various wait
|
|
|
|
functions supported in Casper are documented in the Casper here:
|
2016-08-18 22:31:42 +02:00
|
|
|
<http://docs.casperjs.org/en/latest/modules/casper.html#waitforselector>
|
|
|
|
and the various assert statements available are documented here:
|
|
|
|
<http://docs.casperjs.org/en/latest/modules/tester.html#the-tester-prototype>
|
|
|
|
|
2017-02-10 08:22:13 +01:00
|
|
|
- The `casper.wait` style functions (`waitWhileVisible`,
|
|
|
|
`waitUntilVisible`, etc.) cannot be chained together in certain
|
|
|
|
conditions without creating race conditions where the test may
|
|
|
|
fail nondeterministically. For example, don't do this:
|
2016-08-18 22:31:42 +02:00
|
|
|
|
2017-02-10 08:22:13 +01:00
|
|
|
casper.waitUntilVisible('tag 1');
|
|
|
|
casper.click('button');
|
|
|
|
casper.waitUntilVisible('tag 2');
|
2016-08-18 22:31:42 +02:00
|
|
|
|
|
|
|
Instead, if you want to avoid race condition, wrap the second
|
|
|
|
`waitFor` in a `then` function like this:
|
|
|
|
|
|
|
|
casper.then(function () {
|
2017-02-10 08:22:13 +01:00
|
|
|
casper.waitUntilVisible('tag 1', function () {
|
|
|
|
casper.click('#btn-submit');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
casper.then(function () {
|
|
|
|
casper.waitUntilVisible('tag 2', function () {
|
|
|
|
casper.test.assertExists('#success-elt');
|
|
|
|
});
|
2016-08-18 22:31:42 +02:00
|
|
|
});
|
|
|
|
|
2017-02-10 08:22:13 +01:00
|
|
|
(You'll also want to use selectors that are as explicit as
|
|
|
|
possible, to avoid accidentally clicking multiple buttons or the
|
|
|
|
wrong button in your test, which can cause nondeterministic failures)
|
|
|
|
|
|
|
|
- Generally `casper.waitUntilVisible` is preferable to
|
|
|
|
e.g. `casper.waitForSelector`, since the former will confirm the
|
|
|
|
thing is actually on screen. E.g. if you're waiting to switch
|
2017-04-07 21:39:58 +02:00
|
|
|
from one panel of the the settings overlay to another by waiting
|
|
|
|
for a particular widget to appear, `casper.waitForSelector` may
|
|
|
|
not actually wait (since the widget is probably in the DOM, just
|
|
|
|
not visible), but casper.waitUntilVisible will wait until it's
|
|
|
|
actually shown.
|
2017-02-10 08:22:13 +01:00
|
|
|
|
|
|
|
- The selectors (i.e. things you put inside
|
|
|
|
`casper.waitUntilVisible()` and friends) appearing in Casper tests
|
|
|
|
are CSS3 selectors, which is a slightly different syntax from the
|
|
|
|
jQuery selectors used in the rest of the Zulip codebase; in
|
|
|
|
particular, some expressions that work with jQuery (and thus
|
|
|
|
normal Zulip JavaScript code) won't work with CSS3. It's often
|
|
|
|
helpful to debug selectors interactively, which you can do in the
|
|
|
|
Chrome JavaScript console. The way to do it is
|
|
|
|
`$$("#settings-dropdown")`; that queries CSS3 selectors, so you
|
|
|
|
can debug your selector in the console and then paste it into your
|
|
|
|
Casper test once it's working. For other browsers like Firefox,
|
|
|
|
you can use `querySelectorAll("#settings-dropdown")`, syntax which
|
|
|
|
is only available in the browser's JavaScript console.
|
2017-01-04 00:15:33 +01:00
|
|
|
|
|
|
|
You can learn more about these selectors and other JavaScript console tools
|
|
|
|
[here](https://developers.google.com/web/tools/chrome-devtools/console/command-line-reference).
|
2016-08-18 22:31:42 +02:00
|
|
|
- The test suite uses a smaller set of default user accounts and other
|
|
|
|
data initialized in the database than the development environment;
|
|
|
|
to see what differs check out the section related to
|
|
|
|
`options["test_suite"]` in
|
|
|
|
`zilencer/management/commands/populate_db.py`.
|
|
|
|
- Casper effectively runs your test file in two phases -- first it
|
|
|
|
runs the code in the test file, which for most test files will just
|
|
|
|
collect a series of steps (each being a `casper.then` or
|
|
|
|
`casper.wait...` call). Then, usually at the end of the test file,
|
|
|
|
you'll have a `casper.run` call which actually runs that series of
|
|
|
|
steps. This means that if you write code in your test file outside a
|
|
|
|
`casper.then` or `casper.wait...` method, it will actually run
|
|
|
|
before all the Casper test steps that are declared in the file,
|
|
|
|
which can lead to confusing failures where the new code you write in
|
|
|
|
between two `casper.then` blocks actually runs before either of
|
|
|
|
them. See this for more details about how Casper works:
|
|
|
|
<http://docs.casperjs.org/en/latest/faq.html#how-does-then-and-the-step-stack-work>
|