aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorJacob Scearcy <jacobscearcy@gmail.com>2024-05-06 06:42:35 -0700
committerGitHub <noreply@github.com>2024-05-06 14:42:35 +0100
commit80e2bd66f54bca88a749d40784828d29bae8995f (patch)
tree587a8b2d24f09e1955f11ab1ae7ca1e16084d4e1 /test
parent8adbae91d3e7d526b5950e2299736ab915dc5489 (diff)
downloadlustre-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.js88
-rw-r--r--test/README.md29
-rw-r--r--test/utils.js29
-rw-r--r--test/vdom.ffi.bench.js27
-rw-r--r--test/vdom.ffi.test.js150
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);
+ });
+ });
+ });
+});
+