From 46583f35a0cc18c37503049713519bde6597a9e6 Mon Sep 17 00:00:00 2001
From: shadowusr <nikhakerlab@gmail.com>
Date: Thu, 13 Jun 2024 17:15:30 +0300
Subject: [PATCH] fix: resolve file field correctly for exports mocha interface

---
 .../mocha-reader/tree-builder-decorator.js    |  5 +-
 src/test-reader/mocha-reader/utils.js         | 64 +++++++++++++++++++
 2 files changed, 68 insertions(+), 1 deletion(-)
 create mode 100644 src/test-reader/mocha-reader/utils.js

diff --git a/src/test-reader/mocha-reader/tree-builder-decorator.js b/src/test-reader/mocha-reader/tree-builder-decorator.js
index 4c775ccc4..27cad49de 100644
--- a/src/test-reader/mocha-reader/tree-builder-decorator.js
+++ b/src/test-reader/mocha-reader/tree-builder-decorator.js
@@ -1,5 +1,6 @@
 const { Suite, Test, Hook } = require("../test-object");
 const crypto = require("../../utils/crypto");
+const { computeFile } = require("./utils");
 
 class TreeBuilderDecorator {
     #treeBuilder;
@@ -17,7 +18,9 @@ class TreeBuilderDecorator {
     }
 
     addSuite(mochaSuite) {
-        const { id: mochaId, file } = mochaSuite;
+        const { id: mochaId } = mochaSuite;
+        const file = computeFile(mochaSuite) ?? "unknown-file";
+
         const positionInFile = this.#suiteCounter.get(file) || 0;
         const id = mochaSuite.root ? mochaId : crypto.getShortMD5(file) + positionInFile;
         const suite = this.#mkTestObject(Suite, mochaSuite, { id });
diff --git a/src/test-reader/mocha-reader/utils.js b/src/test-reader/mocha-reader/utils.js
new file mode 100644
index 000000000..eac1aadce
--- /dev/null
+++ b/src/test-reader/mocha-reader/utils.js
@@ -0,0 +1,64 @@
+// When using "exports" mocha interface, "file" field is absent on suites, and available on tests only.
+// This helper tries to resolve "file" field for suites, drilling down to child tests and using their file field.
+
+const findTopmostSuite = mochaSuite => {
+    if (mochaSuite.parent && mochaSuite.parent.root) {
+        return mochaSuite;
+    }
+
+    if (!mochaSuite.parent) {
+        return null;
+    }
+
+    return findTopmostSuite(mochaSuite.parent);
+};
+
+const getFile = mochaSuite => {
+    if (mochaSuite.file) {
+        return mochaSuite.file;
+    }
+
+    if (mochaSuite.tests.length > 0 && mochaSuite.tests[0].file) {
+        return mochaSuite.tests[0].file;
+    }
+
+    for (const childSuite of mochaSuite.suites) {
+        const computedFile = getFile(childSuite);
+        if (computedFile) {
+            return computedFile;
+        }
+    }
+
+    return null;
+};
+
+const fillSuitesFileField = (mochaSuite, file) => {
+    mochaSuite.file = file;
+
+    if (mochaSuite.suites) {
+        for (const childSuite of mochaSuite.suites) {
+            fillSuitesFileField(childSuite, file);
+        }
+    }
+};
+
+const computeFile = mochaSuite => {
+    if (mochaSuite.file) {
+        return mochaSuite.file;
+    }
+
+    const topmostSuite = findTopmostSuite(mochaSuite);
+    const file = topmostSuite && getFile(topmostSuite);
+
+    if (topmostSuite && file) {
+        fillSuitesFileField(topmostSuite, file);
+
+        return file;
+    }
+
+    return null;
+};
+
+module.exports = {
+    computeFile,
+};