import componentTest, { setupRenderingTest, } from "discourse/tests/helpers/component-test"; import { click, render } from "@ember/test-helpers"; import hbs from "htmlbars-inline-precompile"; import { query, queryAll, visible, } from "discourse/tests/helpers/qunit-helpers"; import { module, test } from "qunit"; const youtubeCooked = "

written text

" + '
Vid 1
' + "

more written text

" + '
Vid 2
' + "

and even more

"; const animatedImageCooked = "

written text

" + '

' + "

more written text

" + '

' + "

and even more

"; const externalImageCooked = "

written text

" + '

' + "

more written text

" + '

' + "

and even more

"; const imageCooked = "

written text

" + '

shows alt

' + "

more written text

" + '

' + "

and even more

" + '

'; const galleryCooked = "

written text

" + '
' + '' + 'Le tomtom album' + '' + "" + "
" + "

more written text

"; const evilString = ""; const evilStringEscaped = "<script>someeviltitle</script>"; module("Discourse Chat | Component | chat message collapser", function (hooks) { setupRenderingTest(hooks); test("escapes uploads header", async function (assert) { this.set("uploads", [{ original_filename: evilString }]); await render(hbs`{{chat-message-collapser uploads=uploads}}`); assert.ok( query(".chat-message-collapser-link-small").innerHTML.includes( evilStringEscaped ) ); }); }); module( "Discourse Chat | Component | chat message collapser youtube", function (hooks) { setupRenderingTest(hooks); test("escapes youtube header", async function (assert) { this.set("cooked", youtubeCooked.replace("ytId1", evilString)); await render(hbs`{{chat-message-collapser cooked=cooked}}`); assert.ok( query(".chat-message-collapser-link").href.includes( "%3Cscript%3Esomeeviltitle%3C/script%3E" ) ); }); componentTest("shows youtube link in header", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", youtubeCooked); }, async test(assert) { const link = document.querySelectorAll(".chat-message-collapser-link"); assert.equal(link.length, 2, "two youtube links rendered"); assert.strictEqual( link[0].href, "https://www.youtube.com/watch?v=ytId1" ); assert.strictEqual( link[1].href, "https://www.youtube.com/watch?v=ytId2" ); }, }); componentTest("shows all user written text", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { youtubeCooked.youtubeid; this.set("cooked", youtubeCooked); }, async test(assert) { const text = document.querySelectorAll(".chat-message-collapser p"); assert.equal(text.length, 3, "shows all written text"); assert.strictEqual( text[0].innerText, "written text", "first line of written text" ); assert.strictEqual( text[1].innerText, "more written text", "third line of written text" ); assert.strictEqual( text[2].innerText, "and even more", "fifth line of written text" ); }, }); componentTest("collapses and expands cooked youtube", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", youtubeCooked); }, async test(assert) { const youtubeDivs = document.querySelectorAll(".onebox"); assert.equal(youtubeDivs.length, 2, "two youtube previews rendered"); await click( document.querySelectorAll(".chat-message-collapser-opened")[0], "close first preview" ); assert.notOk( visible(".onebox[data-youtube-id='ytId1']"), "first youtube preview hidden" ); assert.ok( visible(".onebox[data-youtube-id='ytId2']"), "second youtube preview still visible" ); await click(".chat-message-collapser-closed"); assert.equal(youtubeDivs.length, 2, "two youtube previews rendered"); await click( document.querySelectorAll(".chat-message-collapser-opened")[1], "close second preview" ); assert.ok( visible(".onebox[data-youtube-id='ytId1']"), "first youtube preview still visible" ); assert.notOk( visible(".onebox[data-youtube-id='ytId2']"), "second youtube preview hidden" ); await click(".chat-message-collapser-closed"); assert.equal(youtubeDivs.length, 2, "two youtube previews rendered"); }, }); } ); module( "Discourse Chat | Component | chat message collapser images", function (hooks) { setupRenderingTest(hooks); const imageTextCooked = "

A picture of Tomtom

"; componentTest("shows filename for one image", { template: hbs`{{chat-message-collapser cooked=cooked uploads=uploads}}`, beforeEach() { this.set("cooked", imageTextCooked); this.set("uploads", [{ original_filename: "tomtom.jpeg" }]); }, async test(assert) { assert.ok( query(".chat-message-collapser-link-small").innerText.includes( "tomtom.jpeg" ) ); }, }); componentTest("shows number of files for multiple images", { template: hbs`{{chat-message-collapser cooked=cooked uploads=uploads}}`, beforeEach() { this.set("cooked", imageTextCooked); this.set("uploads", [{}, {}]); }, async test(assert) { assert.ok( query(".chat-message-collapser-link-small").innerText.includes( "2 files" ) ); }, }); componentTest("collapses and expands images", { template: hbs`{{chat-message-collapser cooked=cooked uploads=uploads}}`, beforeEach() { this.set("cooked", imageTextCooked); this.set("uploads", [{ original_filename: "tomtom.png" }]); }, async test(assert) { const uploads = ".chat-uploads"; const chatImageUpload = ".chat-img-upload"; assert.ok(visible(uploads)); assert.ok(visible(chatImageUpload)); await click(".chat-message-collapser-opened"); assert.notOk(visible(uploads)); assert.notOk(visible(chatImageUpload)); await click(".chat-message-collapser-closed"); assert.ok(visible(uploads)); assert.ok(visible(chatImageUpload)); }, }); } ); module( "Discourse Chat | Component | chat message collapser animated image", function (hooks) { setupRenderingTest(hooks); componentTest("shows links for animated image", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", animatedImageCooked); }, async test(assert) { const links = document.querySelectorAll( "a.chat-message-collapser-link-small" ); assert.ok(links[0].innerText.trim().includes("avatar.png")); assert.ok(links[0].href.includes("avatar.png")); assert.ok( links[1].innerText.trim().includes("d-logo-sketch-small.png") ); assert.ok(links[1].href.includes("d-logo-sketch-small.png")); }, }); componentTest("shows all user written text", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", animatedImageCooked); }, async test(assert) { const text = document.querySelectorAll(".chat-message-collapser p"); assert.equal(text.length, 5, "shows all written text"); assert.strictEqual(text[0].innerText, "written text"); assert.strictEqual(text[2].innerText, "more written text"); assert.strictEqual(text[4].innerText, "and even more"); }, }); componentTest("collapses and expands animated image onebox", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", animatedImageCooked); }, async test(assert) { const animatedOneboxes = document.querySelectorAll(".animated.onebox"); assert.equal(animatedOneboxes.length, 2, "two oneboxes rendered"); await click( document.querySelectorAll(".chat-message-collapser-opened")[0], "close first preview" ); assert.notOk( visible(".onebox[src='/images/avatar.png']"), "first onebox hidden" ); assert.ok( visible(".onebox[src='/images/d-logo-sketch-small.png']"), "second onebox still visible" ); await click(".chat-message-collapser-closed"); assert.equal(animatedOneboxes.length, 2, "two oneboxes rendered"); await click( document.querySelectorAll(".chat-message-collapser-opened")[1], "close second preview" ); assert.ok( visible(".onebox[src='/images/avatar.png']"), "first onebox still visible" ); assert.notOk( visible(".onebox[src='/images/d-logo-sketch-small.png']"), "second onebox hidden" ); await click(".chat-message-collapser-closed"); assert.equal(animatedOneboxes.length, 2, "two oneboxes rendered"); }, }); } ); module( "Discourse Chat | Component | chat message collapser external image onebox", function (hooks) { setupRenderingTest(hooks); componentTest("shows links for animated image", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", externalImageCooked); }, async test(assert) { const links = document.querySelectorAll( "a.chat-message-collapser-link-small" ); assert.ok(links[0].innerText.trim().includes("http://cat1.com")); assert.ok(links[0].href.includes("http://cat1.com")); assert.ok(links[1].innerText.trim().includes("http://cat2.com")); assert.ok(links[1].href.includes("http://cat2.com")); }, }); componentTest("shows all user written text", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", externalImageCooked); }, async test(assert) { const text = document.querySelectorAll(".chat-message-collapser p"); assert.equal(text.length, 5, "shows all written text"); assert.strictEqual(text[0].innerText, "written text"); assert.strictEqual(text[2].innerText, "more written text"); assert.strictEqual(text[4].innerText, "and even more"); }, }); componentTest("collapses and expands image oneboxes", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", externalImageCooked); }, async test(assert) { const imageOneboxes = document.querySelectorAll(".onebox"); assert.equal(imageOneboxes.length, 2, "two oneboxes rendered"); await click( document.querySelectorAll(".chat-message-collapser-opened")[0], "close first preview" ); assert.notOk( visible(".onebox[href='http://cat1.com']"), "first onebox hidden" ); assert.ok( visible(".onebox[href='http://cat2.com']"), "second onebox still visible" ); await click(".chat-message-collapser-closed"); assert.equal(imageOneboxes.length, 2, "two oneboxes rendered"); await click( document.querySelectorAll(".chat-message-collapser-opened")[1], "close second preview" ); assert.ok( visible(".onebox[href='http://cat1.com']"), "first onebox still visible" ); assert.notOk( visible(".onebox[href='http://cat2.com']"), "second onebox hidden" ); await click(".chat-message-collapser-closed"); assert.equal(imageOneboxes.length, 2, "two oneboxes rendered"); }, }); } ); module( "Discourse Chat | Component | chat message collapser images", function (hooks) { setupRenderingTest(hooks); test("escapes link", async function (assert) { this.set( "cooked", imageCooked .replace("shows alt", evilString) .replace("/images/d-logo-sketch-small.png", evilString) ); await render(hbs`{{chat-message-collapser cooked=cooked}}`); assert.ok( queryAll(".chat-message-collapser-link-small")[0].innerHTML.includes( evilStringEscaped ) ); assert.ok( queryAll(".chat-message-collapser-link-small")[1].innerHTML.includes( "%3Cscript%3Esomeeviltitle%3C/script%3E" ) ); }); componentTest("shows alt or links (if no alt) for linked image", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", imageCooked); }, async test(assert) { const links = document.querySelectorAll( "a.chat-message-collapser-link-small" ); assert.ok(links[0].innerText.trim().includes("shows alt")); assert.ok(links[0].href.includes("/images/avatar.png")); assert.ok( links[1].innerText.trim().includes("/images/d-logo-sketch-small.png") ); assert.ok(links[1].href.includes("/images/d-logo-sketch-small.png")); }, }); componentTest("shows all user written text", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", imageCooked); }, async test(assert) { const text = document.querySelectorAll(".chat-message-collapser p"); assert.equal(text.length, 6, "shows all written text"); assert.strictEqual(text[0].innerText, "written text"); assert.strictEqual(text[2].innerText, "more written text"); assert.strictEqual(text[4].innerText, "and even more"); }, }); componentTest("collapses and expands images", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", imageCooked); }, async test(assert) { const images = document.querySelectorAll("img"); assert.equal(images.length, 3); await click( document.querySelectorAll(".chat-message-collapser-opened")[0], "close first preview" ); assert.notOk( visible("img[src='/images/avatar.png']"), "first image hidden" ); assert.ok( visible("img[src='/images/d-logo-sketch-small.png']"), "second image still visible" ); await click(".chat-message-collapser-closed"); assert.equal(images.length, 3); await click( document.querySelectorAll(".chat-message-collapser-opened")[1], "close second preview" ); assert.ok( visible("img[src='/images/avatar.png']"), "first image still visible" ); assert.notOk( visible("img[src='/images/d-logo-sketch-small.png']"), "second image hidden" ); await click(".chat-message-collapser-closed"); assert.equal(images.length, 3); }, }); componentTest("does not show collapser for emoji images", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", imageCooked); }, async test(assert) { const links = document.querySelectorAll( "a.chat-message-collapser-link-small" ); const images = document.querySelectorAll("img"); const collapser = document.querySelectorAll( ".chat-message-collapser-opened" ); assert.equal(links.length, 2); assert.equal(images.length, 3, "shows images and emoji"); assert.equal(collapser.length, 2); }, }); } ); module( "Discourse Chat | Component | chat message collapser galleries", function (hooks) { setupRenderingTest(hooks); test("escapes title/link", async function (assert) { this.set( "cooked", galleryCooked .replace("https://imgur.com/gallery/yyVx5lJ", evilString) .replace("Le tomtom album", evilString) ); await render(hbs`{{chat-message-collapser cooked=cooked}}`); assert.ok( query(".chat-message-collapser-link-small").href.includes( "%3Cscript%3Esomeeviltitle%3C/script%3E" ) ); assert.strictEqual( query(".chat-message-collapser-link-small").innerHTML.trim(), "someeviltitle" ); }); componentTest("removes album title overlay", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", galleryCooked); }, async test(assert) { assert.notOk(visible(".album-title"), "album title removed"); }, }); componentTest("shows gallery link", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", galleryCooked); }, async test(assert) { assert.ok( query(".chat-message-collapser-link-small").innerText.includes( "Le tomtom album" ) ); }, }); componentTest("shows all user written text", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", galleryCooked); }, async test(assert) { const text = document.querySelectorAll(".chat-message-collapser p"); assert.equal(text.length, 2, "shows all written text"); assert.strictEqual(text[0].innerText, "written text"); assert.strictEqual(text[1].innerText, "more written text"); }, }); componentTest("collapses and expands images", { template: hbs`{{chat-message-collapser cooked=cooked}}`, beforeEach() { this.set("cooked", galleryCooked); }, async test(assert) { assert.ok(visible("img"), "image visible initially"); await click( document.querySelectorAll(".chat-message-collapser-opened")[0], "close preview" ); assert.notOk(visible("img"), "image hidden"); await click(".chat-message-collapser-closed"); assert.ok(visible("img"), "image visible initially"); }, }); } );