# check


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

[`validate`](https://vedicreader.github.io/rupaka/spec.html#validate)
reads the spec.
[`check`](https://vedicreader.github.io/rupaka/check.html#check) runs
it. It writes the static page, opens it in a headless browser with
playwright, and plays the demo as a learner would. The browser is always
headless, so no window opens and nothing takes the focus.

These checks run in light and in dark mode:

1.  The demo draws: the host gets `data-rk-ready="1"`. Any console
    error, page error, or request that is not `file:`, `data:` or
    `about:` fails.
2.  Each visible block has a box that is not empty. Each block with
    `after` is hidden before its gate.
3.  Each text has a contrast of at least 4.5:1 against what is behind it
    (3:1 for large text), before and after the reveal.
4.  Play: for each gate, pick the first option and lock in with ⌘↩. The
    verdict shows, the `rk-predict` event fires, its answer matches the
    Python twin, and the blocks that wait show.
5.  Keys: each control gets a key press, and the state must move. The
    derived values must match the Python twins.

## The browser

The check never uses the Chrome that you use every day: a headless run
of that app shares its crash reporter, its updater and its app
registration, and many runs can crash it. It uses, in this order: a path
that you give, the path in `$FASTCDP_CHROME`, or the Chromium that
`playwright install chromium` gives. One process runs one browser at a
time, and each browser closes in a `finally` block.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/check.py#L29"
target="_blank" style="float:right; font-size:smaller">source</a>

### browser

``` python
def browser(
    path:str=None
):
```

*A headless browser for
[`check`](https://vedicreader.github.io/rupaka/check.html#check); one at
a time in this process, closed at the end.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/check.py#L20"
target="_blank" style="float:right; font-size:smaller">source</a>

### browser_path

``` python
def browser_path(
    path:str=None
):
```

*The browser to use: `path`, else `$FASTCDP_CHROME`, else None for
playwright’s own Chromium. Never the everyday Chrome.*

The default is playwright’s own Chromium, not the everyday Chrome, and a
path into the everyday Chrome is refused.

``` python
import os
```

``` python
if not os.getenv('FASTCDP_CHROME'): test_eq(browser_path(), None)
test_fail(lambda: browser_path('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'), contains='everyday Chrome')
```

[`can_check`](https://vedicreader.github.io/rupaka/check.html#can_check)
says if a browser starts here. The notebook runs its browser tests only
when one does.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/check.py#L51"
target="_blank" style="float:right; font-size:smaller">source</a>

### can_check

``` python
def can_check()->bool:
```

*True when playwright and a browser are there.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/check.py#L42"
target="_blank" style="float:right; font-size:smaller">source</a>

### in_thread

``` python
def in_thread(
    f, *a, **kw
):
```

*Call `f`; in a thread when an asyncio loop runs here (as in a
notebook), because playwright’s sync API cannot run in a loop.*

## Contrast

This script runs in the page. For each element with its own text, it
finds the colour behind the text: the backgrounds of the element and its
parents, and for SVG text, a shape behind it in the same group (the dot
of a picked wheel item, the cell of a grid). It mixes in opacity, then
gives the WCAG ratio. Disabled controls and hidden text are skipped.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/check.py#L88"
target="_blank" style="float:right; font-size:smaller">source</a>

### contrast

``` python
def contrast(
    pg
)->list:
```

*The texts of the page whose contrast is under the WCAG need, as
`(text, ratio, need)`.*

## The parts of the check

[`_state`](https://vedicreader.github.io/rupaka/check.html#_state) reads
the state of the demo from the page.
[`_parity`](https://vedicreader.github.io/rupaka/check.html#_parity)
compares each derived value with the Python twin of its fn.

[`_boxes`](https://vedicreader.github.io/rupaka/check.html#_boxes)
checks that each visible block has a box, and that each block with
`after` is hidden before its gate. Chrome’s `checkVisibility()` does not
see `display: none` on an SVG element, so `VIS` also looks at the
element and its parents.

[`_play`](https://vedicreader.github.io/rupaka/check.html#_play) plays
each gate: it picks the first option with the key 1, and locks in with
⌘↩.

[`_keys`](https://vedicreader.github.io/rupaka/check.html#_keys) sends
keys to each control and checks that the state moves. A control at one
end of its range gets the opposite key too.

## check

[`check`](https://vedicreader.github.io/rupaka/check.html#check) puts
the parts together. It returns `AttrDict(ok, errors, info, shots)`. With
`shots`, it saves screenshots at 2× before and after the play, in light
and dark.

[`check`](https://vedicreader.github.io/rupaka/check.html#check) runs in
a thread when an asyncio loop runs, as in a notebook.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/check.py#L202"
target="_blank" style="float:right; font-size:smaller">source</a>

### check

``` python
def check(
    spec, shots:NoneType=None, modes:tuple=('light', 'dark'), play:bool=True, b:NoneType=None, path:str=None
)->fastcore.basics.AttrDict:
```

*Draw `spec` in a headless browser, play it, and return
`AttrDict(ok, errors, info, shots)`; `b` reuses an open browser.*

## Tests

These tests need a browser, so they run only where one starts.

``` python
ok = can_check()
ok
```

A good demo passes. The info has the options of the gate and the
verdict.

``` python
from rupaka.spec import examples
```

``` python
if ok:
    r = check(examples()['gate'])
    test_eq(r.errors, [])
    test_eq(r.info['light'][0]['options'], ['4', '5', '6', '23'])
    assert r.info['light'][0]['verdict'].startswith('You said 4. It is 5.')
```

A spec with a broken fn name fails with the error text of the runtime.

``` python
if ok:
    bad = {**examples()['gate'], 'derive': [{'fn': 'sums', 'out': 'sum'}]}
    r = check(bad, modes=('light',))
    assert not r.ok and 'unknown fn "sums"' in r.errors[0], r.errors
```

Each block example passes the whole check, in light and dark.

``` python
def check_all(specs):
    with browser() as br: return {n: check(s, b=br).errors for n, s in specs.items()}
if ok: test_eq(in_thread(check_all, examples()), {n: [] for n in examples()})
```
