a3dbceed952bbbfbd67e73148714bb3a828a0ec8
chmalee
  Mon Sep 21 15:24:52 2026 -0700
Add some uiTests for hubspace, mostly checking that file metadata editing works when a hub.txt is in a batch. This test does not actually test uploading anything, refs #38398

diff --git src/hg/hgHubConnect/tests/t01-hubTxtGenome.js src/hg/hgHubConnect/tests/t01-hubTxtGenome.js
new file mode 100644
index 00000000000..693bb1e244d
--- /dev/null
+++ src/hg/hgHubConnect/tests/t01-hubTxtGenome.js
@@ -0,0 +1,80 @@
+// hubSpace: a hub.txt in the upload batch decides the genome, and nothing
+// downstream may put the session default back.
+//
+// Nothing here is uploaded. The checks read uppy's own file list, which is the
+// metadata the upload would carry, so the run leaves nothing on the server and
+// needs no write permission.
+
+'use strict';
+
+const hub = require('./pages/hgHubConnect');
+
+// A real UCSC assembly that is not the session default, which is what the bug
+// needed: with hg38 on both sides there is nothing to tell apart.
+const GENOME = 'mm10';
+const HUB_NAME = 'uiTestHubTxtGenome';
+const HUB_TXT = [
+    `hub ${HUB_NAME}`,
+    'shortLabel uiTest hub',
+    'longLabel uiTest hub for the hub.txt genome checks',
+    'useOneFile on',
+    `genome ${GENOME}`,
+    '',
+    'track uiTestTrack',
+    'shortLabel uiTest track',
+    'longLabel uiTest track',
+    'type bigWig',
+    'bigDataUrl uiTest.bw',
+    '',
+].join('\n');
+
+module.exports.needs = { login: true, hgsql: false, write: false };
+
+module.exports.main = async function (p, t) {
+    const env = t.env;
+
+    await hub.openUploadTab(p, env);
+    if (!await hub.hasUploadTab(p)) {
+        t.skipped('hub.txt names the genome',
+            `${env.base} has no Hub Upload tab, so storeUserFiles is off there`);
+        return;
+    }
+    await hub.openDashboard(p);
+    await hub.addFile(p, 'hub.txt', HUB_TXT);
+
+    await t.check('uppy takes the genome from hub.txt', async () => {
+        const meta = await hub.metaOf(p, 'hub.txt');
+        if (!meta) {
+            return 'hub.txt is not in uppy\'s file list at all';
+        }
+        return meta.genome === GENOME ? null
+            : `uppy has genome "${meta.genome}", and hub.txt says "${GENOME}"`;
+    });
+
+    await t.check('the file card opens on the genome hub.txt named', async () => {
+        // The card copies file.meta when it opens, so opening it before the
+        // hub.txt has been read is what makes the save below wrong (#38367).
+        await hub.waitForFileCard(p);
+        const shown = await hub.cardGenome(p);
+        return shown === GENOME ? null
+            : `the card shows genome "${shown}", and hub.txt says "${GENOME}"`;
+    });
+
+    await t.check('saving the file card keeps the genome from hub.txt', async () => {
+        await hub.saveFileCard(p);
+        const meta = await hub.metaOf(p, 'hub.txt');
+        if (!meta) {
+            return 'hub.txt left uppy\'s file list when the card was saved';
+        }
+        return meta.genome === GENOME ? null
+            : `the card wrote genome "${meta.genome}" back over "${GENOME}", so ` +
+              `the upload would be rejected for the wrong assembly`;
+    });
+
+    await t.check('saving the file card keeps the hub name from hub.txt', async () => {
+        const meta = await hub.metaOf(p, 'hub.txt');
+        return meta && meta.parentDir === HUB_NAME ? null
+            : `the file is headed for hub "${meta ? meta.parentDir : '(gone)'}", ` +
+              `and hub.txt names "${HUB_NAME}"`;
+    });
+};