« get me outta code hell

data steps: basic custom mocking function support - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/test/lib/content-function.js
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2023-03-27 12:47:04 -0300
committer(quasar) nebula <qznebula@protonmail.com>2023-03-27 12:47:04 -0300
commitc6f1011722dc6fe50afb3a63ee414c70dbfd6abf (patch)
treed13235a4b37e8264a1fbccdfad12009f7a3a4f6d /test/lib/content-function.js
parentcb13d591c6965dc52d89ec4d1e10558e6b22456b (diff)
data steps: basic custom mocking function support
I checked out a few libraries but none really behaved
the way I needed, and coding it myself means much lower-
level access, which makes certain options a lot easier
(e.g. excluding one argument of a mocked function from
assertion while matching the rest against a pattern).
Diffstat (limited to 'test/lib/content-function.js')
-rw-r--r--test/lib/content-function.js93
1 files changed, 85 insertions, 8 deletions
diff --git a/test/lib/content-function.js b/test/lib/content-function.js
index b51f2847..21af0e5a 100644
--- a/test/lib/content-function.js
+++ b/test/lib/content-function.js
@@ -7,11 +7,15 @@ import urlSpec from '../../src/url-spec.js';
 import {getColors} from '../../src/util/colors.js';
 import {generateURLs} from '../../src/util/urls.js';
 
+import mock from './generic-mock.js';
+
 export function testContentFunctions(t, message, fn) {
   const urls = generateURLs(urlSpec);
 
   t.test(message, async t => {
-    const loadedContentDependencies = await quickLoadContentDependencies();
+    let loadedContentDependencies;
+
+    const mocks = [];
 
     const evaluate = ({
       from = 'localized.home',
@@ -19,9 +23,13 @@ export function testContentFunctions(t, message, fn) {
       extraDependencies = {},
       ...opts
     }) => {
+      if (!loadedContentDependencies) {
+        throw new Error(`Await .load() before performing tests`);
+      }
+
       const {to} = urls.from(from);
 
-      try {
+      return cleanCatchAggregate(() => {
         return quickEvaluate({
           ...opts,
           contentDependencies: {
@@ -37,19 +45,88 @@ export function testContentFunctions(t, message, fn) {
             ...extraDependencies,
           },
         });
-      } catch (error) {
-        if (error instanceof AggregateError) {
-          error = new Error(`AggregateError: ${error.message}\n${error.errors.map(err => `** ${err}`).join('\n')}`);
-        }
-        throw error;
+      });
+    };
+
+    evaluate.load = async (opts) => {
+      if (loadedContentDependencies) {
+        throw new Error(`Already loaded!`);
       }
+
+      loadedContentDependencies = await asyncCleanCatchAggregate(() =>
+        quickLoadContentDependencies(opts));
     };
 
     evaluate.snapshot = (opts, fn) => {
+      if (!loadedContentDependencies) {
+        throw new Error(`Await .load() before performing tests`);
+      }
+
       const result = (fn ? fn(evaluate(opts)) : evaluate(opts));
       t.matchSnapshot(result.toString(), 'output');
     };
 
-    return fn(t, evaluate);
+    evaluate.mock = (...opts) => {
+      const {value, close} = mock(...opts);
+      mocks.push({close});
+      return value;
+    };
+
+    await fn(t, evaluate);
+
+    if (mocks.length) {
+      cleanCatchAggregate(() => {
+        const errors = [];
+        for (const {close} of mocks) {
+          try {
+            close();
+          } catch (error) {
+            errors.push(error);
+          }
+        }
+        if (errors.length) {
+          throw new AggregateError(errors, `Errors closing mocks`);
+        }
+      });
+    }
   });
 }
+
+function cleanAggregate(error) {
+  if (error instanceof AggregateError) {
+    return new Error(`[AggregateError: ${error.message}\n${
+      error.errors
+        .map(cleanAggregate)
+        .map(err => ` * ${err.message.split('\n').map((l, i) => (i > 0 ? '   ' + l : l)).join('\n')}`)
+        .join('\n')}]`);
+  } else {
+    return error;
+  }
+}
+
+function printAggregate(error) {
+  if (error instanceof AggregateError) {
+    const {message} = cleanAggregate(error);
+    for (const line of message.split('\n')) {
+      console.error(line);
+    }
+  }
+}
+
+function cleanCatchAggregate(fn) {
+  try {
+    return fn();
+  } catch (error) {
+    printAggregate(error);
+    throw error;
+  }
+}
+
+async function asyncCleanCatchAggregate(fn) {
+  try {
+    return await fn();
+  } catch (error) {
+    printAggregate(error);
+    throw error;
+  }
+}