« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/test/lib
diff options
context:
space:
mode:
Diffstat (limited to 'test/lib')
-rw-r--r--test/lib/content-function.js96
-rw-r--r--test/lib/index.js3
-rw-r--r--test/lib/wiki-data.js24
3 files changed, 108 insertions, 15 deletions
diff --git a/test/lib/content-function.js b/test/lib/content-function.js
index bb12be82..5cb499b1 100644
--- a/test/lib/content-function.js
+++ b/test/lib/content-function.js
@@ -1,5 +1,6 @@
 import * as path from 'node:path';
 import {fileURLToPath} from 'node:url';
+import {inspect} from 'node:util';
 
 import chroma from 'chroma-js';
 
@@ -90,27 +91,92 @@ export function testContentFunctions(t, message, fn) {
       t.matchSnapshot(result, description);
     };
 
-    evaluate.stubTemplate = name => {
+    evaluate.stubTemplate = name =>
       // Creates a particularly permissable template, allowing any slot values
       // to be stored and just outputting the contents of those slots as-are.
+      _stubTemplate(name, false);
 
-      return new (class extends html.Template {
-        #slotValues = {};
+    evaluate.stubContentFunction = name =>
+      // Like stubTemplate, but instead of a template directly, returns
+      // an object describing a content function - suitable for passing
+      // into evaluate.mock.
+      _stubTemplate(name, true);
 
-        constructor() {
-          super({
-            content: () => `${name}: ${JSON.stringify(this.#slotValues)}`,
-          });
-        }
+    const _stubTemplate = (name, mockContentFunction) => {
+      const inspectNicely = (value, opts = {}) =>
+        inspect(value, {
+          ...opts,
+          colors: false,
+          sort: true,
+        });
 
-        setSlots(slotNamesToValues) {
-          Object.assign(this.#slotValues, slotNamesToValues);
-        }
+      const makeTemplate = formatContentFn =>
+        new (class extends html.Template {
+          #slotValues = {};
 
-        setSlot(slotName, slotValue) {
-          this.#slotValues[slotName] = slotValue;
-        }
-      });
+          constructor() {
+            super({
+              content: () => this.#getContent(formatContentFn),
+            });
+          }
+
+          setSlots(slotNamesToValues) {
+            Object.assign(this.#slotValues, slotNamesToValues);
+          }
+
+          setSlot(slotName, slotValue) {
+            this.#slotValues[slotName] = slotValue;
+          }
+
+          #getContent(formatContentFn) {
+            const toInspect =
+              Object.fromEntries(
+                Object.entries(this.#slotValues)
+                  .filter(([key, value]) => value !== null));
+
+            const inspected =
+              inspectNicely(toInspect, {
+                breakLength: Infinity,
+                compact: true,
+                depth: Infinity,
+              });
+
+            return formatContentFn(inspected); `${name}: ${inspected}`;
+          }
+        });
+
+      if (mockContentFunction) {
+        return {
+          data: (...args) => ({args}),
+          generate: (data) =>
+            makeTemplate(slots => {
+              const argsLines =
+                (empty(data.args)
+                  ? []
+                  : inspectNicely(data.args, {depth: Infinity})
+                      .split('\n'));
+
+              return (`[mocked: ${name}` +
+
+                (empty(data.args)
+                  ? ``
+               : argsLines.length === 1
+                  ? `\n args: ${argsLines[0]}`
+                  : `\n args: ${argsLines[0]}\n` +
+                    argsLines.slice(1).join('\n').replace(/^/gm, ' ')) +
+
+                (!empty(data.args)
+                  ? `\n `
+                  : ` - `) +
+
+                (slots
+                  ? `slots: ${slots}]`
+                  : `slots: none]`));
+            }),
+        };
+      } else {
+        return makeTemplate(slots => `${name}: ${slots}`);
+      }
     };
 
     evaluate.mock = (...opts) => {
diff --git a/test/lib/index.js b/test/lib/index.js
index b9cc82f8..5fb5bf78 100644
--- a/test/lib/index.js
+++ b/test/lib/index.js
@@ -1,3 +1,6 @@
+Error.stackTraceLimit = Infinity;
+
 export * from './content-function.js';
 export * from './generic-mock.js';
+export * from './wiki-data.js';
 export * from './strict-match-error.js';
diff --git a/test/lib/wiki-data.js b/test/lib/wiki-data.js
new file mode 100644
index 00000000..c4083a56
--- /dev/null
+++ b/test/lib/wiki-data.js
@@ -0,0 +1,24 @@
+import {linkWikiDataArrays} from '#yaml';
+
+export function linkAndBindWikiData(wikiData) {
+  linkWikiDataArrays(wikiData);
+
+  return {
+    // Mutate to make the below functions aware of new data objects, or of
+    // reordering the existing ones. Don't mutate arrays such as trackData
+    // in-place; assign completely new arrays to this wikiData object instead.
+    wikiData,
+
+    // Use this after you've mutated wikiData to assign new data arrays.
+    // It'll automatically relink everything on wikiData so all the objects
+    // are caught up to date.
+    linkWikiDataArrays:
+      linkWikiDataArrays.bind(null, wikiData),
+
+    // Use this if you HAVEN'T mutated wikiData and just need to decache
+    // indirect dependencies on exposed properties of other data objects.
+    // See documentation on linkWikiDataArarys (in yaml.js) for more info.
+    XXX_decacheWikiData:
+      linkWikiDataArrays.bind(null, wikiData, {XXX_decacheWikiData: true}),
+  };
+}