diff options
author | Jacob Scearcy <jacobscearcy@gmail.com> | 2024-05-06 06:42:35 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-06 14:42:35 +0100 |
commit | 80e2bd66f54bca88a749d40784828d29bae8995f (patch) | |
tree | 587a8b2d24f09e1955f11ab1ae7ca1e16084d4e1 /test | |
parent | 8adbae91d3e7d526b5950e2299736ab915dc5489 (diff) | |
download | lustre-80e2bd66f54bca88a749d40784828d29bae8995f.tar.gz lustre-80e2bd66f54bca88a749d40784828d29bae8995f.zip |
๐ Use vitest for runtime/vdom testing. (#124)
* ๐งช move tests into test directory, bump birdie to ignore non-gleam files
* implement feedback
* add comments, update doc
Diffstat (limited to 'test')
-rw-r--r-- | test/02-interactivity.test.js | 88 | ||||
-rw-r--r-- | test/README.md | 29 | ||||
-rw-r--r-- | test/utils.js | 29 | ||||
-rw-r--r-- | test/vdom.ffi.bench.js | 27 | ||||
-rw-r--r-- | test/vdom.ffi.test.js | 150 |
5 files changed, 323 insertions, 0 deletions
diff --git a/test/02-interactivity.test.js b/test/02-interactivity.test.js new file mode 100644 index 0000000..3355e94 --- /dev/null +++ b/test/02-interactivity.test.js @@ -0,0 +1,88 @@ +import { beforeEach, describe, expect, test } from "vitest"; +import { setupDOM } from "./utils.js"; +// built via npm script "build:test:02" +import { main } from "@root/examples/02-interactivity/build/dev/javascript/app/app.mjs"; + +let appEl; +beforeEach(() => { + setupDOM(); + appEl = document.getElementById("app"); +}); + +describe("counter example", () => { + test("should render initially", () => { + main(); + + expect(document.toString()).toMatchSnapshot(); + }); + + test("should increment on button press", () => { + main(); + + const buttons = document.querySelectorAll("button.lustre-ui-button"); + const incrementButton = buttons[0]; + const count = document.querySelector("p"); + + expect(incrementButton).toBeTruthy(); + + incrementButton.click(); + + expect(count.innerText).toBe("1"); + + incrementButton.click(); + expect(count.innerText).toBe("2"); + + incrementButton.click(); + expect(count.innerText).toBe("3"); + + }); + + test("should decrement on button press", () => { + main(); + + const buttons = document.querySelectorAll("button.lustre-ui-button"); + const decrementButton = buttons[1]; + const count = document.querySelector("p"); + + expect(decrementButton).toBeTruthy(); + + decrementButton.click(); + expect(count.innerText).toBe("-1"); + + decrementButton.click(); + expect(count.innerText).toBe("-2"); + + decrementButton.click(); + expect(count.innerText).toBe("-3"); + }); + + test("should increment and decrement on button press", () => { + main(); + + const buttons = document.querySelectorAll("button.lustre-ui-button"); + const incrementButton = buttons[0]; + const decrementButton = buttons[1]; + const count = document.querySelector("p"); + + incrementButton.click(); + + expect(count.innerText).toBe("1"); + + incrementButton.click(); + expect(count.innerText).toBe("2"); + + incrementButton.click(); + expect(count.innerText).toBe("3"); + + expect(decrementButton).toBeTruthy(); + + decrementButton.click(); + expect(count.innerText).toBe("2"); + + decrementButton.click(); + expect(count.innerText).toBe("1"); + + decrementButton.click(); + expect(count.innerText).toBe("0"); + }); +}); diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..6577c5c --- /dev/null +++ b/test/README.md @@ -0,0 +1,29 @@ +# Client testing for Lustre runtime + +1. Build and test example projects +2. Build and test vdom + +Depends on: +- `linkedom` - headless DOM testing +- `npm-run-all` - run watch in parallel +- `vitest` - execute tests + + +### Commands + +Run from the `test` directory +Each command will run a `build` command to build project dependencies + +#### Benchmark + +- `npm run bench` + +#### Test + +- ##### Single + + - `npm run test` + +- ##### Watch + + - `npm run test:watch` diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..c3abaee --- /dev/null +++ b/test/utils.js @@ -0,0 +1,29 @@ +import { parseHTML } from 'linkedom'; +import { vi } from 'vitest'; + +// Parse the starting state of the basic starting template +export function setupDOM() { + const result = parseHTML(` +<!doctype html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + + <title>๐ง {app_name}</title> + + </head> + + <body> + <div id="app"></div> + </body> +</html>`); + + global.HTMLElement = result.HTMLElement; + global.Node = result.Node; + global.document = result.document; + global.window = result.window; + global.window.requestAnimationFrame = vi.fn().mockImplementation((cb) => cb()); + + return result; +}
\ No newline at end of file diff --git a/test/vdom.ffi.bench.js b/test/vdom.ffi.bench.js new file mode 100644 index 0000000..a8e139e --- /dev/null +++ b/test/vdom.ffi.bench.js @@ -0,0 +1,27 @@ +import { bench, describe } from "vitest"; +import { setupDOM } from "./utils"; +import { morph } from "../src/vdom.ffi.mjs"; +import { smoke_test } from "../test-apps/vdom-test-templates/build/dev/javascript/app/client_test.mjs"; + +// BENCH ------------------------------------------------------------------------ + +describe("vdom morph bench", () => { + let appEl; + let template; + bench( + "smoke test morph", + () => { + appEl = morph(appEl, template); + }, + { + setup: () => { + const result = setupDOM(); + + global.Node = result.Node; + global.document = result.document; + appEl = document.getElementById("app"); + template = smoke_test(); + } + } + ); +}); diff --git a/test/vdom.ffi.test.js b/test/vdom.ffi.test.js new file mode 100644 index 0000000..2614c71 --- /dev/null +++ b/test/vdom.ffi.test.js @@ -0,0 +1,150 @@ +import { beforeEach, describe, expect, test } from "vitest"; +import { setupDOM } from "./utils.js"; + +import { morph } from "@root/src/vdom.ffi.mjs"; + +// built via npm script "build:test:vdom" +import { + disabled_attr_test, + dynamic_content_test, + fragment_test, + keyed_test, + smoke_test, +} from "../test-apps/vdom-test-templates/build/dev/javascript/app/client_test.mjs"; + +let appEl; +beforeEach(() => { + setupDOM(); + appEl = document.getElementById("app"); +}); + +// TEST ------------------------------------------------------------------------ + +const singleMorphSnapshot = (name, template) => { + appEl = morph(appEl, template); + + const currentState = document.toString(); + + expect(currentState).toMatchSnapshot(name); +}; + +describe("vdom morph", () => { + test(`should render smoke test with vdom morph`, () => { + const template = smoke_test(); + + singleMorphSnapshot("smoke_test", template); + }); + + test(`should render using vdom morph with fragments`, () => { + const template = fragment_test(); + + singleMorphSnapshot("fragment_test", template); + }); + + test(`should render using vdom morph with keys`, () => { + const template = keyed_test(); + + singleMorphSnapshot("fragment_test", template); + }); + + test(`should be stable when vdom morph is called multiple times with no changes using fragment`, () => { + const template = fragment_test(); + appEl = morph(appEl, template); + + const initialState = document.toString(); + + const states = []; + for (let i = 0; i < 5; i++) { + appEl = morph(appEl, template); + states.push(document.toString()); + } + + states.forEach((state) => { + expect(state).toEqual(initialState); + }); + }); + + test(`should be stable when vdom morph is called multiple times with no changes using keys`, () => { + const template = keyed_test(); + appEl = morph(appEl, template); + + const initialState = document.toString(); + + const states = []; + for (let i = 0; i < 5; i++) { + appEl = morph(appEl, template); + states.push(document.toString()); + } + + states.forEach((state) => { + expect(state).toEqual(initialState); + }); + }); + + test(`should render updated templates`, () => { + const initialTemplate = dynamic_content_test(0, "initial_name"); + + appEl = morph(appEl, initialTemplate); + + const initialState = document.toString(); + + expect(initialState).toContain("0"); + expect(initialState).toContain("initial_name"); + + const updatedtemplate = dynamic_content_test(56, "updated_name"); + + appEl = morph(appEl, updatedtemplate); + + const updatedState = document.toString(); + + expect(updatedState).toContain("56"); + expect(updatedState).toContain("updated_name"); + }); +}); + +describe("vdom morph attribute", () => { + describe("disabled", () => { + test("should not be disabled when is_disabled is false", () => { + const template = disabled_attr_test(false); + + appEl = morph(appEl, template); + + const domResult = document.toString(); + + expect(domResult).toContain("input"); + expect(domResult).not.toContain("disabled"); + }); + + test("should be disabled when is_disabled is true", () => { + const template = disabled_attr_test(true); + + appEl = morph(appEl, template); + + const domResult = document.toString(); + + expect(domResult).toContain("input"); + expect(domResult).toContain("disabled"); + }); + + + // this fails today + test.skip("should be stable when disabled attribute does not change", () => { + const template = disabled_attr_test(true); + + appEl = morph(appEl, template); + + const initialState = document.toString(); + + const states = []; + for (let i = 0; i < 5; i++) { + appEl = morph(appEl, template); + states.push(document.toString()); + } + + states.forEach((state) => { + expect(state).toEqual(initialState); + }); + }); + }); +}); + |