mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-08 22:02:54 +00:00
Merge pull request #2734 from PR0M3TH3AN/refactor/revert-modal-rendering-15253432646628070109
Refactor RevertModal rendering logic
This commit is contained in:
3 files changed
+696
-449
No files matched your search
@@ -0,0 +1,208 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
createHeader,
|
||||
createDescription,
|
||||
createAudienceFlags,
|
||||
createEventMetadata,
|
||||
createNotePointers,
|
||||
} from '../../../js/ui/components/revertModalRenderers.js';
|
||||
|
||||
// --- Mocks ---
|
||||
|
||||
class MockHTMLElement {
|
||||
constructor(tagName) {
|
||||
this.tagName = tagName.toUpperCase();
|
||||
this.className = '';
|
||||
this.textContent = '';
|
||||
this.children = [];
|
||||
this.attributes = {};
|
||||
this.dataset = {};
|
||||
}
|
||||
|
||||
appendChild(child) {
|
||||
if (typeof child === 'string') {
|
||||
// Text node simulation
|
||||
this.children.push({ nodeType: 3, textContent: child });
|
||||
} else {
|
||||
this.children.push(child);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
setAttribute(name, value) {
|
||||
this.attributes[name] = value;
|
||||
}
|
||||
|
||||
hasChildNodes() {
|
||||
return this.children.length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
const documentMock = {
|
||||
createElement: (tagName) => new MockHTMLElement(tagName),
|
||||
createDocumentFragment: () => {
|
||||
const fragment = {
|
||||
nodeType: 11,
|
||||
children: [],
|
||||
appendChild(child) {
|
||||
this.children.push(child);
|
||||
}
|
||||
};
|
||||
return fragment;
|
||||
},
|
||||
createTextNode: (text) => ({ nodeType: 3, textContent: text }),
|
||||
};
|
||||
|
||||
// Global document mock
|
||||
globalThis.document = documentMock;
|
||||
|
||||
const utilsMock = {
|
||||
formatAbsoluteTimestamp: (ts) => `ABS:${ts}`,
|
||||
formatTimeAgo: (ts) => `AGO:${ts}`,
|
||||
truncateMiddle: (str) => `TRUNC:${str}`,
|
||||
createPlaceholder: (text) => {
|
||||
const s = new MockHTMLElement('span');
|
||||
s.textContent = text || 'PLACEHOLDER';
|
||||
return s;
|
||||
},
|
||||
createLinkMarkup: (url) => {
|
||||
const a = new MockHTMLElement('a');
|
||||
a.attributes.href = url;
|
||||
a.textContent = url;
|
||||
return a;
|
||||
},
|
||||
createListEmpty: (text) => {
|
||||
const p = new MockHTMLElement('p');
|
||||
p.textContent = text;
|
||||
return p;
|
||||
},
|
||||
formatDurationSeconds: (s) => `DUR:${s}`,
|
||||
fallbackThumbnailSrc: 'fallback.jpg',
|
||||
};
|
||||
|
||||
// --- Tests ---
|
||||
|
||||
test('createHeader renders correctly', () => {
|
||||
const version = {
|
||||
created_at: 1234567890,
|
||||
title: 'Test Title',
|
||||
thumbnail: 'thumb.jpg',
|
||||
url: 'https://example.com',
|
||||
magnet: 'magnet:?xt=urn:btih:123',
|
||||
isNsfw: true,
|
||||
version: 2
|
||||
};
|
||||
const nip71Metadata = {};
|
||||
|
||||
const header = createHeader(version, nip71Metadata, utilsMock);
|
||||
|
||||
assert.equal(header.tagName, 'DIV');
|
||||
assert.ok(header.className.includes('space-y-6'));
|
||||
|
||||
// Find title
|
||||
const h3 = findElement(header, 'H3');
|
||||
assert.equal(h3.textContent, 'Test Title');
|
||||
|
||||
// Find timestamp
|
||||
// We expect "ABS:1234567890 (AGO:1234567890)"
|
||||
const p = findElement(header, 'P', (el) => el.textContent.includes('ABS:'));
|
||||
assert.ok(p);
|
||||
|
||||
// Find NSFW pill
|
||||
const nsfwPill = findElement(header, 'SPAN', (el) => el.textContent === 'Marked NSFW');
|
||||
assert.ok(nsfwPill);
|
||||
|
||||
// Find Schema pill
|
||||
const schemaPill = findElement(header, 'SPAN', (el) => el.textContent === 'Schema v2');
|
||||
assert.ok(schemaPill);
|
||||
});
|
||||
|
||||
test('createDescription renders description', () => {
|
||||
const desc = "My Description";
|
||||
const section = createDescription(desc, utilsMock);
|
||||
|
||||
assert.equal(section.tagName, 'SECTION');
|
||||
// Check title (from createSection mock)
|
||||
const h4 = findElement(section, 'H4');
|
||||
assert.equal(h4.textContent, 'Description');
|
||||
|
||||
const p = findElement(section, 'P', (el) => el.textContent === desc);
|
||||
assert.ok(p);
|
||||
});
|
||||
|
||||
test('createAudienceFlags renders flags', () => {
|
||||
const version = { isNsfw: true, isForKids: false };
|
||||
const section = createAudienceFlags(version, utilsMock);
|
||||
|
||||
const nsfw = findElement(section, 'SPAN', (el) => el.textContent === 'Yes — marked NSFW');
|
||||
assert.ok(nsfw);
|
||||
|
||||
const kids = findElement(section, 'SPAN', (el) => el.textContent === 'No — not marked for kids');
|
||||
assert.ok(kids);
|
||||
});
|
||||
|
||||
test('createEventMetadata renders metadata', () => {
|
||||
const metadata = {
|
||||
kind: 21,
|
||||
summary: 'Summary Text',
|
||||
contentWarning: 'Warning',
|
||||
publishedAtSeconds: 1000,
|
||||
durationSeconds: 60,
|
||||
alt: 'Alt Text'
|
||||
};
|
||||
|
||||
const section = createEventMetadata(metadata, utilsMock);
|
||||
|
||||
// Kind
|
||||
const kindCode = findElement(section, 'CODE', (el) => el.textContent === 'kind 21');
|
||||
assert.ok(kindCode);
|
||||
|
||||
// Summary
|
||||
const summary = findElement(section, 'P', (el) => el.textContent === 'Summary Text');
|
||||
assert.ok(summary);
|
||||
|
||||
// Duration
|
||||
const duration = findElement(section, 'SPAN', (el) => el.textContent === 'DUR:60');
|
||||
assert.ok(duration);
|
||||
});
|
||||
|
||||
test('createNotePointers renders pointers', () => {
|
||||
const version = {
|
||||
mode: 'live',
|
||||
videoRootId: 'root123',
|
||||
id: 'event123'
|
||||
};
|
||||
const dTag = 'd-tag-val';
|
||||
|
||||
const section = createNotePointers(version, dTag, utilsMock);
|
||||
|
||||
const dTagCode = findElement(section, 'CODE', (el) => el.textContent === 'd-tag-val');
|
||||
assert.ok(dTagCode);
|
||||
|
||||
const rootCode = findElement(section, 'CODE', (el) => el.textContent === 'TRUNC:root123');
|
||||
assert.ok(rootCode);
|
||||
});
|
||||
|
||||
|
||||
// Helper to recursively find element
|
||||
function findElement(root, tagName, predicate) {
|
||||
if (!root || !root.children) return null;
|
||||
|
||||
if (root.tagName === tagName) {
|
||||
if (!predicate || predicate(root)) return root;
|
||||
}
|
||||
|
||||
for (const child of root.children) {
|
||||
if (child.nodeType === 3) continue; // Skip text nodes
|
||||
if (child.nodeType === 11) { // Fragment
|
||||
const found = findElement(child, tagName, predicate);
|
||||
if (found) return found;
|
||||
continue;
|
||||
}
|
||||
|
||||
const found = findElement(child, tagName, predicate);
|
||||
if (found) return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Reference in new issue
Block a user