CI / Build & Quality Checks (pull_request) Successful in 1m48s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 7s
CI / Playwright smoke (e2e) (pull_request) Successful in 12m19s
The topic editor's hand-rolled markdown treated any single _ or * as emphasis, inside URLs and #alias:server / @user:server too, and built the plain topic by stripping tags, so the underscores were lost for every client. It also left " unescaped in link targets. buildTopicContent() now uses the composer's inline markdown parser (links, bare URLs and HTML escaping as for messages) and treats _ between two letters/digits as literal (CommonMark), except inside code, link targets and URLs. The plain topic drops the markers but keeps links as "text (url)" and decodes entities. Fixes #262 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
73 lines
2.9 KiB
TypeScript
73 lines
2.9 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { buildTopicContent } from './topicMarkdown';
|
|
|
|
test('a URL with underscores is sent verbatim (#262)', () => {
|
|
const t = 'Rules: https://wiki.lotusguild.org/en/server_rules_and_faq';
|
|
assert.deepEqual(buildTopicContent(t), { topic: t });
|
|
});
|
|
|
|
test('a markdown link keeps its underscored URL in both fields (#262)', () => {
|
|
const c = buildTopicContent('[Our wiki](https://wiki.example.org/my_cool_page)');
|
|
assert.ok('formatted_body' in c);
|
|
assert.match(c.formatted_body, /href="https:\/\/wiki\.example\.org\/my_cool_page"/);
|
|
assert.equal(c.topic, 'Our wiki (https://wiki.example.org/my_cool_page)');
|
|
});
|
|
|
|
test('room aliases and user IDs with underscores are untouched (#262)', () => {
|
|
for (const t of [
|
|
'Join #my_room_name:matrix.org',
|
|
'Ask @some_user_x:matrix.org',
|
|
'see snake_case_word',
|
|
]) {
|
|
assert.deepEqual(buildTopicContent(t), { topic: t });
|
|
}
|
|
});
|
|
|
|
test('emphasis still works at word boundaries', () => {
|
|
const c = buildTopicContent('this is _really_ *great* and **bold**');
|
|
assert.ok('formatted_body' in c);
|
|
assert.match(c.formatted_body, /<i data-md="_">really<\/i>/);
|
|
assert.match(c.formatted_body, /<i data-md="\*">great<\/i>/);
|
|
assert.match(c.formatted_body, /<strong data-md="\*\*">bold<\/strong>/);
|
|
assert.equal(c.topic, 'this is really great and bold');
|
|
});
|
|
|
|
test('emphasis around an alias keeps the alias intact', () => {
|
|
const c = buildTopicContent('**Join #my_room:matrix.org**');
|
|
assert.ok('formatted_body' in c);
|
|
assert.match(c.formatted_body, /<strong data-md="\*\*">Join #my_room:matrix\.org<\/strong>/);
|
|
assert.equal(c.topic, 'Join #my_room:matrix.org');
|
|
});
|
|
|
|
test('inline code keeps its underscores and no backslashes appear', () => {
|
|
const c = buildTopicContent('run `a_b_c` now');
|
|
assert.ok('formatted_body' in c);
|
|
assert.match(c.formatted_body, /<code data-md="`">a_b_c<\/code>/);
|
|
assert.equal(c.topic, 'run a_b_c now');
|
|
});
|
|
|
|
test('a double quote in a link target cannot break out of the attribute (#262)', () => {
|
|
const c = buildTopicContent('[x](https://a" onclick="alert(1))');
|
|
assert.ok('formatted_body' in c);
|
|
assert.doesNotMatch(c.formatted_body, /" onclick="/);
|
|
});
|
|
|
|
test('HTML in the topic is escaped, and the plain topic shows it as typed', () => {
|
|
const c = buildTopicContent('**<b>hi</b> & bye**');
|
|
assert.ok('formatted_body' in c);
|
|
assert.match(c.formatted_body, /<b>hi<\/b> & bye/);
|
|
assert.equal(c.topic, '<b>hi</b> & bye');
|
|
});
|
|
|
|
test('line breaks become <br> and survive in the plain topic', () => {
|
|
const c = buildTopicContent('**Rules**\nBe nice');
|
|
assert.ok('formatted_body' in c);
|
|
assert.equal(c.formatted_body, '<strong data-md="**">Rules</strong><br>Be nice');
|
|
assert.equal(c.topic, 'Rules\nBe nice');
|
|
});
|
|
|
|
test('plain text with no markdown is sent as typed', () => {
|
|
assert.deepEqual(buildTopicContent("It's <fun> & games"), { topic: "It's <fun> & games" });
|
|
});
|