Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Amend web conversion functions #735

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 21 additions & 79 deletions platforms/web/lib/conversion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import {
richToPlain,
plainToRich,
plainToMarkdown,
markdownToPlain,
} from './conversion';
import { richToPlain, plainToRich, markdownToPlain } from './conversion';

describe('Rich text <=> plain text', () => {
const testCases = [
Expand All @@ -44,7 +39,7 @@ describe('Rich text <=> plain text', () => {
);

it('converts linebreaks for display rich => plain', async () => {
const richText = 'multi<br />line';
const richText = '<p>multi</p><p>line</p>';
const convertedPlainText = await richToPlain(richText);
const expectedPlainText = `multi\nline`;

Expand All @@ -54,32 +49,13 @@ describe('Rich text <=> plain text', () => {
it('converts linebreaks for display plain => rich', async () => {
const plainText = 'multi\nline';
const convertedRichText = await plainToRich(plainText, false);
const expectedRichText = 'multi<br />line';
const expectedRichText = 'multi<br />line'; // TODO shouldn't the rust wrap this in <p>?

expect(convertedRichText).toBe(expectedRichText);
});
});

describe('Plain text <=> markdown', () => {
it('converts single linebreak for plain => markdown', () => {
const plain = 'multi\nline';
const convertedMarkdown = plainToMarkdown(plain);
const expectedMarkdown = `multi<br />line`;

expect(convertedMarkdown).toBe(expectedMarkdown);
});

it('converts multiple linebreak for plain => markdown', () => {
// nb for correct display, there will be one br tag less
// than \n at the end
const plain = 'multiple\nline\n\nbreaks\n\n\n';
const convertedMarkdown = plainToMarkdown(plain);
const expectedMarkdown =
'multiple<br />line<br /><br />breaks<br /><br />';

expect(convertedMarkdown).toBe(expectedMarkdown);
});

describe('markdownToPlain', () => {
it('converts single linebreak for markdown => plain', () => {
const markdown = 'multi\\\nline';
const convertedPlainText = markdownToPlain(markdown);
Expand All @@ -99,62 +75,28 @@ describe('Plain text <=> markdown', () => {
});
});

describe('Mentions', () => {
it('converts at-room mentions for composer as expected', async () => {
const input = '@room';
const asComposerHtml = await plainToRich(input, false);

expect(asComposerHtml).toBe(
'<a data-mention-type="at-room" href="#" contenteditable="false">@room</a>',
);
});

it('converts at-room mentions for message as expected', async () => {
const input = '@room';
const asMessageHtml = await plainToRich(input, true);

expect(asMessageHtml).toBe('@room');
});

it('converts user mentions for composer as expected', async () => {
const input =
'<a href="https://matrix.to/#/@test_user:element.io" contenteditable="false" data-mention-type="user" style="some styling">a test user</a> ';
const asComposerHtml = await plainToRich(input, false);
describe('PlainToRich', () => {
it('handles quotes', async () => {
const text = '> quote from html';
const div = document.createElement('div');
div.innerText = text;

expect(asComposerHtml).toMatchInlineSnapshot(
'"<a style=\\"some styling\\" data-mention-type=\\"user\\" href=\\"https://matrix.to/#/@test_user:element.io\\" contenteditable=\\"false\\">a test user</a> "',
);
});

it('converts user mentions for message as expected', async () => {
const input =
'<a href="https://matrix.to/#/@test_user:element.io" contenteditable="false" data-mention-type="user" style="some styling">a test user</a> ';
const asMessageHtml = await plainToRich(input, true);
const input = div.innerText;
const expected = '<blockquote><p>quote from html</p></blockquote>';
const output = await plainToRich(input, true);

expect(asMessageHtml).toMatchInlineSnapshot(
'"<a href=\\"https://matrix.to/#/@test_user:element.io\\">a test user</a> "',
);
expect(output).toBe(expected);
});

it('converts room mentions for composer as expected', async () => {
const input =
'<a href="https://matrix.to/#/#test_room:element.io" contenteditable="false" data-mention-type="user" style="some styling">a test user</a> ';
const asComposerHtml = await plainToRich(input, false);

// note inner text is the same as the input inner text
expect(asComposerHtml).toMatchInlineSnapshot(
'"<a style=\\"some styling\\" data-mention-type=\\"room\\" href=\\"https://matrix.to/#/#test_room:element.io\\" contenteditable=\\"false\\">a test user</a> "',
);
});
it('handles some random taglike input', async () => {
const text = '< > << >> < hi!';
const div = document.createElement('div');
div.innerText = text;

it('converts room mentions for message as expected', async () => {
const input =
'<a href="https://matrix.to/#/#test_room:element.io" contenteditable="false" data-mention-type="user" style="some styling">a test user</a> ';
const asMessageHtml = await plainToRich(input, true);
const input = div.innerText;
const expected = '&lt; &gt; &lt;&lt; &gt;&gt; &lt; hi!';
const output = await plainToRich(input, true);

// note inner text is the mx id
expect(asMessageHtml).toMatchInlineSnapshot(
'"<a href=\\"https://matrix.to/#/#test_room:element.io\\">#test_room:element.io</a> "',
);
expect(output).toBe(expected);
});
});
21 changes: 2 additions & 19 deletions platforms/web/lib/conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,6 @@ import {
} from '../generated/wysiwyg.js';
import { initOnce } from './useComposerModel.js';

// In plain text, due to cursor positioning, ending at a linebreak will
// include an extra \n, so trim that off if required.
// We replace the remaining \n with valid markdown before
// parsing by MarkdownHTMLParser::to_html.
export const plainToMarkdown = (plainText: string) => {
let markdown = plainText;
if (markdown.endsWith('\n')) {
// manually remove the final linebreak
markdown = markdown.slice(0, -1);
}
return markdown.replaceAll(/\n/g, '<br />');
};

// In plain text, markdown newlines (displays '\' character followed by
// a newline character) will be represented as \n for display as the
// display box can not interpret markdown.
Expand Down Expand Up @@ -67,19 +54,15 @@ export async function richToPlain(richText: string) {
return plainText;
}

export async function plainToRich(plainText: string, inMessageFormat: boolean) {
if (plainText.length === 0) {
export async function plainToRich(markdown: string, inMessageFormat: boolean) {
if (markdown.length === 0) {
return '';
}

// this function could be called before initialising the WASM
// so we need to try to initialise
await initOnce();

// convert the plain text into markdown so that we can use it to
// set the model
const markdown = plainToMarkdown(plainText);

// set the model and return the rich text
const model = new_composer_model();
model.set_content_from_markdown(markdown);
Expand Down
Loading