Add timestamp, virtual corpus and actual URL to test output and notifications

Make failures easier to trace at a glance:

- Log a "Run started <ISO time> on <url> (virtual corpus: ...)" line at startup.
- Include the configured KORAP_VC in the has-hits test titles, so it shows in
  both the console output and the Slack/Nextcloud Talk notifications.
- Prefix Slack/Nextcloud Talk notifications with an ISO timestamp and echo the
  result to the console log.
- Report the actual page URL (the full search results URL with query and cq)
  in the console log, the Nextcloud Talk message and the Slack URL field, so a
  failing search can be reproduced immediately. Falls back to the instance URL
  for tests that never navigated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Change-Id: I6e372e12db7fe5fdc6f6987b1fc3c46981c333a0
diff --git a/test/korap-ui.js b/test/korap-ui.js
index aaf46c1..a0b0a79 100644
--- a/test/korap-ui.js
+++ b/test/korap-ui.js
@@ -61,6 +61,8 @@
                 height: 768,
                 deviceScaleFactor: 1,
             });
+            console.log(`Run started ${new Date().toISOString()} on ${KORAP_URL}` +
+                (KORAP_VC ? ` (virtual corpus: ${KORAP_VC})` : ''));
         } catch (error) {
             console.error('Failed to initialize Puppeteer browser:', error.message);
             
@@ -134,12 +136,25 @@
             const emoji = testPassed ? '✅' : '🚨';
             const status = testPassed ? 'passed' : 'failed';
             const color = testPassed ? 'good' : 'danger';
+            const timestamp = new Date().toISOString();
+
+            // Capture the actual page URL (e.g. the full search results URL with
+            // query and cq) so issues can be reproduced/traced immediately. Fall
+            // back to the instance URL for tests that never navigated (about:blank).
+            let currentUrl = KORAP_URL;
+            try {
+                const u = page && typeof page.url === 'function' ? page.url() : '';
+                if (/^https?:/.test(u)) currentUrl = u;
+            } catch (e) { /* keep KORAP_URL */ }
+
+            // Echo the result with its URL to the console log as well.
+            console.log(`${emoji} [${timestamp}] ${status}: ${this.currentTest.title} — ${currentUrl}`);
 
             // Send notification to Slack
             if (slack) {
                 try {
                     slack.alert({
-                        text: `${emoji} Test on ${KORAP_URL} ${status}: *${this.currentTest.title}*`,
+                        text: `${emoji} [${timestamp}] Test on ${KORAP_URL} ${status}: *${this.currentTest.title}*`,
                         attachments: [{
                             color: color,
                             fields: [{
@@ -148,8 +163,8 @@
                                 short: false
                             }, {
                                 title: 'URL',
-                                value: KORAP_URL,
-                                short: true
+                                value: currentUrl,
+                                short: false
                             }]
                         }]
                     });
@@ -184,7 +199,7 @@
 
             // Send notification to Nextcloud Talk with screenshot (if available)
             try {
-                const message = `${emoji} Test on ${KORAP_URL} ${status}: **${this.currentTest.title}**`;
+                const message = `${emoji} [${timestamp}] Test on ${KORAP_URL} ${status}: **${this.currentTest.title}**\n${currentUrl}`;
                 await sendToNextcloudTalk(message, false, screenshotPath);
             } catch (ncError) {
                 console.error('Failed to send notification to Nextcloud Talk:', ncError.message);
@@ -345,8 +360,9 @@
                 }
             })
 
+            const vcLabel = KORAP_VC ? ` in vc "${KORAP_VC}"` : '';
             KORAP_QUERIES.split(/[;,] */).forEach((query, i) => {
-                it('Search for "' + query + '" has hits',
+                it('Search for "' + query + '"' + vcLabel + ' has hits',
                     (async () => {
                         await korap_rc.assure_glimpse_off(page)
                         const hits = await korap_rc.search(page, query, { timeout: KORAP_SEARCH_TIMEOUT, vc: KORAP_VC })