Gate login, reveal the form after a rating, and wire inline Rate this mint; drop --experimental-strip-types. Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
3.5 KiB
JavaScript
78 lines
3.5 KiB
JavaScript
/**
|
|
* The review body renderer, against hostile relay content.
|
|
*
|
|
* `lib/review-body.ts` is deliberately import-free so this file can load it under
|
|
* plain node with type stripping: `pnpm test` (node --test).
|
|
* If these fail, an event someone can sign today can break or script the card.
|
|
*/
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { escapeHtml, reviewBodyHtml } from '../src/lib/review-body.ts';
|
|
import { HOSTILE_REVIEW_CONTENT, LONG_URL, PASTED_NPUB } from './hostile-review.mjs';
|
|
|
|
/** The output with this module's own <a> elements removed: nothing else may be markup. */
|
|
function withoutOwnLinks(html) {
|
|
return html.replace(/<a class="rev-link" [^>]*>.*?<\/a>/gs, '');
|
|
}
|
|
|
|
test('the hostile fixture renders with no live markup', () => {
|
|
const html = reviewBodyHtml(HOSTILE_REVIEW_CONTENT);
|
|
const rest = withoutOwnLinks(html);
|
|
assert.ok(!rest.includes('<'), `unescaped markup survived: ${rest.slice(0, 200)}`);
|
|
assert.ok(html.includes('<script>'), 'script tag must render as text');
|
|
assert.ok(!/<img|<b>|<script/.test(html), 'no element from the body ever becomes real');
|
|
});
|
|
|
|
test('40 newlines collapse to a single blank line', () => {
|
|
const html = reviewBodyHtml(HOSTILE_REVIEW_CONTENT);
|
|
assert.ok(!/\n{3,}/.test(html), '3+ consecutive newlines must not survive');
|
|
});
|
|
|
|
test('the 500-char URL links out but shows at most 40 characters', () => {
|
|
const html = reviewBodyHtml(HOSTILE_REVIEW_CONTENT);
|
|
const match = /<a class="rev-link" href="([^"]+)" target="_blank" rel="nofollow ugc noopener">([^<]+)<\/a>/.exec(html);
|
|
assert.ok(match, 'the URL must become a link');
|
|
assert.equal(match[1], LONG_URL, 'the full URL goes in the href');
|
|
assert.ok(match[2].length <= 40, `shown text is ${match[2].length} chars, wanted <= 40`);
|
|
assert.ok(match[2].includes('…'), 'truncation is middle-out, marked with an ellipsis');
|
|
assert.ok(match[2].startsWith('https://very.long.exampl'), 'the head survives');
|
|
});
|
|
|
|
test('a pasted npub stays inert text', () => {
|
|
const html = reviewBodyHtml(HOSTILE_REVIEW_CONTENT);
|
|
assert.ok(html.includes(PASTED_NPUB), 'the npub passes through as text');
|
|
assert.ok(!html.includes(`href="${PASTED_NPUB}`), 'and is never a link');
|
|
});
|
|
|
|
test('emoji pass through untouched', () => {
|
|
const html = reviewBodyHtml(HOSTILE_REVIEW_CONTENT);
|
|
assert.ok(html.includes('\u{1F9C0}✅'));
|
|
});
|
|
|
|
test('nostr: URIs render as short plain text, never links', () => {
|
|
const html = reviewBodyHtml(`see nostr:${PASTED_NPUB} for who I am`);
|
|
assert.ok(!html.includes('<a'), 'a nostr URI must not become a link');
|
|
assert.ok(!html.includes('nostr:'), 'the scheme is dropped');
|
|
assert.ok(html.includes('npub180cvv07…yjh6w6'), 'the payload is shortened middle-out');
|
|
});
|
|
|
|
test('control characters and bidi overrides are stripped', () => {
|
|
const html = reviewBodyHtml('a\u0000bc \u202Eevil\u202C d\u200Be');
|
|
assert.equal(html, 'abc evil de', 'controls, overrides and zero-widths gone');
|
|
});
|
|
|
|
test('trailing sentence punctuation stays out of the href', () => {
|
|
const html = reviewBodyHtml('read this (https://example.com/page).');
|
|
assert.ok(html.includes('href="https://example.com/page"'), 'the parenthesis and dot are prose');
|
|
assert.ok(html.includes('</a>).'), 'and stay visible after the link');
|
|
});
|
|
|
|
test('short URLs are shown whole', () => {
|
|
const html = reviewBodyHtml('https://mint.example.com');
|
|
assert.ok(html.includes('>https://mint.example.com</a>'));
|
|
});
|
|
|
|
test('escapeHtml escapes all five characters', () => {
|
|
assert.equal(escapeHtml(`<a b="c" & 'd'>`), '<a b="c" & 'd'>');
|
|
});
|