const assert = require("assert"); const fs = require("fs"); const path = require("path"); const ejs = require("ejs"); const root = path.join(__dirname, ".."); const sandbox = fs.mkdtempSync(path.join(root, ".tmp-lumi-feedback-test-")); const serviceDir = path.join(sandbox, "src", "services"); fs.mkdirSync(serviceDir, { recursive: true }); for (const file of ["db.js", "feedback.js"]) { fs.copyFileSync(path.join(root, "src", "services", file), path.join(serviceDir, file)); } let database = null; try { database = require(path.join(serviceDir, "db.js")); database.migrate(); const feedback = require(path.join(serviceDir, "feedback.js")); const db = database.db; const now = Date.now(); db.prepare( "INSERT INTO user_profiles (id, internal_username, created_at, updated_at) VALUES (?, ?, ?, ?)" ).run("user-1", "FeedbackUser", now, now); db.prepare( "INSERT INTO user_profiles (id, internal_username, created_at, updated_at) VALUES (?, ?, ?, ?)" ).run("admin-1", "AdminUser", now, now); db.prepare( "INSERT INTO user_profiles (id, internal_username, created_at, updated_at) VALUES (?, ?, ?, ?)" ).run("user-2", "SecondUser", now, now); const entry = feedback.createFeedback({ summary: "Save button fails", category: "broken_interaction", severity: "broken", scope_type: "element", scope_label: "Save settings button", description: "Clicking the save button does not apply the changes.", steps_to_reproduce: "Open settings, change a field, click save.", expected_behavior: "The settings should be saved.", actual_behavior: "The page reloads without saving.", current_url: "http://localhost/admin/settings", page_title: "Settings", target_metadata: { selector: "#save-settings", tag: "button", text: "Save settings", secret: "must not persist" }, diagnostics: { user_agent: "verification-agent", viewport: "1200x800", screenshot_mode: "target", hidden: "must not persist" } }, { id: "user-1" }, { screenshot: { path: "feedback/screenshots/test-shot.png", mime: "image/png", size: 2048 } }); assert.equal(entry.status, "new"); assert.equal(entry.scope_label, "Clicked element: Save settings"); assert.equal(entry.target_metadata.secret, undefined); assert.equal(entry.diagnostics.hidden, undefined); assert.equal(entry.screenshot.mime, "image/png"); assert.equal(feedback.listPublicFeedback({ userId: "user-1" })[0].is_mine, true); assert.equal(feedback.listMyFeedback("user-1").length, 1); assert.equal(feedback.supportFeedback(entry.id, { id: "user-2" }), 1); assert.equal(feedback.listPublicFeedback({ userId: "user-2" })[0].supported_by_me, true); feedback.addSubmitterComment(entry.id, "This also happens after a hard reload.", { id: "user-1" }); feedback.addSubmitterComment(entry.id, "I see this too on the same settings page.", { id: "user-2" }); const withComment = feedback.getFeedbackForSubmitter(entry.id, "user-1"); assert.equal(withComment.comments.length, 2); assert.equal(withComment.comments[0].kind, "submitter_comment"); assert.equal(withComment.comments[1].kind, "public_comment"); const publicViewerEntry = feedback.getFeedbackForViewer(entry.id, "user-2"); assert.equal(publicViewerEntry.is_mine, false); assert.equal(publicViewerEntry.screenshot, null); assert(publicViewerEntry.comments.some((comment) => comment.kind === "public_comment")); feedback.markFeedbackViewed("user-1"); Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2); feedback.adminUpdateFeedback(entry.id, { status: "needs_more_context", category: "bug", severity: "urgent", admin_reply: "Can you add which browser this happened in?", work_note: "Likely settings form regression.", status_note: "Need browser detail." }, { id: "admin-1" }); const notices = feedback.notificationSummary("user-1"); assert.equal(notices.needs_context, 1); const adminEntry = feedback.getFeedbackForAdmin(entry.id); assert(adminEntry.comments.some((comment) => comment.kind === "admin_reply")); assert(adminEntry.comments.some((comment) => comment.kind === "work_note")); const submitterEntry = feedback.getFeedbackForSubmitter(entry.id, "user-1"); assert.equal(submitterEntry.comments.some((comment) => comment.kind === "work_note"), false); feedback.adminUpdateFeedback(entry.id, { status: "closed", category: "bug", severity: "urgent", status_note: "Finalized after verification." }, { id: "admin-1" }); assert.equal(feedback.getFeedbackForAdmin(entry.id).status, "closed"); feedback.adminUpdateFeedback(entry.id, { status: "reviewed", category: "bug", severity: "urgent", status_note: "Reopened after verification." }, { id: "admin-1" }); assert.equal(feedback.getFeedbackForAdmin(entry.id).status, "reviewed"); assert(feedback.findSimilarFeedback({ summary: "Settings save button does nothing", category: "bug", scope_type: "element", current_url: "http://localhost/admin/settings" }, { userId: "user-2" }).some((match) => match.id === entry.id)); assert(feedback.findSimilarFeedback({ summary: "Controls are weird", description: "Clicking the save button does not apply changes after editing settings.", category: "bug", scope_type: "element", current_url: "http://localhost/admin/settings" }, { userId: "user-2" }).some((match) => match.id === entry.id)); const cleanupEntry = feedback.createFeedback({ summary: "Sensitive screenshot cleanup", category: "bug", severity: "minor", scope_type: "page", description: "This report exists to verify cleanup behavior.", current_url: "http://localhost/admin/feedback", page_title: "Feedback review", diagnostics: { user_agent: "sensitive-agent" } }, { id: "user-1" }, { screenshot: { path: "feedback/screenshots/cleanup-shot.png", mime: "image/png", size: 1024 }, attachments: [{ path: "feedback/attachments/cleanup-note.txt", mime: "text/plain", size: 128, original_name: "cleanup-note.txt" }] }); assert.equal(feedback.getFeedbackForAdmin(cleanupEntry.id).attachments.length, 1); let cleanedScreenshot = ""; let cleanedAttachment = ""; feedback.cleanupFeedback(cleanupEntry.id, { clear_screenshot: "1", clear_attachments: "1", clear_diagnostics: "1", clear_target_metadata: "1" }, { id: "admin-1" }, { deleteScreenshot(relativePath) { cleanedScreenshot = relativePath; }, deleteAttachment(relativePath) { cleanedAttachment = relativePath; } }); const cleaned = feedback.getFeedbackForAdmin(cleanupEntry.id); assert.equal(cleaned.screenshot, null); assert.deepEqual(cleaned.diagnostics, {}); assert.deepEqual(cleaned.target_metadata, {}); assert.equal(cleaned.attachments.length, 0); assert.equal(cleanedScreenshot, "feedback/screenshots/cleanup-shot.png"); assert.equal(cleanedAttachment, "feedback/attachments/cleanup-note.txt"); const feedbackView = path.join(root, "src", "web", "views", "feedback.ejs"); const adminView = path.join(root, "src", "web", "views", "admin-feedback.ejs"); const commonLocals = { siteTitle: "Lumi Bot", assetVersion: "verify", theme: null, botAvatar: null, navSections: [], user: { id: "user-1", username: "FeedbackUser" }, userAvatar: null, userInitial: "F", platformLogins: [], platformLinks: [], platforms: [], flash: null, softError: null, feedbackNotifications: feedback.notificationSummary("user-1"), feedbackOptions: feedback.feedbackOptions() }; const userRendered = ejs.render(fs.readFileSync(feedbackView, "utf8"), { ...commonLocals, title: "Feedback", feedbackList: feedback.listPublicFeedback({ userId: "user-1" }), myFeedback: feedback.listMyFeedback("user-1"), selectedFeedback: feedback.getFeedbackForSubmitter(entry.id, "user-1") }, { filename: feedbackView }); assert(userRendered.includes("My feedback")); assert(userRendered.includes("Admin reply")); assert(userRendered.includes("Open screenshot")); assert(userRendered.includes("Community comment")); const supporterRendered = ejs.render(fs.readFileSync(feedbackView, "utf8"), { ...commonLocals, user: { id: "user-2", username: "SecondUser" }, title: "Feedback", feedbackList: feedback.listPublicFeedback({ userId: "user-2" }), myFeedback: feedback.listMyFeedback("user-2"), selectedFeedback: null }, { filename: feedbackView }); assert(supporterRendered.includes("Marked as affecting you too.")); const adminRendered = ejs.render(fs.readFileSync(adminView, "utf8"), { ...commonLocals, user: { id: "admin-1", username: "AdminUser", isAdmin: true }, userInitial: "A", title: "Feedback review", feedbackItems: feedback.listFeedbackForAdmin({}), filters: { status: "", category: "", severity: "", scope: "", area: "", submitter: "", date_from: "", date_to: "", needs_action: "", sort: "last_activity" } }, { filename: adminView }); assert(adminRendered.includes("Feedback queue")); assert(!adminRendered.includes("Convert useful reports")); assert(adminRendered.includes("Finalize & Close")); assert(adminRendered.includes("Delete")); assert(adminRendered.includes("Open attached screenshot")); assert(adminRendered.includes("Sensitive data cleanup")); assert(adminRendered.includes("Remove attachments")); assert(adminRendered.includes("Plugin/area")); assert(adminRendered.includes("1 also affected")); let deletedScreenshot = ""; feedback.deleteFeedback(entry.id, { deleteScreenshot(relativePath) { deletedScreenshot = relativePath; } }); assert.equal(feedback.getFeedbackForAdmin(entry.id), null); assert.equal(db.prepare("SELECT COUNT(*) AS count FROM feedback_comments WHERE feedback_id = ?").get(entry.id).count, 0); assert.equal(db.prepare("SELECT COUNT(*) AS count FROM feedback_status_history WHERE feedback_id = ?").get(entry.id).count, 0); assert.equal(deletedScreenshot, "feedback/screenshots/test-shot.png"); console.log("Core feedback system verification passed."); } finally { database?.db.close(); fs.rmSync(sandbox, { recursive: true, force: true }); }