diff --git a/js/ui/components/VideoModal.js b/js/ui/components/VideoModal.js index 9f0f2efe..8d0a6210 100644 --- a/js/ui/components/VideoModal.js +++ b/js/ui/components/VideoModal.js @@ -3532,7 +3532,14 @@ export class VideoModal { if (status !== "success" && status !== "") { return false; } - return receipt?.validation?.status !== "passed"; + const validationStatus = + typeof receipt.validation?.status === "string" + ? receipt.validation.status.toLowerCase() + : ""; + if (!validationStatus || validationStatus === "skipped") { + return false; + } + return validationStatus !== "passed"; }); const renderReceiptItem = ({ receipt, tone }) => { diff --git a/js/ui/zapController.js b/js/ui/zapController.js index dcd041b2..8aa302a2 100644 --- a/js/ui/zapController.js +++ b/js/ui/zapController.js @@ -341,9 +341,12 @@ export default class ZapController { if (status !== "success" && status !== "") { return false; } - const validationStatus = receipt.validation?.status; - if (!validationStatus) { - return true; + const validationStatus = + typeof receipt.validation?.status === "string" + ? receipt.validation.status.toLowerCase() + : ""; + if (!validationStatus || validationStatus === "skipped") { + return false; } return validationStatus !== "passed"; }); diff --git a/tests/video-modal-zap.test.mjs b/tests/video-modal-zap.test.mjs index 7bfd3d08..1011fb1c 100644 --- a/tests/video-modal-zap.test.mjs +++ b/tests/video-modal-zap.test.mjs @@ -568,6 +568,84 @@ await (async () => { app.destroy(); })(); +await (async () => { + // Test: validation skipped receipts do not trigger warnings + const receipts = [ + { + recipientType: "creator", + status: "success", + amount: 600, + address: "creator@example.com", + payment: { result: { preimage: "dd".repeat(16) } }, + validation: { + status: "skipped", + reason: "Zap request unavailable", + }, + }, + { + recipientType: "platform", + status: "success", + amount: 100, + address: "platform@example.com", + validation: { + status: "skipped", + reason: "Zap request unavailable", + }, + }, + ]; + + const { app, modalStub, creatorAddress } = await createApp({ + splitAndZap: async () => ({ receipts }), + }); + + const pubkeyHex = "d".repeat(64); + app.pubkey = pubkeyHex; + const normalized = app.normalizeHexPubkey(pubkeyHex); + app.nwcSettingsService.cache.set(normalized, { + nwcUri: "nostr+walletconnect://example", + defaultZap: null, + lastChecked: null, + version: "", + }); + + app.currentVideo = { + id: "event901", + pubkey: pubkeyHex, + tags: [["d", "video901"]], + content: "", + created_at: 1_700_000_150, + lightningAddress: creatorAddress, + }; + + const initialWarningCount = modalStub.statusMessages.filter( + (entry) => entry.tone === "warning" + ).length; + + await app.zapController.sendZap({ amount: 700, comment: "No receipt support" }); + + const warningsAfter = modalStub.statusMessages.filter( + (entry) => entry.tone === "warning" + ).length; + assert.equal( + warningsAfter, + initialWarningCount, + "skipped validation should not emit new warnings", + ); + + const lastStatus = modalStub.statusMessages[modalStub.statusMessages.length - 1]; + assert.equal(lastStatus.tone, "success", "zap should report success when validation skipped"); + assert(!/awaiting validated zap receipt/i.test(lastStatus.message)); + + const lastReceiptEntry = modalStub.receipts[modalStub.receipts.length - 1]; + assert.equal( + lastReceiptEntry?.options?.partial, + false, + "receipt rendering should complete when validation is skipped", + ); + + app.destroy(); +})(); + await (async () => { // Test: platform receipt failure keeps retry state active and surfaces warning const platformError = new Error("Budget exceeded");