Testing ======= We take very seriously code quality. Tests is an important part of a solid and robust application. Each feature should be (if reasonable) tested properly, in the QUnit test suite (available in the route ``/wowl/tests`` ) In a few sentences, here is how the test suite is organized: * tests are written in the ``static/tests`` folder. * the main entry point for the suite is ``static/tests/main.ts`` , which is the code that setup the test suite environment. * some helpers are available in ``static/tests/helpers.ts``. * to access the test suite, one needs to have an odoo server running and then open ``/wowl/tests`` in a browser The test suite file structure looks like this: .. code-block:: static/ ... tests/ components/ navbar_tests.ts ... services/ router_tests.ts ... helpers.ts <-- helpers that will probably be imported in each test file main.ts <-- main file, used to generate the test bundle qunit.ts <-- qunit config file. No test should import this Test helpers ------------ * ``mount(SomeComponent, {env, target})`` : create a component with the ``env`` provided, and mount it to the ``target`` html element. This method is asynchronous, and returns the instance of the component * ``makeTestEnv(params?: Params)`` : create asynchronously a test environment. By default, a test environment has no service, components or browser access. It can be optionally customized with the ``params`` argument: * ``services (optional, Registry)`` : a registry of services * ``Components (optional, Registry)`` ) main component registry * ``browser (optional, object)`` : the browser object that will be used for the test environment. * ``getFixture(): HTMLElement`` : return an html element to use as a DOM node for tests (only applies to code that needs to interact with the DOM, obviously). * ``nextTick(): Promise`` : helper to wait for the next animation frame. This is extremely useful while writing tests involving components updating the DOM. * ``click(el: HTMLElement, selector?: string): Promise`` : helper to click on an element, given the element itself, or an element and a selector that matches a single element inside it. The helper will crash when called with a selector that have no match, or more than 1 match. It returns a promise resolved after the next animation frame, i.e. after potential DOM updates have been applied. QUnit custom assertions ----------------------- QUnit provides several `built-in assertions `_\ , available on the ``QUnit.assert`` object. Alongside them, we provide custom assertions. * ``assert.containsN(el: HTMLElement, selector: string, n: number, msg?: string)`` : check that a target element ``el`` contains exactly ``n`` matches for the given ``selector`` , with ``msg`` being an optional short description of the assertion * ``assert.containsNone(el: HTMLElement, selector: string, msg?: string)`` : check that a target element ``el`` contains no match for the given ``selector`` * ``assert.containsOnce(el: HTMLElement, selector: string, msg?: string)`` : check that a target element ``el`` contains exactly one match for the given ``selector`` * ``assert.hasClass(el: HTMLElement, classNames: string, msg?: string)`` : check that a target element ``el`` has the given ``classNames`` * ``assert.doesNotHaveClass(el: HTMLElement, classNames: string, msg?: string)`` : check that a target element ``el`` does not have the given ``classNames``. Adding a new test ----------------- To add a new test file to the QUnit suite, the following steps need to be done: #. create a new file named ``something_test.ts`` in the ``static/tests`` folder #. import your file in ``static/tests/main.ts`` (so it will be added to the test bundle) #. add your tests inside your new file. Here is a simple example of a test file to test a component: .. code-block:: ts import * as QUnit from "qunit"; import { MyComponent } from "../../src/components/..."; import { getFixture, makeTestEnv, mount, OdooEnv } from "../helpers"; let target: HTMLElement; let env: OdooEnv; QUnit.module("MyComponent", { beforeEach() { target = getFixture(); env = makeTestEnv(); } }); QUnit.test("can be rendered", async (assert) => { assert.expect(1); const myComponent = await mount(MyComponent, { env, target }); // perform some assertion/actions }); Debugging a test ---------------- Sometimes, changes in the code make tests fail. Understanding why assertions fail reading the logs might be tedious. Hopefully, one can use our custom ``QUnit.debug`` function instead of ``QUnit.test`` (basically, rename ``ŧest`` into ``debug`` for the failing test). With this, the target returned by ``getFixture`` will be ``document.body`` , so that what has been inserted into the DOM is visible, and can be directly interacted with.