Skip to content

Commit 2735122

Browse files
committed
update test suite readme
1 parent bd8d915 commit 2735122

2 files changed

Lines changed: 118 additions & 11 deletions

File tree

surplus-test/README.md

Lines changed: 116 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,120 @@
11
# Surplus Test Suite
22

3-
A quick primer on how the tests work:
3+
The Surplus test suite is a [JSDOM](https://github.com/jsdom/jsdom)-based
4+
custom test harness that tests most aspects related to Surplus.
45

5-
- Write/edit tests in `src/tests/*.jsx`. **Do not edit `src/tests/*.compiled.js`.**
6-
- Run `cargo run -p surplus-test` to generated the compiled outputs. Run with `--check`
7-
to perform existence checks.
8-
- Check the generated output for correctness.
9-
- Run `cargo test` to actually run the tests.
6+
There are test 'suites' and 'cases' - for every suite, there exists
7+
a single `tests/<suite_name>.jsx` file, and one or more test cases
8+
in `tests/<suite_name>.<case_name>.jsx`. These are automatically
9+
build as `cargo test` cases.
1010

11-
It's not a perfect system, but it guards against compiler outputs being changed
12-
without expecting those changes.
11+
Suite names become `mod suite_name { /* cases */ }`. Thus, suite names
12+
_must_ be valid Rust `mod`ule identifiers.
13+
14+
The harness expects the `<suite_name>.jsx` file to `export default`
15+
an element (_not_ an element function) or fragment (or array)
16+
that is first appended to an otherwise empty HTML document's `<body>`.
17+
This occurs cleanly for each and every test case; no test case risks
18+
sharing any state from any other case.
19+
20+
Then, the case code is run. It's expected to be an asynchronous module;
21+
no exports necessary. It runs immediately after the element is mounted.
22+
23+
The typical `window`, `document`, etc. are made available to the context.
24+
Further, a number of assertion helpers are installed (after mounting,
25+
before test case code runs) that includes a `$()` function (which is
26+
a wrapper around `document.body.querySelectorAll()`, by default
27+
targeting the actual elements that were mounted) as well as a number
28+
of _intrusive_ `.prototype` functions useful for testing values.
29+
30+
This test case API can be seen and extended in `/harness-api.mjs`. As a rule,
31+
any equality checks use `.is()`, and all errors that propagate upward
32+
are caught, set as a `.cause` on a new error object providing context,
33+
and re-thrown.
34+
35+
The harness also outputs a lot of debugging information. Most of the time
36+
this information can be ignored, but it is printed in full if a test case
37+
fails. It includes the transpiled JSX output from Surplus, followed by
38+
any and all output from the test case - including stacktraces. These
39+
stacktraces will then include the exact, contextual cause for assertion
40+
failure due to the extended `.cause`-based assertions.
41+
42+
## Adding a Suite
43+
44+
Add a new "suite" (harness component, testing some aspect of Surplus)
45+
by adding a `some_suite.jsx` file in the `tests/` directory. It is automatically
46+
picked up by the `build.rs` of the `surplus-test` crate and will be included
47+
on next `cargo test`.
48+
49+
Every suite _must_ have at least one test case (see below);
50+
otherwise, `build.rs` will panic.
51+
52+
It should `export default` the component to mount (_not_ a function that returns
53+
an element).
54+
55+
For example:
56+
57+
```jsx
58+
const MyRootComponent = () => <div>My Root!</div>;
59+
60+
//export default MyRootComponent; // WRONG - exports the function, not an Element.
61+
export default <MyRootComponent />; // Correct
62+
```
63+
64+
While atypical of real applications, the harness also supports default exports
65+
that are fragments (`<>...</>`), and - by proxy - arrays (`[e1, e2, ...]`).
66+
All of them are appended to the `<body>`, in order, during the mount phase
67+
of the test case. They are not wrapped in any element, each becoming a _direct_
68+
child of `<body>`.
69+
70+
## Adding a Case
71+
72+
To add a case to a new or existing suite (see above), it _must_ be named
73+
`<suite_name>.some_case.js`, where `some_case` becomes the rust `#[test] fn some_case()`
74+
function name. Thus, case names _must_ be valid Rust function identifier names.
75+
76+
The code within each test case is guaranteed to be run after the corresponding
77+
suite elements have been compiled and mounted onto `<body>`. Test case code
78+
is run immediately, and can be `async`/`await`.
79+
80+
Further, a number of assertion helpers are included as part of this environment,
81+
installed _after_ the suite code has run and the exported element has been
82+
mounted.
83+
84+
This code is located in `harness-api.mjs`, and includes - among other things -
85+
the `$()` function and various intrusive `.prototype`-based assertion functions.
86+
87+
The `$()` function takes a CSS selector string and queries against the mounted
88+
component directly using `querySelectAll`. Unliked that method, it returns
89+
a _real_ array.
90+
91+
From there, various assertions can be made about primitive values, or against
92+
`Element` types, as method invocations - many of which are chainable. See
93+
the `harness-api.mjs` for all provided methods (and feel free to add any
94+
you feel are helpful but missing), and see the various existing test cases
95+
to see how they're most often used.
96+
97+
For example, to make sure that a `<div>Hello!</div>` only mounts a single
98+
`<div>` with the text `Hello!`, one might write:
99+
100+
```javascript
101+
$('*').only().isTag('div');
102+
$('div').only().text().is('Hello!');
103+
```
104+
105+
where `$('*')` recursively selects all elements, `$('div')` recursively
106+
selects all `<div>` elements, `.only()` ensures that exactly one item
107+
is returned (or fails with an error), `isTag()` checks the tag name
108+
of an element, `.text()` is a convenience function that
109+
returns the text content of an element, and `.is()` performs an
110+
equality check on steroids.
111+
112+
## Restrictions
113+
114+
Test cases and suite fixtures are limited to a single allowed import:
115+
116+
```javascript
117+
import S from '@surplus/s';
118+
```
119+
120+
No other imports are allowed, and will error if attempted.

surplus-test/tests/div.mount.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
$("*").length.is(1);
2-
$("div").length.is(1);
3-
$("div").only().text().is("Hello, world!");
1+
$('*').only().isTag('div');
2+
$('div').only().text().is('Hello, world!');

0 commit comments

Comments
 (0)