Skip to content

Commit

Permalink
Complete rewrite of first tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
owickstrom committed Nov 9, 2022
1 parent 69fb021 commit 52bb082
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 85 deletions.
16 changes: 13 additions & 3 deletions docs/source/_static/audioplayer/audioplayer.strom
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ action ~tick? = changed?(`.time-display`);

// We want to interact with the player by clicking buttons.
action ~pause! = click!(`.play-pause`) when playing;
// We wait for 1000ms after starting playback, to allow for ticks to
// happen asynchronously.
action ~play! = click!(`.play-pause`) when paused;

// We wait to allow for ticks to happen asynchronously.
action ~wait! = noop! timeout 1000;

// The proposition describes the correct behavior of the web
// application. Here we start in the paused state, and a valid
// transition is either `play` or `pause`.
Expand All @@ -56,6 +57,10 @@ let ~proposition =
&& nextT playing
&& (let old = timeInSeconds; nextT (old < timeInSeconds));

let ~wait =
nextT (contains(noop!, happened))
==> unchanged([timeInSeconds, playing]);

// This last part is the central part of the specification,
// describing the initial state and the possible transitions. It
// can be read in English as:
Expand All @@ -65,6 +70,11 @@ let ~proposition =
// all indefinitely.
//
// We require 20 states to be checked.
paused && (always {20} (play || pause || tick));
paused && (always {20} (
play
|| pause
|| tick
|| wait
));

check proposition with * when loaded?;
197 changes: 115 additions & 82 deletions docs/source/tutorials/first.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ it using ``curl``:
If you don't have ``curl`` installed, you can download it from `this
URL
<https://github.com/quickstrom/quickstrom/raw/main/examples/AudioPlayer.html>`__
<https://github.com/quickstrom/quickstrom/raw/main/docs/source/_static/audioplayer/audioplayer.html>`__
using your web browser. Make sure you've saved it our working
directory as ``AudioPlayer.html``.
directory as ``audioplayer.html``.

.. code-block:: console
Expand Down Expand Up @@ -292,48 +292,63 @@ You'll see a bunch of output, involving shrinking tests and more. It
should end with something like the following:

.. code-block::
:emphasize-lines: 16
1. State
• .play-pause
-
- property "textContent" = "Play"
• .time-display
-
- property "textContent" = "00:00"
2. click button[0]
3. click button[0]
4. State
• .play-pause
-
- property "textContent" = "Play"
• .time-display
-
- property "textContent" = "NaN:NaN"
Failed after 1 tests and 4 levels of shrinking.
Whoops, look at that! It says that the time display shows
"NaN:NaN". We've found our first bug using Quickstrom!

Open up ``AudioPlayer.html``, and change the following lines near the
end of the file:
:emphasize-lines: 5,14,21
.. code-block:: javascript
Transition #1
case "pause":
return await inPaused();
Actions and events:
They should be:
- click('33a4c299-a382-44c2-870e-b48335f9c23a')
State difference:
`.play-pause`
╒══════════════════════════════════════╤══════════════════════════════════════╕
│ 33a4c299-a382-44c2-870e-b48335f9c23a │ 33a4c299-a382-44c2-870e-b48335f9c23a │
│ enabled: true │ enabled: true │
│ interactable: true │ interactable: true │
│ textContent: "Play" │ textContent: "Play" │
│ visible: true │ visible: true │
╘══════════════════════════════════════╧══════════════════════════════════════╛
`.time-display`
╒══════════════════════════════════════╤══════════════════════════════════════╕
│ bd797365-5239-4a9b-9976-942288bd227a │ bd797365-5239-4a9b-9976-942288bd227a │
│ textContent: "00:00" │ textContent: "00:00" │
╘══════════════════════════════════════╧══════════════════════════════════════╛
Look, we've found our first bug using Quickstrom! It seems clicking the
play/pause doesn't do anything. It should change the label to "Pause" to
indicate it's in the playing state.

The problem is the button text. Open up ``audioplayer.html``, and change the
following function called ``playPauseLabel``:

.. code-block:: javascript
case "pause":
return await inPaused(time); // <-- this is where we must pass in time
function playPauseLabel(state) {
let label;
switch (state) {
case "playing":
label = "Pause";
case "paused":
label = "Play";
}
return label;
}
Rerun the tests using the same ``quickstrom`` command as before. All
tests pass!
It should be:

.. code-block:: javascript
function playPauseLabel(state) {
switch (state) {
case "playing":
return "Pause";
case "paused":
return "Play";
}
}
Are we done? Is the audio player correct? Not quite.

Expand All @@ -342,61 +357,78 @@ Transitions Based on Time

The audio player transitions between states mainly as a result of
user action, but not only. A ``tick`` transition (going from
``playing`` to ``playing`` with an incremented progress) is triggered
``playing`` to ``playing`` with an increased time) is triggered
by *time*.

We'll try tweaking Quickstrom's options related to :doc:`trailing
state changes <../../topics/trailing-state-changes>` to test more of the
time-related behavior of the application.
In addition to our ``play!`` and ``pause!`` actions, we'll add a ``wait!``
action. It does nothing for at most two seconds, until either something
happens (a ``tick?`` event) or a timeout:

.. code-block:: javascript
action ~wait! = noop! timeout 2000;
But this means we have to allow nothing to change. This is often
called a *stutter state*. Let's do that, but only in combination with
a ``noop!`` action. Add one more state transition to the proposition:

.. code-block:: javascript
:emphasize-lines: 3-5, 11
let ~proposition =
...
let ~wait =
nextT (contains(noop!, happened))
==> unchanged([timeInSeconds, playing]);
paused && (always {20} (
play
|| pause
|| tick
|| wait
));
Run new tests by executing the following command:
Run another check by executing the same command as before:

.. code-block:: console
:emphasize-lines: 10-11
$ docker run --rm \
--network quickstrom \
-v $PWD:/my-first-spec \
quickstrom/quickstrom \
quickstrom check \
--webdriver-host=webdriver \
--webdriver-path=/wd/hub \
--browser=chrome \
--tests=5 \
--max-trailing-state-changes=1 \
--trailing-state-change-timeout=500 \
/my-first-spec/AudioPlayer.spec.purs \
/my-first-spec/AudioPlayer.html
quickstrom/quickstrom:0.5.0 \
quickstrom -I/my-first-spec check \
audioplayer \
/my-first-spec/audioplayer.html \
--browser=chrome
You should see output such as the following:

.. code::
.. code-block::
:emphasize-lines: 3
Check was aborted:
parseInt could not parse: "NaN"
ParseInt @ /my-first-spec/audioplayer.strom:15:2
<fun> @ /nix/store/xradz0aav0ijajw91x1vqfqsprgipfhx-specstrom/share/ulib/control.strom:30:21
Whoops, look at that! It crashes because the time display shows "NaN",
which is definitely not intended behavior. Open up
``audioplayer.html``, and change the following lines near the end of
the file:

.. code-block:: javascript
case "pause":
return await inPaused();
They should be:

.. code-block:: javascript
case "pause":
return await inPaused(time); // <-- this is where we must pass in time
1. State
• .play-pause
-
- property "textContent" = "Play"
• .time-display
-
- property "textContent" = "00:00"
2. click button[0]
3. State
• .play-pause
-
- property "textContent" = "Play"
• .time-display
-
- property "textContent" = "00:01"
Failed after 1 tests and 5 levels of shrinking.
Look, another bug! It seems that there are ``tick`` transitions even
though the play/pause button indicates that we're in the ``paused``
state.

In fact, the problem is the button text, not the time display. I'll
leave it up to you to find the error in the code, fix it, and make
the tests pass.
Rerun the check using the same ``quickstrom`` command as before. It passes!

Summary
-------
Expand All @@ -405,8 +437,9 @@ Congratulations! You've completed the tutorial, created your first
specification, and found multiple bugs.

Have we found all bugs? Possibly not. This is the thing with testing.
We can't know if we've found all problems. However, Quickstrom tries
very hard to find more of them for you, requiring less effort.
We can't know if we've found all problems. However, with Quickstrom
you can increase your confidence in the correctness of your web
app, especially if you continously run tests on it.

This tutorial is intentionally fast-paced and low on theory. Now that
you've got your hands dirty, it's a good time to check out
Expand Down

0 comments on commit 52bb082

Please sign in to comment.