diff --git a/tests/components/basics.test.ts b/tests/components/basics.test.ts
index c644a4a5..90d63321 100644
--- a/tests/components/basics.test.ts
+++ b/tests/components/basics.test.ts
@@ -832,9 +832,7 @@ describe.skip("mount special cases", () => {
// fixture.appendChild(span);
// const w = new MyWidget();
// await w.mount(div);
-
// expect(fixture.innerHTML).toBe("
diff --git a/tests/components/lifecycle.test.ts b/tests/components/lifecycle.test.ts
index 6f8942e4..1954d99d 100644
--- a/tests/components/lifecycle.test.ts
+++ b/tests/components/lifecycle.test.ts
@@ -938,4 +938,35 @@ describe("lifecycle hooks", () => {
expect(mounted).toBe(true);
expect(created).toBe(true);
});
+
+ test.skip("two different call to mounted/willunmount should work", async () => {
+ /*const steps: string[] = [];
+ function useMyHook(i) {
+ onMounted(() => {
+ steps.push("hook:mounted" + i);
+ });
+ onWillUnmount(() => {
+ steps.push("hook:willunmount" + i);
+ });
+ }
+ class MyComponent extends Component {
+ static template = xml`
hey
`;
+ constructor() {
+ super();
+ useMyHook(1);
+ useMyHook(2);
+ }
+ }
+ const component = new MyComponent();
+ await component.mount(fixture);
+ expect(fixture.innerHTML).toBe("
hey
");
+ component.unmount();
+ expect(fixture.innerHTML).toBe("");
+ expect(steps).toEqual([
+ "hook:mounted1",
+ "hook:mounted2",
+ "hook:willunmount2",
+ "hook:willunmount1",
+ ]);*/
+ });
});
diff --git a/tests/components/reactivity.test.ts b/tests/components/reactivity.test.ts
index 8868012d..c64b8da8 100644
--- a/tests/components/reactivity.test.ts
+++ b/tests/components/reactivity.test.ts
@@ -1,7 +1,19 @@
describe.skip("reactivity in lifecycle", () => {
+ test.skip("can use a state hook", async () => {
+ /* class Counter extends Component {
+ static template = xml`
`;
+ counter = useState({ value: 42 });
+ }
+ const counter = new Counter();
+ await counter.mount(fixture);
+ expect(fixture.innerHTML).toBe("
42
");
+ counter.counter.value = 3;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("
3
");*/
+ });
+
test("state changes in willUnmount do not trigger rerender", async () => {
// const steps: string[] = [];
-
// class Child extends Component {
// static template = xml`
//
@@ -17,7 +29,6 @@ describe.skip("reactivity in lifecycle", () => {
// patched() {
// steps.push("patched");
// }
-
// willUnmount() {
// steps.push("willUnmount");
// this.state.n = 3;
@@ -32,7 +43,6 @@ describe.skip("reactivity in lifecycle", () => {
// static components = { Child };
// state = useState({ val: 1, flag: true });
// }
-
// const widget = await mount(Parent, { target: fixture });
// expect(steps).toEqual(["render"]);
// expect(fixture.innerHTML).toBe("
12
");
@@ -52,7 +62,6 @@ describe.skip("reactivity in lifecycle", () => {
// this.state.val = 3;
// }
// }
-
// const widget = new TestWidget();
// await widget.mount(fixture);
// expect(fixture.innerHTML).toBe("
1
");
@@ -78,13 +87,11 @@ describe.skip("reactivity in lifecycle", () => {
// }
// }
// TestWidget.prototype.__render = jest.fn(TestWidget.prototype.__render);
-
// const widget = new TestWidget();
// widget.state.val = 2;
// await widget.mount(fixture);
// expect(fixture.innerHTML).toBe("
2
");
// expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(1);
-
// // unmount and re-mount, as in this case, willStart won't be called, so it's
// // slightly different
// widget.unmount();
@@ -109,14 +116,12 @@ describe.skip("reactivity in lifecycle", () => {
// }
// TestWidget.prototype.__render = jest.fn(TestWidget.prototype.__render);
// TestWidget.prototype.__patch = jest.fn(TestWidget.prototype.__patch);
-
// const widget = new TestWidget();
// let prom = widget.mount(fixture);
// widget.state.val = 2;
// await prom;
// expect(fixture.innerHTML).toBe("
2
");
// expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(1);
-
// // unmount and re-mount, as in this case, willStart won't be called, so it's
// // slightly different
// widget.unmount();
@@ -134,10 +139,8 @@ describe.skip("reactivity in lifecycle", () => {
// static template = xml`
`;
// state = useState({ val: 1 });
// }
-
// const detachedDiv = document.createElement("div");
// const app = await mount(App, { target: detachedDiv });
-
// expect(detachedDiv.innerHTML).toBe("
1
");
// app.state.val = 2;
// await nextTick();
@@ -149,12 +152,9 @@ describe.skip("reactivity in lifecycle", () => {
// static template = xml`
`;
// state = useState({ val: 1 });
// }
-
// const detachedDiv = document.createElement("div");
// const app = await mount(App, { target: detachedDiv });
-
// expect(detachedDiv.innerHTML).toBe("
1
");
-
// app.destroy();
// app.state.val = 2;
// await nextTick();
@@ -173,24 +173,19 @@ describe.skip("reactivity in lifecycle", () => {
// child = this;
// }
// }
-
// class Parent extends Component {
// static components = { Child };
// static template = xml`
`;
// state = useState({ val: "P1" });
// }
-
// const parent = new Parent();
// await parent.mount(fixture);
// expect(fixture.innerHTML).toBe("
P1C1
");
-
// parent.unmount();
// expect(fixture.innerHTML).toBe("");
-
// parent.state.val = "P2";
// child.state.val = "C2";
-
// await parent.mount(fixture);
// expect(fixture.innerHTML).toBe("
P2C2
");
});
-})
\ No newline at end of file
+});
diff --git a/tests/qweb/attributes.test.ts b/tests/qweb/attributes.test.ts
index a10eb543..7f623003 100644
--- a/tests/qweb/attributes.test.ts
+++ b/tests/qweb/attributes.test.ts
@@ -382,7 +382,7 @@ describe("special cases for some specific html attributes/properties", () => {
test.skip("textarea with t-att-value", () => {
// render input with initial value
-/* qweb.addTemplate("test", `
`);
+ /* qweb.addTemplate("test", `
`);
const vnode1 = qweb.render("test", { v: "zucchini" });
const vnode2 = patch(document.createElement("textarea"), vnode1);
let elm = vnode2.elm as HTMLInputElement;
@@ -400,7 +400,7 @@ describe("special cases for some specific html attributes/properties", () => {
});
test.skip("select with t-att-value", () => {
-/* const template = `
+ /* const template = `
Potato
Tomato
diff --git a/tests/qweb/simple_templates.test.ts b/tests/qweb/simple_templates.test.ts
index e0ab3c25..dcbf9b68 100644
--- a/tests/qweb/simple_templates.test.ts
+++ b/tests/qweb/simple_templates.test.ts
@@ -126,7 +126,7 @@ describe("simple templates, mostly static", () => {
describe("loading templates", () => {
test.skip("can initialize qweb with a string", () => {
-/* const templates = `
+ /* const templates = `
jupiler
`;
@@ -152,4 +152,22 @@ describe("loading templates", () => {
qweb.addTemplates(data);
expect(Object.keys(qweb.templates)).toEqual([]);*/
});
-});
\ No newline at end of file
+});
+
+describe("global template registration", () => {
+ test.skip("can register template globally", () => {
+ // expect.assertions(5);
+ // let qweb = new QWeb();
+ // try {
+ // qweb.render("mytemplate");
+ // } catch (e) {
+ // expect(e.message).toMatch("Template mytemplate does not exist");
+ // }
+ // expect(qweb.templates.mytemplate).toBeUndefined();
+ // QWeb.registerTemplate("mytemplate", "global
");
+ // expect(qweb.templates.mytemplate).toBeDefined();
+ // const vnode = qweb.render("mytemplate");
+ // expect(vnode.sel).toBe("div");
+ // expect((vnode as any).children[0].text).toBe("global");
+ });
+});
diff --git a/tests/qweb/svg.test.ts b/tests/qweb/svg.test.ts
new file mode 100644
index 00000000..a3113efe
--- /dev/null
+++ b/tests/qweb/svg.test.ts
@@ -0,0 +1,22 @@
+describe("properly support svg", () => {
+ test.skip("add proper namespace to svg", () => {
+ // qweb.addTemplate(
+ // "test",
+ // ` `
+ // );
+ // expect(renderToString(qweb, "test")).toBe(
+ // ` `
+ // );
+ });
+
+ test.skip("add proper namespace to g tags", () => {
+ // this is necessary if one wants to use components in a svg
+ // qweb.addTemplate(
+ // "test",
+ // ` `
+ // );
+ // expect(renderToString(qweb, "test")).toBe(
+ // ` `
+ // );
+ });
+});
diff --git a/tests/qweb/t_call.test.ts b/tests/qweb/t_call.test.ts
index f1b0be29..4615c4c7 100644
--- a/tests/qweb/t_call.test.ts
+++ b/tests/qweb/t_call.test.ts
@@ -15,6 +15,13 @@ describe("t-call (template calling)", () => {
expect(context.renderToString("caller")).toBe("ok
");
});
+ test.skip("t-call, global templates", () => {
+ // QWeb.registerTemplate("abcd", '
');
+ // qweb.addTemplate("john", `desk `);
+ // const expected = "desk
";
+ // expect(trim(renderToString(qweb, "abcd"))).toBe(expected);
+ });
+
test("basic caller, no parent node", () => {
const context = new TestContext();
context.addTemplate("_basic-callee", `ok `);
diff --git a/tests/qweb/t_debug_log.test.ts b/tests/qweb/t_debug_log.test.ts
index 7c2eb92b..59c25b3b 100644
--- a/tests/qweb/t_debug_log.test.ts
+++ b/tests/qweb/t_debug_log.test.ts
@@ -14,6 +14,21 @@ describe("debugging", () => {
console.log = consoleLog;
});
+ test.skip("t-debug on sub template", () => {
+ // const consoleLog = console.log;
+ // console.log = jest.fn();
+ // qweb.addTemplates(`
+ //
+ // coucou
+ //
+ //
+ //
+ // `);
+ // qweb.render("test");
+ // expect(console.log).toHaveBeenCalledTimes(1);
+ // console.log = consoleLog;
+ });
+
test("t-log", () => {
const consoleLog = console.log;
console.log = jest.fn();
diff --git a/tests/qweb/t_foreach.test.ts b/tests/qweb/t_foreach.test.ts
index 61be04c9..11a0668d 100644
--- a/tests/qweb/t_foreach.test.ts
+++ b/tests/qweb/t_foreach.test.ts
@@ -23,6 +23,28 @@ describe("t-foreach", () => {
expect(renderToString(template)).toBe(expected);
});
+ test.skip("t-key on t-foreach", async () => {
+ // qweb.addTemplate(
+ // "test",
+ // `
+ //
+ //
+ //
+ //
+ //
`
+ // );
+ // let vnode = qweb.render("test", { things: [1, 2] });
+ // vnode = patch(document.createElement("div"), vnode);
+ // let elm = vnode.elm as HTMLElement;
+ // expect(elm.outerHTML).toBe("
");
+ // const first = elm.querySelectorAll("span")[0];
+ // const second = elm.querySelectorAll("span")[1];
+ // patch(vnode, qweb.render("test", { things: [2, 1] }));
+ // expect(elm.outerHTML).toBe("
");
+ // expect(first).toBe(elm.querySelectorAll("span")[1]);
+ // expect(second).toBe(elm.querySelectorAll("span")[0]);
+ });
+
test("simple iteration (in a node)", () => {
const template = `
diff --git a/tests/qweb/t_key.test.ts b/tests/qweb/t_key.test.ts
new file mode 100644
index 00000000..f6cc5515
--- /dev/null
+++ b/tests/qweb/t_key.test.ts
@@ -0,0 +1,22 @@
+describe.skip("t-key", () => {
+ test("can use t-key directive on a node", () => {
+ // qweb.addTemplate("test", `
`);
+ // expect(renderToString(qweb, "test", { beer: { id: 12, name: "Chimay Rouge" } })).toBe(
+ // "
Chimay Rouge
"
+ // );
+ });
+
+ test("t-key directive in a list", () => {
+ // qweb.addTemplate(
+ // "test",
+ // `
`
+ // );
+ // expect(
+ // renderToString(qweb, "test", {
+ // beers: [{ id: 12, name: "Chimay Rouge" }],
+ // })
+ // ).toMatchSnapshot();
+ });
+});
diff --git a/tests/reactivity.test.ts b/tests/reactivity.test.ts
index b98241c4..f22cf6d4 100644
--- a/tests/reactivity.test.ts
+++ b/tests/reactivity.test.ts
@@ -447,6 +447,50 @@ describe("observe", () => {
obj2.key = 3;
expect(n).toBe(1);
});
+
+ test.skip("call callback when state is changed", async () => {
+ /* const observer = new Observer();
+ observer.notifyCB = jest.fn();
+ const obj: any = observer.observe({ a: 1, b: { c: 2 }, d: [{ e: 3 }], f: 4 });
+
+ expect(observer.notifyCB).toBeCalledTimes(0);
+
+ obj.a = 2;
+ await nextMicroTick();
+ expect(observer.notifyCB).toBeCalledTimes(1);
+
+ obj.b.c = 3;
+ await nextMicroTick();
+ expect(observer.notifyCB).toBeCalledTimes(2);
+
+ obj.d[0].e = 5;
+ await nextMicroTick();
+ expect(observer.notifyCB).toBeCalledTimes(3);
+
+ obj.a = 111;
+ obj.f = 222;
+ await nextMicroTick();
+ expect(observer.notifyCB).toBeCalledTimes(5);*/
+ });
+
+ test.skip("throw error when state is mutated in object if allowMutation=false", async () => {
+ /*const observer = new Observer();
+ observer.allowMutations = false;
+ const obj: any = observer.observe({ a: 1 });
+ expect(() => {
+ obj.a = 2;
+ }).toThrow('Observed state cannot be changed here! (key: "a", val: "2")');*/
+ });
+
+ test.skip("throw error when state is mutated in array if allowMutation=false", async () => {
+ /* const observer = new Observer();
+ observer.allowMutations = false;
+ const obj: any = observer.observe({ a: [1] });
+
+ expect(() => {
+ obj.a.push(2);
+ }).toThrow('Observed state cannot be changed here! (key: "1", val: "2")');*/
+ });
});
describe("unobserve", () => {
diff --git a/tests/tooling/debug_script_1.test.ts b/tests/tooling/debug_script_1.test.ts
new file mode 100644
index 00000000..e98ee121
--- /dev/null
+++ b/tests/tooling/debug_script_1.test.ts
@@ -0,0 +1,52 @@
+/**
+ * We can only make one test per file, since the debug tool modify in place
+ * the owl object in a way that is difficult to undo.
+ */
+
+// import { debugOwl } from "../../tools/debug";
+// import * as owl from "../../src/index";
+
+// import { Component, Env } from "../../src/component/component";
+// import { xml } from "../../src/tags";
+// import { useState } from "../../src/hooks";
+// import { makeTestFixture, makeTestEnv, nextTick } from "../helpers";
+
+// let fixture: HTMLElement = makeTestFixture();
+// let env: Env = makeTestEnv();
+// Component.env = env;
+
+// debugOwl(owl, {});
+
+test.skip("can log full lifecycle", async () => {
+ // const steps: string[] = [];
+ // const log = console.log;
+ // console.log = (arg) => steps.push(arg);
+ // class Child extends Component {
+ // static template = xml`
child
`;
+ // }
+ // class Parent extends Component {
+ // static template = xml`
`;
+ // static components = { Child };
+ // state = useState({ flag: false });
+ // }
+ // const parent = new Parent(null, {});
+ // await parent.mount(fixture);
+ // parent.state.flag = true;
+ // await nextTick();
+ // expect(steps).toEqual([
+ // "[OWL_DEBUG] Parent
constructor, props={}",
+ // "[OWL_DEBUG] Parent mount",
+ // "[OWL_DEBUG] Parent willStart",
+ // "[OWL_DEBUG] Parent rendering template",
+ // "[OWL_DEBUG] Parent mounted",
+ // "[OWL_DEBUG] Parent render",
+ // "[OWL_DEBUG] Parent rendering template",
+ // "[OWL_DEBUG] Child constructor, props={}",
+ // "[OWL_DEBUG] Child willStart",
+ // "[OWL_DEBUG] Child rendering template",
+ // "[OWL_DEBUG] Parent willPatch",
+ // "[OWL_DEBUG] Child mounted",
+ // "[OWL_DEBUG] Parent patched",
+ // ]);
+ // console.log = log;
+});
diff --git a/tests/tooling/debug_script_2.test.ts b/tests/tooling/debug_script_2.test.ts
new file mode 100644
index 00000000..ed2bedd2
--- /dev/null
+++ b/tests/tooling/debug_script_2.test.ts
@@ -0,0 +1,46 @@
+/**
+ * We can only make one test per file, since the debug tool modify in place
+ * the owl object in a way that is difficult to undo.
+ */
+
+// import { debugOwl } from "../../tools/debug";
+// import * as owl from "../../src/index";
+
+// import { Component, Env } from "../../src/component/component";
+// import { xml } from "../../src/tags";
+// import { makeTestFixture, makeTestEnv } from "../helpers";
+
+// let fixture: HTMLElement = makeTestFixture();
+// let env: Env = makeTestEnv();
+// Component.env = env;
+
+// debugOwl(owl, { logScheduler: true });
+
+test.skip("can log scheduler start and stop", async () => {
+ // const steps: string[] = [];
+ // const log = console.log;
+ // console.log = (arg) => steps.push(arg);
+ // class Child extends Component {
+ // static template = xml`child
`;
+ // }
+ // class Parent extends Component {
+ // static template = xml`
`;
+ // static components = { Child };
+ // }
+ // const parent = new Parent(null, {});
+ // await parent.mount(fixture);
+ // expect(steps).toEqual([
+ // "[OWL_DEBUG] Parent constructor, props={}",
+ // "[OWL_DEBUG] Parent mount",
+ // "[OWL_DEBUG] Parent willStart",
+ // "[OWL_DEBUG] scheduler: start running tasks queue",
+ // "[OWL_DEBUG] Parent rendering template",
+ // "[OWL_DEBUG] Child constructor, props={}",
+ // "[OWL_DEBUG] Child willStart",
+ // "[OWL_DEBUG] Child rendering template",
+ // "[OWL_DEBUG] Child mounted",
+ // "[OWL_DEBUG] Parent mounted",
+ // "[OWL_DEBUG] scheduler: stop running tasks queue",
+ // ]);
+ // console.log = log;
+});
diff --git a/tests/tooling/debug_script_3.test.ts b/tests/tooling/debug_script_3.test.ts
new file mode 100644
index 00000000..af1f0c3c
--- /dev/null
+++ b/tests/tooling/debug_script_3.test.ts
@@ -0,0 +1,47 @@
+/**
+ * We can only make one test per file, since the debug tool modify in place
+ * the owl object in a way that is difficult to undo.
+ */
+
+// import { debugOwl } from "../../tools/debug";
+// import * as owl from "../../src/index";
+
+// import { Component, Env } from "../../src/component/component";
+// import { xml } from "../../src/tags";
+// import { makeTestFixture, makeTestEnv, nextTick } from "../helpers";
+
+// let fixture: HTMLElement = makeTestFixture();
+// let env: Env = makeTestEnv();
+// Component.env = env;
+
+// debugOwl(owl, { logScheduler: true });
+
+test.skip("log a specific message for render method calls if component is not mounted", async () => {
+ // const steps: string[] = [];
+ // const log = console.log;
+ // console.log = (arg) => steps.push(arg);
+ // class Parent extends Component {
+ // static template = xml`
`;
+ // state = owl.hooks.useState({ value: 1 });
+ // }
+ // const parent = new Parent(null, {});
+ // await parent.mount(fixture);
+ // parent.unmount();
+ // parent.state.value = 2;
+ // await nextTick();
+ // expect(steps).toEqual([
+ // "[OWL_DEBUG] Parent constructor, props={}",
+ // "[OWL_DEBUG] Parent mount",
+ // "[OWL_DEBUG] Parent willStart",
+ // "[OWL_DEBUG] scheduler: start running tasks queue",
+ // "[OWL_DEBUG] Parent rendering template",
+ // "[OWL_DEBUG] Parent mounted",
+ // "[OWL_DEBUG] scheduler: stop running tasks queue",
+ // "[OWL_DEBUG] Parent willUnmount",
+ // "[OWL_DEBUG] Parent render (warning: component is not mounted)",
+ // "[OWL_DEBUG] scheduler: start running tasks queue",
+ // "[OWL_DEBUG] Parent rendering template",
+ // "[OWL_DEBUG] scheduler: stop running tasks queue",
+ // ]);
+ // console.log = log;
+});
diff --git a/tests/tooling/debug_script_4.test.ts b/tests/tooling/debug_script_4.test.ts
new file mode 100644
index 00000000..339a9074
--- /dev/null
+++ b/tests/tooling/debug_script_4.test.ts
@@ -0,0 +1,52 @@
+/**
+ * We can only make one test per file, since the debug tool modify in place
+ * the owl object in a way that is difficult to undo.
+ */
+
+// import { debugOwl } from "../../tools/debug";
+// import * as owl from "../../src/index";
+
+// import { Component, Env } from "../../src/component/component";
+// import { xml } from "../../src/tags";
+// import { makeTestFixture, makeTestEnv } from "../helpers";
+
+// let fixture: HTMLElement = makeTestFixture();
+// let env: Env = makeTestEnv();
+// Component.env = env;
+
+// debugOwl(owl, { logScheduler: true });
+
+test.skip("log a sub component with non stringifiable props", async () => {
+ // const steps: string[] = [];
+ // const log = console.log;
+ // console.log = (arg) => steps.push(arg);
+ // class Child extends Component {
+ // static template = xml` `;
+ // }
+ // const circularObject: any = { val: 1 };
+ // circularObject.circularObject = circularObject;
+ // class Parent extends Component {
+ // static template = xml`
`;
+ // static components = { Child };
+ // obj = circularObject;
+ // }
+ // const parent = new Parent(null, {});
+ // await parent.mount(fixture);
+ // parent.unmount();
+ // expect(steps).toEqual([
+ // "[OWL_DEBUG] Parent constructor, props={}",
+ // "[OWL_DEBUG] Parent mount",
+ // "[OWL_DEBUG] Parent willStart",
+ // "[OWL_DEBUG] scheduler: start running tasks queue",
+ // "[OWL_DEBUG] Parent rendering template",
+ // "[OWL_DEBUG] Child constructor, props=",
+ // "[OWL_DEBUG] Child willStart",
+ // "[OWL_DEBUG] Child rendering template",
+ // "[OWL_DEBUG] Child mounted",
+ // "[OWL_DEBUG] Parent mounted",
+ // "[OWL_DEBUG] scheduler: stop running tasks queue",
+ // "[OWL_DEBUG] Parent willUnmount",
+ // "[OWL_DEBUG] Child willUnmount",
+ // ]);
+ // console.log = log;
+});
diff --git a/tests/tooling/doc_link_checker.test.ts b/tests/tooling/doc_link_checker.test.ts
new file mode 100644
index 00000000..39177db7
--- /dev/null
+++ b/tests/tooling/doc_link_checker.test.ts
@@ -0,0 +1,183 @@
+/**
+ * Doc Link Checker
+ *
+ * We define here a test to make sure that there are no dead link in the Owl
+ * documentation.
+ */
+// import * as fs from "fs";
+
+// //--------------------------------------------------------------------------
+// // Helpers
+// //--------------------------------------------------------------------------
+
+// interface MarkDownLink {
+// name: string;
+// link: string;
+// }
+
+// interface MarkDownSection {
+// name: string;
+// slug: string;
+// }
+
+// interface FileData {
+// name: string;
+// path: string[];
+// fullName: string;
+// links: MarkDownLink[];
+// sections: MarkDownSection[];
+// }
+
+// const LINK_REGEXP = /\[([^\[]+)\]\(([^\)]+)\)/g;
+// const HEADING_REGEXP = /\n(#+\s*)(.*)/g;
+
+// export function addMardownData(fileData): void {
+// const sep = fileData.path.length > 0 ? "/" : "";
+// const fullName = fileData.path.join("/") + sep + fileData.name;
+// const content = fs.readFileSync(fullName, { encoding: "utf8" });
+// let m;
+// // get links info
+// do {
+// m = LINK_REGEXP.exec(content);
+// if (m) {
+// fileData.links.push({ name: m[0], link: m[2] });
+// }
+// } while (m);
+// // get sections info
+// do {
+// m = HEADING_REGEXP.exec(content);
+// if (m) {
+// fileData.sections.push({ name: m[0], slug: slugify(m[2]) });
+// }
+// } while (m);
+// }
+
+// /**
+// * Returns a list of FileData corresponding to all files that need to be
+// * validated.
+// */
+// function getFiles(path: string[] = []): FileData[] {
+// if (path.length === 0) {
+// const baseFiles: FileData[] = [
+// { name: "README.md", path: [], links: [], sections: [], fullName: "README.md" },
+// { name: "roadmap.md", path: [], links: [], sections: [], fullName: "roadmap.md" },
+// ];
+// const rest = getFiles(["doc"]);
+// const result = baseFiles.concat(rest);
+// result.forEach(addMardownData);
+// return result;
+// }
+// const files = fs.readdirSync(path.join("/"), { withFileTypes: true }).map((f) => {
+// if (f.isDirectory()) {
+// return getFiles(path.concat(f.name));
+// }
+// const fullName = path.join("/") + (path.length > 0 ? "/" : "") + f.name;
+// return [
+// {
+// name: f.name,
+// path,
+// links: [],
+// sections: [],
+// fullName,
+// },
+// ];
+// });
+// return Array.prototype.concat(...files);
+// }
+
+// const LOCAL_FILES = ["LICENSE", "tools/debug.js"];
+// export function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean {
+// if (link.link.startsWith("http")) {
+// // no check on external links
+// return true;
+// }
+// // Step 1: extract path, name, hash
+// // path = ['doc', 'architecture]
+// // name = 'rendering.md'
+// // hash = 'blabla' (or '' if no hash)
+
+// const parts = link.link.split("#");
+// const hash = parts[1] || "";
+// let name;
+// let path;
+// if (parts[0]) {
+// let temp = parts[0].split("/");
+// name = temp[temp.length - 1];
+// temp.splice(-1);
+// path = current.path.slice();
+// for (let elem of temp) {
+// if (elem === "..") {
+// path.splice(-1);
+// } else if (elem !== ".") {
+// path.push(elem);
+// }
+// }
+// } else {
+// // there are no file name, so this is a relative link to the current file
+// name = current.name;
+// path = current.path;
+// }
+
+// // Step 2: build normalized link file name
+// const linkFullName = path.join("/") + (path.length > 0 ? "/" : "") + name;
+
+// // Step 3: check link name against white list of local files
+// if (LOCAL_FILES.includes(linkFullName)) {
+// return true;
+// }
+
+// // Step 4: check if there is a matching file
+// let target: FileData | undefined = files.find((f) => f.fullName === linkFullName);
+// if (!target) {
+// return false;
+// }
+
+// // Step 5: if necessary, check if there is a corresponding link inside the target
+// // link name
+// if (hash) {
+// if (!target.sections.find((s) => s.slug === hash)) {
+// return false;
+// }
+// }
+
+// return true;
+// }
+
+// // adapted from https://medium.com/@mhagemann/the-ultimate-way-to-slugify-a-url-string-in-javascript-b8e4a0d849e1
+// function slugify(str) {
+// const a = "àáäâãåăæçèéëêǵḧìíïîḿńǹñòóöôœøṕŕßśșțùúüûǘẃẍÿź·_,:;";
+// const b = "aaaaaaaaceeeeghiiiimnnnooooooprssstuuuuuwxyz-----";
+// const p = new RegExp(a.split("").join("|"), "g");
+// return str
+// .toString()
+// .toLowerCase()
+// .replace(/\//g, "") // remove /
+// .replace(/\s+/g, "-") // Replace spaces with -
+// .replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
+// .replace(/&/g, "-and-") // Replace & with ‘and’
+// .replace(/[^\w\-]+/g, "") // Remove all non-word characters
+// .replace(/\-\-+/g, "-") // Replace multiple - with single -
+// .replace(/^-+/, "") // Trim - from start of text
+// .replace(/-+$/, ""); // Trim - from end of text
+// }
+
+//--------------------------------------------------------------------------
+// Test
+//--------------------------------------------------------------------------
+
+test.skip("All markdown links work", () => {
+ // let linkNumber = 0;
+ // let invalidLinkNumber = 0;
+ // const data = getFiles();
+ // for (let file of data) {
+ // for (let link of file.links) {
+ // linkNumber++;
+ // if (!isLinkValid(link, file, data)) {
+ // console.warn(`Invalid Link: "${link.name}" in "${file.name}"`);
+ // invalidLinkNumber++;
+ // }
+ // }
+ // }
+ // expect(invalidLinkNumber).toBe(0);
+ // expect(linkNumber).toBeGreaterThan(10);
+});