import PrettyText, { buildOptions } from "pretty-text/pretty-text"; import { emojiUnescape } from "discourse/lib/text"; import I18n from "I18n"; import topicFixtures from "discourse/tests/fixtures/topic"; import { cloneJSON, deepMerge } from "discourse-common/lib/object"; import QUnit, { test } from "qunit"; import { click, fillIn, visit } from "@ember/test-helpers"; import { acceptance, query } from "discourse/tests/helpers/qunit-helpers"; const rawOpts = { siteSettings: { enable_emoji: true, enable_emoji_shortcuts: true, enable_mentions: true, emoji_set: "twitter", external_emoji_url: "", highlighted_languages: "json|ruby|javascript", default_code_lang: "auto", enable_markdown_linkify: true, markdown_linkify_tlds: "com", chat_enabled: true, }, getURL: (url) => url, }; function cookMarkdown(input, opts) { const merged = deepMerge({}, rawOpts, opts); return new PrettyText(buildOptions(merged)).cook(input); } QUnit.assert.cookedChatTranscript = function (input, opts, expected, message) { const actual = cookMarkdown(input, opts); this.pushResult({ result: actual === expected, actual, expected, message, }); }; function generateTranscriptHTML(messageContent, opts) { const channelDataAttr = opts.channel ? ` data-channel-name=\"${opts.channel}\"` : ""; const channelIdDataAttr = opts.channelId ? ` data-channel-id=\"${opts.channelId}\"` : ""; const reactDataAttr = opts.reactions ? ` data-reactions=\"${opts.reactionsAttr}\"` : ""; let tabIndexHTML = opts.linkTabIndex ? ' tabindex="-1"' : ""; let transcriptClasses = ["chat-transcript"]; if (opts.chained) { transcriptClasses.push("chat-transcript-chained"); } const transcript = []; transcript.push( `
This is a chat message.
", { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", timezone: "Australia/Brisbane", }), "renders the chat message with the required CSS classes and attributes" ); }); test("renders the channel name if provided with multiQuote", function (assert) { assert.cookedChatTranscript( `[chat quote="martin;2321;2022-01-25T05:40:39Z" channel="Cool Cats Club" channelId="1234" multiQuote="true"]\nThis is a chat message.\n[/chat]`, { additionalOptions }, generateTranscriptHTML("This is a chat message.
", { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", channel: "Cool Cats Club", channelId: "1234", multiQuote: true, timezone: "Australia/Brisbane", }), "renders the chat transcript with the channel name included above the user and datetime" ); }); test("renders the channel name if provided without multiQuote", function (assert) { assert.cookedChatTranscript( `[chat quote="martin;2321;2022-01-25T05:40:39Z" channel="Cool Cats Club" channelId="1234"]\nThis is a chat message.\n[/chat]`, { additionalOptions }, generateTranscriptHTML("This is a chat message.
", { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", channel: "Cool Cats Club", channelId: "1234", timezone: "Australia/Brisbane", }), "renders the chat transcript with the channel name included next to the datetime" ); }); test("renders with the chained attribute for more compact quotes", function (assert) { assert.cookedChatTranscript( `[chat quote="martin;2321;2022-01-25T05:40:39Z" channel="Cool Cats Club" channelId="1234" multiQuote="true" chained="true"]\nThis is a chat message.\n[/chat]`, { additionalOptions }, generateTranscriptHTML("This is a chat message.
", { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", channel: "Cool Cats Club", channelId: "1234", multiQuote: true, chained: true, timezone: "Australia/Brisbane", }), "renders with the chained attribute" ); }); test("renders with the noLink attribute to remove the links to the individual messages from the datetimes", function (assert) { assert.cookedChatTranscript( `[chat quote="martin;2321;2022-01-25T05:40:39Z" channel="Cool Cats Club" channelId="1234" multiQuote="true" noLink="true"]\nThis is a chat message.\n[/chat]`, { additionalOptions }, generateTranscriptHTML("This is a chat message.
", { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", channel: "Cool Cats Club", channelId: "1234", multiQuote: true, noLink: true, timezone: "Australia/Brisbane", }), "renders with the noLink attribute" ); }); test("renders with the reactions attribute", function (assert) { const reactionsAttr = "+1:martin;heart:martin,eviltrout"; assert.cookedChatTranscript( `[chat quote="martin;2321;2022-01-25T05:40:39Z" channel="Cool Cats Club" channelId="1234" reactions="${reactionsAttr}"]\nThis is a chat message.\n[/chat]`, { additionalOptions }, generateTranscriptHTML("This is a chat message.
", { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", channel: "Cool Cats Club", channelId: "1234", timezone: "Australia/Brisbane", reactionsAttr, reactions: [ { emoji: "+1", usernames: ["martin"] }, { emoji: "heart", usernames: ["martin", "eviltrout"] }, ], }), "renders with the reaction data attribute and HTML" ); }); test("renders with minimal markdown rules inside the quote bbcode block, same as server-side chat messages", function (assert) { assert.cookedChatTranscript( `[chat quote="johnsmith;450;2021-04-25T05:40:39Z"] [quote="martin, post:3, topic:6215"] another cool reply [/quote] [/chat]`, { additionalOptions }, generateTranscriptHTML( `[quote="martin, post:3, topic:6215"]
another cool reply
[/quote]
This does work with removed rules.
This ~~does work~~ with removed _rules_.
* list item 1
`, { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", timezone: "Australia/Brisbane", } ), "renders correctly with some obvious rules excluded (list/strikethrough/emphasis)" ); assert.cookedChatTranscript( `[chat quote="martin;2321;2022-01-25T05:40:39Z"]\nhere is a message :P with category hashtag #test\n[/chat]`, { additionalOptions }, generateTranscriptHTML( `here is a message with category hashtag #test
`, { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", timezone: "Australia/Brisbane", } ), "renders correctly when the feature has not been excluded" ); additionalOptions.chat.limited_pretty_text_features = [ "anchor", "bbcode-block", "bbcode-inline", "code", // "category-hashtag", "censored", "discourse-local-dates", "emoji", // "emojiShortcuts", "inlineEmoji", "html-img", "mentions", "onebox", "text-post-process", "upload-protocolrouter.location.setURL", "watched-words", "table", "spoiler-alert", ]; assert.cookedChatTranscript( `[chat quote="martin;2321;2022-01-25T05:40:39Z"]\nhere is a message :P with category hashtag #test\n[/chat]`, { additionalOptions }, generateTranscriptHTML( `here is a message :P with category hashtag #test
`, { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", timezone: "Australia/Brisbane", } ), "renders correctly with some obvious features excluded (category-hashtag, emojiShortcuts)" ); assert.cookedChatTranscript( `This ~~does work~~ with removed _rules_. * list item 1 here is a message :P with category hashtag #test [chat quote="martin;2321;2022-01-25T05:40:39Z"] This ~~does work~~ with removed _rules_. * list item 1 here is a message :P with category hashtag #test [/chat]`, { additionalOptions }, `This does work with removed rules.
here is a message with category hashtag #test
\n` + generateTranscriptHTML( `This ~~does work~~ with removed _rules_.
* list item 1
here is a message :P with category hashtag #test
`, { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", timezone: "Australia/Brisbane", } ), "the rule changes do not apply outside the BBCode [chat] block" ); }); }); acceptance( "Discourse Chat | chat-transcript date decoration", function (needs) { let additionalOptions = buildAdditionalOptions(); needs.user({ admin: false, moderator: false, username: "eviltrout", id: 1, can_chat: true, has_chat_enabled: true, timezone: "Australia/Brisbane", }); needs.settings({ chat_enabled: true, }); needs.pretender((server, helper) => { server.get("/chat/chat_channels.json", () => helper.response({ public_channels: [], direct_message_channels: [], }) ); const topicResponse = cloneJSON(topicFixtures["/t/280/1.json"]); const firstPost = topicResponse.post_stream.posts[0]; const postCooked = cookMarkdown( `[chat quote="martin;2321;2022-01-25T05:40:39Z"]\nThis is a chat message.\n[/chat]`, { additionalOptions } ); firstPost.cooked += postCooked; server.get("/t/280.json", () => helper.response(topicResponse)); }); test("chat transcript datetimes are formatted into the link with decorateCookedElement", async function (assert) { await visit("/t/-/280"); assert.strictEqual( query(".chat-transcript-datetime span").innerText.trim(), moment .tz("2022-01-25T05:40:39Z", "Australia/Brisbane") .format(I18n.t("dates.long_no_year")), "it decorates the chat transcript datetime link with a formatted date" ); }); } ); acceptance( "Discourse Chat - chat-transcript - Composer Oneboxes ", function (needs) { let additionalOptions = buildAdditionalOptions(); needs.user({ admin: false, moderator: false, username: "eviltrout", id: 1, can_chat: true, has_chat_enabled: true, timezone: "Australia/Brisbane", }); needs.settings({ chat_enabled: true, enable_markdown_linkify: true, max_oneboxes_per_post: 2, }); needs.pretender((server, helper) => { server.get("/chat/chat_channels.json", () => helper.response({ public_channels: [], direct_message_channels: [], }) ); const topicResponse = cloneJSON(topicFixtures["/t/280/1.json"]); const firstPost = topicResponse.post_stream.posts[0]; const postCooked = cookMarkdown( `[chat quote="martin;2321;2022-01-25T05:40:39Z"]\nThis is a chat message.\n[/chat]`, { additionalOptions } ); firstPost.cooked += postCooked; server.get("/t/280.json", () => helper.response(topicResponse)); }); test("Preview should not error for oneboxes within [chat] bbcode", async function (assert) { await visit("/t/internationalization-localization/280"); await click("#topic-footer-buttons .btn.create"); await fillIn( ".d-editor-input", ` [chat quote="martin;2321;2022-01-25T05:40:39Z" channel="Cool Cats Club" channelId="1234" multiQuote="true"] http://www.example.com/has-title.html [/chat]` ); const rendered = generateTranscriptHTML( '', { messageId: "2321", username: "martin", datetime: "2022-01-25T05:40:39Z", channel: "Cool Cats Club", channelId: "1234", multiQuote: true, linkTabIndex: true, showDateTimeText: true, timezone: "Australia/Brisbane", } ); assert.strictEqual( query(".d-editor-preview").innerHTML.trim(), rendered.trim(), "it renders correctly with the onebox inside the [chat] bbcode" ); const textarea = query("#reply-control .d-editor-input"); await fillIn(".d-editor-input", textarea.value + "\nA"); assert.ok( query(".d-editor-preview").innerHTML.trim().includes("\nA
"), "it does not error with a opts.discourse.hoisted error in the markdown pipeline when typing more text" ); }); } );