npm package: @srhazi/gooey-test
Description: An in-browser test runner
Author: Sufian Rhazi
Latest version: 0.8.0

## Description

gooey-test is an in-browser test-runner.


## Usage

TBD


## API

### Running and organizing tests

The following functions allow test authors to write and group tests together:

* `test(name, testBodyFn)` -- define a test function, which passes when it runs without raising an exception
* `suite(name, suiteBodyFn)` -- define a suite function, which groups tests and any associated initialization/cleanup together
* `beforeEach(initializeFn)` -- run an initialization function before each test (scoped to the suite)
* `afterEach(cleanupFn)` -- run a cleanup function after each test (scoped to the suite, regardless of test pass/failure)

Conceptually, a test file acts like a single suite, which contains any number of `test` functions. `suite`s added within
a test file define nested groups of tests. `suite`s may be nested with any number of depth.

Any of the functions passed to `test`, `beforeEach`, and `afterEach` may be async functions (or normal functions which
return promises).

Prior to having a test be run, each of the test's suite's `beforeEach` initializers (and all of its ancestor suites
`beforeEach` initializers) are executed in order, from outermost to innermost suite. After a test is run, regardless of
its passing or failure, each of the test's suite's `afterEach` initializers (and all of its ancestor suites `afterEach`
initializers) are executed in reverse order, from innermost to outermost suite.


### Asserting behavior

The `assert` export is an object which contains the following assertions. Each assertion function may be provided an
additional `msg` parameter, which will be displayed for additional context if the assertion fails.

* `assert.fail(msg?: string)` -- fail immediately
* `assert.is(a: any, b, any, msg?: string)` -- assert `a === b`
* `assert.isNot(a: any, b, any, msg?: string)` -- assert `a !== b`
* `assert.isTruthy(a: any, msg?: string)` -- assert `a` is truthy (`!!a === true`)
* `assert.isFalsy(a: any, msg?: string)` -- assert `a` is falsy (`!a === true`)
* `assert.lessThan(a: string | number, b: string | number, msg?: string)` -- assert `a < b`
* `assert.lessThanOrEqual(a: string | number, b: string | number, msg?: string)` -- assert `a <= b`
* `assert.greaterThan(a: string | number, b: string | number, msg?: string)` -- assert `a > b`
* `assert.greaterThanOrEqual(a: string | number, b: string | number, msg?: string)` -- assert `a >= b`
* `assert.arrayIs(a: any[], b: any[], msg?: string)` -- assert `a` and `b` have the same `length` and each corresponding item in `a` and `b` are equal via `aItem === bItem`
* `assert.arrayIncludes(haystack: any[], needle: any[], msg?: string)` -- assert `needle` exists within `haystack` (via reference equality) 
* `assert.notArrayIncludes(haystack: any[], needle: any[], msg?: string)` -- assert `needle` exists within `haystack` (via reference equality) 
* `assert.arrayEqualsUnsorted(a: any[], b: any[], msg?: string)` -- assert `a` has the same members as `b` (via reference equality)
* `assert.deepEqual(a: any, b: any, msg?: string)` -- assert `a` is deeply equal to `b` (via lodash's `isEqual`)
* `assert.notDeepEqual(a: any, b: any, msg?: string)` -- assert `a` is not deeply equal to `b` (via lodash's `isEqual`)
* `assert.assertionCount(num: number, msg?: string)` -- assert that the number of assertions performed prior to this
  call for this test is `num`
* `assert.throwsMatching(match: string | RegExp, fn: () => void, msg?: string)` -- assert `fn` throws an exception whose
  'message' property matches the provided `match`, which is a regular expression object or string containing a regular
  expression


### Using the DOM

Prior to running a test a single, empty `<div id="test-root"></div>` element is added to `document.body`. If this prior
created `#test-root` element exists, it will be removed prior to running any test.

Within a test, it should is safe to mutate/add event listeners to this `#test-root` element without performing any cleanup.

If you need to mutate DOM data that is outside of the `#test-root` element, you will manually need to clean up this
mutation to avoid leaking state between tests.


### Performance testing

This is an unstable API.

`assert.medianRuntimeLessThan(ms: number, fn: (measure: <T>(measurement: () => T) => T) => void, numRuns?: number, msg?: string)`

The `medianRuntimeLessThan` assertion can be used to ensure that a portion of a provided function takes less than `ms`
wallclock milliseconds to execute. An example:

```
test('performance test', async () => {
    await assert.medianRuntimeLessThan(16, (measure) => {
        const data = ...
        const renderedResult = measure(() => {
            return render(data);
        });
        cleanup(renderedResult);
    });
});
```

This test will measure the amount of time the function passed to `measure` takes less than 16 milliseconds to perform.
Any value returned by the function passed to `measure` will be returned by `measure` itself.

In addition to measuring wallclock time, memory usage is also measured. This is not (currently) assertable, but can be
viewed in the test output.

Note: the mechanism used for performance testing does not perform any error estimate calculation. Many would argue that
this makes measurements mostly worthless, they are correct. This will be fixed eventually.

Note: for better visibility into memory usage, pass the following flags to Chrome: `--js-flags="--expose-gc" --enable-precise-memory-info`
* On MacOS, you can do this via: `/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --js-flags="--expose-gc" --enable-precise-memory-info


## Mocking and spying

The author of this tool is a proponent of [classical
style](https://martinfowler.com/articles/mocksArentStubs.html#ClassicalAndMockistTesting) tests, and considers mocking
or spying on modules, properties, functions, or any sort of data access to be harmful in the long term. As such, this
library does not and will not support any form of this.