Merge master

This commit is contained in:
Neil Lalonde
2020-02-25 17:21:37 -05:00
7562 changed files with 133059 additions and 55230 deletions
@@ -1,5 +1,5 @@
import { acceptance } from "helpers/qunit-helpers";
import { default as siteSettingFixture } from "fixtures/site_settings";
import siteSettingFixture from "fixtures/site_settings";
var titleOverride = undefined;
@@ -8,7 +8,7 @@ acceptance("Admin - Suspend User", {
server.put("/admin/users/:user_id/suspend", () =>
helper.response(200, {
suspension: {
suspended: true
suspended_till: "2099-01-01T12:00:00.000Z"
}
})
);
@@ -16,7 +16,7 @@ acceptance("Admin - Suspend User", {
server.put("/admin/users/:user_id/unsuspend", () =>
helper.response(200, {
suspension: {
suspended: false
suspended_till: null
}
})
);
@@ -1,3 +1,4 @@
import selectKit from "helpers/select-kit-helper";
import { acceptance } from "helpers/qunit-helpers";
acceptance("Admin - User Index", {
@@ -84,15 +85,10 @@ QUnit.test("will clear unsaved groups when switching user", async assert => {
"the name should be correct"
);
await fillIn(".admin-group-selector .filter-input", "Macdonald");
await click(".admin-group-selector .filter-input");
await keyEvent(".admin-group-selector .filter-input", "keydown", 13);
assert.equal(
find('.admin-group-selector span[title="Macdonald"]').length,
1,
"group should be set"
);
const groupSelector = selectKit(".admin-group-selector");
await groupSelector.expand();
await groupSelector.selectRowByValue(42);
assert.equal(groupSelector.header().value(), 42, "group should be set");
await visit("/admin/users/1/eviltrout");
@@ -13,7 +13,7 @@ QUnit.test("Can open the category modal", async assert => {
await click(".edit-category");
assert.ok(visible(".d-modal"), "it pops up a modal");
await click("a.close");
await click("button.modal-close");
assert.ok(!visible(".d-modal"), "it closes the modal");
});
@@ -36,7 +36,7 @@ QUnit.test("Editing the category", async assert => {
assert.ok(!visible(".d-modal"), "it closes the modal");
assert.equal(
DiscourseURL.redirectedTo,
"/c/bug",
"/c/bug/1",
"it does one of the rare full page redirects"
);
});
@@ -7,6 +7,7 @@ QUnit.test("category hashtag is cooked properly", async assert => {
await click("#topic-footer-buttons .btn.create");
await fillIn(".d-editor-input", "this is a category hashtag #bug");
// TODO: Test that the autocomplete shows
assert.equal(
find(".d-editor-preview:visible")
@@ -14,12 +15,4 @@ QUnit.test("category hashtag is cooked properly", async assert => {
.trim(),
'<p>this is a category hashtag <a href="/c/bugs" class="hashtag">#<span>bug</span></a></p>'
);
await click("#reply-control .btn.create");
assert.equal(
find(".topic-post:last .cooked p")
.html()
.trim(),
'this is a category hashtag <a href="/c/bugs" class="hashtag">#<span>bug</span></a>'
);
});
@@ -7,9 +7,9 @@ QUnit.test("Do not track mentions", async assert => {
server.post("/clicks/track", () => assert.ok(false));
await visit("/t/internationalization-localization/280");
assert.ok(invisible("#user-card"), "card should not appear");
assert.ok(invisible(".user-card"), "card should not appear");
await click("article[data-post-id=3651] a.mention");
assert.ok(visible("#user-card"), "card should appear");
assert.ok(visible(".user-card"), "card should appear");
assert.equal(currentURL(), "/t/internationalization-localization/280");
});
@@ -2,6 +2,8 @@ import selectKit from "helpers/select-kit-helper";
import { acceptance, updateCurrentUser } from "helpers/qunit-helpers";
import { _clearSnapshots } from "select-kit/components/composer-actions";
import { toggleCheckDraftPopup } from "discourse/controllers/composer";
import Draft from "discourse/models/draft";
import { Promise } from "rsvp";
acceptance("Composer Actions", {
loggedIn: true,
@@ -99,6 +101,9 @@ QUnit.test("replying to post - toggle_whisper", async assert => {
});
QUnit.test("replying to post - reply_as_new_topic", async assert => {
sandbox
.stub(Draft, "get")
.returns(Promise.resolve({ draft: "", draft_sequence: 0 }));
const composerActions = selectKit(".composer-actions");
const categoryChooser = selectKit(".title-wrapper .category-chooser");
const categoryChooserReplyArea = selectKit(".reply-area .category-chooser");
@@ -129,46 +134,16 @@ QUnit.test("replying to post - reply_as_new_topic", async assert => {
.val()
.includes(quote)
);
sandbox.restore();
});
QUnit.test("shared draft", async assert => {
try {
toggleCheckDraftPopup(true);
const composerActions = selectKit(".composer-actions");
const tags = selectKit(".mini-tag-chooser");
await visit("/");
await click("#create-topic");
await fillIn(
"#reply-title",
"This is the new text for the title using 'quotes'"
);
await fillIn(".d-editor-input", "This is the new text for the post");
await tags.expand();
await tags.selectRowByValue("monkey");
await composerActions.expand();
await composerActions.selectRowByValue("shared_draft");
assert.equal(tags.header().value(), "monkey", "tags are not reset");
assert.equal(
find("#reply-title").val(),
"This is the new text for the title using 'quotes'"
);
assert.equal(
find("#reply-control .btn-primary.create .d-button-label").text(),
I18n.t("composer.create_shared_draft")
);
assert.ok(find("#reply-control.composing-shared-draft").length === 1);
await click(".modal-footer .btn.btn-default");
} finally {
toggleCheckDraftPopup(false);
}
QUnit.test("reply_as_new_topic without a new_topic draft", async assert => {
await visit("/t/internationalization-localization/280");
await click(".create.reply");
const composerActions = selectKit(".composer-actions");
await composerActions.expand();
await composerActions.selectRowByValue("reply_as_new_topic");
assert.equal(exists(find(".bootbox")), false);
});
QUnit.test("hide component if no content", async assert => {
@@ -305,7 +280,7 @@ QUnit.test("replying to post - toggle_topic_bump", async assert => {
QUnit.test("replying to post as staff", async assert => {
const composerActions = selectKit(".composer-actions");
updateCurrentUser({ staff: true, admin: false });
updateCurrentUser({ admin: true });
await visit("/t/internationalization-localization/280");
await click("article#post_3 button.reply");
await composerActions.expand();
@@ -317,7 +292,7 @@ QUnit.test("replying to post as staff", async assert => {
QUnit.test("replying to post as TL3 user", async assert => {
const composerActions = selectKit(".composer-actions");
updateCurrentUser({ staff: false, admin: false, trust_level: 3 });
updateCurrentUser({ moderator: false, admin: false, trust_level: 3 });
await visit("/t/internationalization-localization/280");
await click("article#post_3 button.reply");
await composerActions.expand();
@@ -335,7 +310,7 @@ QUnit.test("replying to post as TL3 user", async assert => {
QUnit.test("replying to post as TL4 user", async assert => {
const composerActions = selectKit(".composer-actions");
updateCurrentUser({ staff: false, admin: false, trust_level: 4 });
updateCurrentUser({ moderator: false, admin: false, trust_level: 4 });
await visit("/t/internationalization-localization/280");
await click("article#post_3 button.reply");
await composerActions.expand();
@@ -363,3 +338,78 @@ QUnit.test(
);
}
);
acceptance("Composer Actions With New Topic Draft", {
loggedIn: true,
settings: {
enable_whispers: true
},
site: {
can_tag_topics: true
},
beforeEach() {
_clearSnapshots();
},
pretend(server, helper) {
server.get("draft.json", () => {
return helper.response({
draft:
'{"reply":"dum de dum da ba.","action":"createTopic","title":"dum da ba dum dum","categoryId":null,"archetypeId":"regular","metaData":null,"composerTime":540879,"typingTime":3400}',
draft_sequence: 0
});
});
}
});
QUnit.test("shared draft", async assert => {
try {
toggleCheckDraftPopup(true);
const composerActions = selectKit(".composer-actions");
const tags = selectKit(".mini-tag-chooser");
await visit("/");
await click("#create-topic");
await fillIn(
"#reply-title",
"This is the new text for the title using 'quotes'"
);
await fillIn(".d-editor-input", "This is the new text for the post");
await tags.expand();
await tags.selectRowByValue("monkey");
await composerActions.expand();
await composerActions.selectRowByValue("shared_draft");
assert.equal(tags.header().value(), "monkey", "tags are not reset");
assert.equal(
find("#reply-title").val(),
"This is the new text for the title using 'quotes'"
);
assert.equal(
find("#reply-control .btn-primary.create .d-button-label").text(),
I18n.t("composer.create_shared_draft")
);
assert.ok(find("#reply-control.composing-shared-draft").length === 1);
await click(".modal-footer .btn.btn-default");
} finally {
toggleCheckDraftPopup(false);
}
});
QUnit.test("reply_as_new_topic with new_topic draft", async assert => {
await visit("/t/internationalization-localization/280");
await click(".create.reply");
const composerActions = selectKit(".composer-actions");
await composerActions.expand();
await composerActions.selectRowByValue("reply_as_new_topic");
assert.equal(
find(".bootbox .modal-body").text(),
I18n.t("composer.composer_actions.reply_as_new_topic.confirm")
);
await click(".modal-footer .btn.btn-default");
});
@@ -34,6 +34,6 @@ QUnit.test("attachments are cooked properly", async assert => {
find(".d-editor-preview:visible")
.html()
.trim(),
'<p><a href="/uploads/short-url/asdsad.png" class="attachment">test</a></p>'
'<p><a class="attachment" href="/uploads/short-url/asdsad.png">test</a></p>'
);
});
@@ -0,0 +1,98 @@
import { acceptance } from "helpers/qunit-helpers";
acceptance("Composer - Hyperlink", {
loggedIn: true
});
QUnit.test("add a hyperlink to a reply", async assert => {
await visit("/t/internationalization-localization/280");
await click(".topic-post:first-child button.reply");
await fillIn(".d-editor-input", "This is a link to ");
assert.ok(
!exists(".insert-link.modal-body"),
"no hyperlink modal by default"
);
await click(".d-editor button.link");
assert.ok(exists(".insert-link.modal-body"), "hyperlink modal visible");
await fillIn(".modal-body .link-url", "google.com");
await fillIn(".modal-body .link-text", "Google");
await click(".modal-footer button.btn-primary");
assert.equal(
find(".d-editor-input").val(),
"This is a link to [Google](http://google.com)",
"adds link with url and text, prepends 'http://'"
);
assert.ok(
!exists(".insert-link.modal-body"),
"modal dismissed after submitting link"
);
await fillIn(".d-editor-input", "Reset textarea contents.");
await click(".d-editor button.link");
await fillIn(".modal-body .link-url", "google.com");
await fillIn(".modal-body .link-text", "Google");
await click(".modal-footer button.btn-danger");
assert.equal(
find(".d-editor-input").val(),
"Reset textarea contents.",
"adds link with url and text, prepends 'http://'"
);
assert.ok(
!exists(".insert-link.modal-body"),
"modal dismissed after cancelling"
);
const textarea = find("#reply-control .d-editor-input")[0];
textarea.selectionStart = 0;
textarea.selectionEnd = 6;
await click(".d-editor button.link");
await fillIn(".modal-body .link-url", "somelink.com");
await click(".modal-footer button.btn-primary");
assert.equal(
find(".d-editor-input").val(),
"[Reset](http://somelink.com) textarea contents.",
"adds link to a selected text"
);
await fillIn(".d-editor-input", "");
await click(".d-editor button.link");
await fillIn(".modal-body .link-url", "http://google.com");
await keyEvent(".modal-body .link-url", "keyup", 32);
assert.ok(
!exists(".internal-link-results"),
"does not show internal links search dropdown when inputting a url"
);
await fillIn(".modal-body .link-url", "local");
await keyEvent(".modal-body .link-url", "keyup", 32);
assert.ok(
exists(".internal-link-results"),
"shows internal links search dropdown when entering keywords"
);
await keyEvent(".insert-link", "keydown", 40);
await keyEvent(".insert-link", "keydown", 13);
assert.ok(
!exists(".internal-link-results"),
"search dropdown dismissed after selecting an internal link"
);
assert.ok(
find(".link-url")
.val()
.includes("http"),
"replaces link url field with internal link"
);
});
+139 -86
View File
@@ -1,3 +1,4 @@
import { run } from "@ember/runloop";
import selectKit from "helpers/select-kit-helper";
import { acceptance } from "helpers/qunit-helpers";
import { toggleCheckDraftPopup } from "discourse/controllers/composer";
@@ -84,7 +85,7 @@ QUnit.test("Tests the Composer controls", async assert => {
event[mac ? "metaKey" : "ctrlKey"] = true;
event.keyCode = 66;
Ember.run(() => textarea.dispatchEvent(event));
run(() => textarea.dispatchEvent(event));
const example = I18n.t(`composer.bold_text`);
assert.equal(
@@ -233,6 +234,26 @@ QUnit.test("Create an enqueued Topic", async assert => {
assert.ok(invisible(".d-modal"), "the modal can be dismissed");
});
QUnit.test("Can display a message and route to a URL", async assert => {
await visit("/");
await click("#create-topic");
await fillIn("#reply-title", "This title doesn't matter");
await fillIn(".d-editor-input", "custom message");
await click("#reply-control button.create");
assert.equal(
find(".bootbox .modal-body").text(),
"This is a custom response"
);
assert.equal(currentURL(), "/", "it doesn't change routes");
await click(".bootbox .btn-primary");
assert.equal(
currentURL(),
"/faq",
"can navigate to a `route_to` destination"
);
});
QUnit.test("Create a Reply", async assert => {
await visit("/t/internationalization-localization/280");
@@ -249,7 +270,7 @@ QUnit.test("Create a Reply", async assert => {
await click("#reply-control button.create");
assert.equal(
find(".cooked:last p").text(),
"this is the content of my reply"
"If you use gettext format you could leverage Launchpad 13 translations and the community behind it."
);
});
@@ -266,7 +287,7 @@ QUnit.test("Posting on a different topic", async assert => {
await click(".btn-reply-here");
assert.equal(
find(".cooked:last p").text(),
"this is the content for a different topic"
"If you use gettext format you could leverage Launchpad 13 translations and the community behind it."
);
});
@@ -426,50 +447,14 @@ QUnit.test("Composer can toggle whispers", async assert => {
await click(".toggle-fullscreen");
await menu.expand();
assert.ok(
menu.rowByValue("toggleWhisper").exists(),
"whisper toggling is still present when going fullscreen"
);
});
QUnit.test("Switching composer whisper state", async assert => {
const menu = selectKit(".toolbar-popup-menu-options");
await visit("/t/this-is-a-test-topic/9");
await click(".topic-post:eq(0) button.reply");
await menu.expand();
await menu.selectRowByValue("toggleWhisper");
await fillIn(".d-editor-input", "this is the content of my reply");
await click("#reply-control button.create");
assert.ok(find(".topic-post:last").hasClass("whisper"));
await click("#topic-footer-buttons .btn.create");
assert.ok(
find(".composer-fields .whisper .d-icon-far-eye-slash").length === 0,
"doesnt set topic reply as whisper"
);
await click(".topic-post:last button.reply");
assert.ok(find(".topic-post:last").hasClass("whisper"));
assert.ok(
find(".composer-fields .whisper .d-icon-far-eye-slash").length === 1,
"sets post reply as a whisper"
);
await click(".topic-post:nth-last-child(2) button.reply");
assert.notOk(find(".topic-post:nth-last-child(2)").hasClass("whisper"));
assert.ok(
find(".composer-fields .whisper .d-icon-far-eye-slash").length === 0,
"doesnt set post reply as a whisper"
);
});
QUnit.test(
"Composer can toggle layouts (open, fullscreen and draft)",
async assert => {
@@ -586,19 +571,48 @@ QUnit.test(
await click(".topic-post:eq(0) button.reply");
await fillIn(".d-editor-input", "This is a dirty reply");
await click(".toggler");
await click(".topic-post:eq(0) button.edit");
await click(".topic-post:eq(1) button.edit");
assert.ok(exists(".bootbox.modal"), "it pops up a confirmation dialog");
assert.equal(
find(".modal-footer a:eq(1)").text(),
I18n.t("post.abandon.no_value")
);
await click(".modal-footer a:eq(0)");
assert.equal(
find(".d-editor-input")
.val()
.indexOf("This is the first post."),
.indexOf("This is the second post."),
0,
"it populates the input with the post text"
);
}
);
QUnit.test(
"Composer draft can switch to draft in new context without destroying current draft",
async assert => {
await visit("/t/this-is-a-test-topic/9");
await click(".topic-post:eq(0) button.reply");
await fillIn(".d-editor-input", "This is a dirty reply");
await click("#site-logo");
await click("#create-topic");
assert.ok(exists(".bootbox.modal"), "it pops up a confirmation dialog");
assert.equal(
find(".modal-footer a:eq(1)").text(),
I18n.t("post.abandon.no_save_draft")
);
await click(".modal-footer a:eq(1)");
assert.equal(
find(".d-editor-input").val(),
"",
"it populates the input with the post text"
);
}
);
QUnit.test("Checks for existing draft", async assert => {
try {
toggleCheckDraftPopup(true);
@@ -626,7 +640,6 @@ QUnit.test("Checks for existing draft", async assert => {
QUnit.test("Can switch states without abandon popup", async assert => {
try {
const composerActions = selectKit(".composer-actions");
toggleCheckDraftPopup(true);
await visit("/t/internationalization-localization/280");
@@ -647,8 +660,9 @@ QUnit.test("Can switch states without abandon popup", async assert => {
await click("article#post_3 button.reply");
const composerActions = selectKit(".composer-actions");
await composerActions.expand();
await composerActions.selectRowByValue("reply_to_topic");
await composerActions.selectRowByValue("reply_as_private_message");
assert.equal(
find(".modal-body").text(),
@@ -656,9 +670,10 @@ QUnit.test("Can switch states without abandon popup", async assert => {
"abandon popup shouldn't come"
);
assert.equal(
find(".d-editor-input").val(),
longText,
assert.ok(
find(".d-editor-input")
.val()
.includes(longText),
"entered text should still be there"
);
@@ -729,66 +744,89 @@ QUnit.test("Image resizing buttons", async assert => {
await click("#create-topic");
let uploads = [
// 0 Default markdown with dimensions- should work
"![test|690x313](upload://test.png)",
"[img]http://example.com/image.jpg[/img]",
"![anotherOne|690x463](upload://anotherOne.jpeg)",
"![](upload://withoutAltAndSize.jpeg)",
// 1 Image with scaling percentage, should work
"![test|690x313,50%](upload://test.png)",
// 2 image with scaling percentage and a proceeding whitespace, should work
"![test|690x313, 50%](upload://test.png)",
// 3 No dimensions, should not work
"![test](upload://test.jpeg)",
// 4 Wrapped in backquetes should not work
"`![test|690x313](upload://test.png)`",
"![withoutSize](upload://withoutSize.png)",
// 5 html image - should not work
"<img src='http://someimage.jpg' wight='20' height='20'>",
// 6 two images one the same line, but both are syntactically correct - both should work
"![onTheSameLine1|200x200](upload://onTheSameLine1.jpeg) ![onTheSameLine2|250x250](upload://onTheSameLine2.jpeg)",
// 7 & 8 Identical images - both should work
"![identicalImage|300x300](upload://identicalImage.png)",
"![identicalImage|300x300](upload://identicalImage.png)"
"![identicalImage|300x300](upload://identicalImage.png)",
// 9 Image with whitespaces in alt - should work
"![image with spaces in alt|690x220](upload://test.png)",
// 10 Image with markdown title - should work
`![image|690x220](upload://test.png "image title")`,
// 11 bbcode - should not work
"[img]http://example.com/image.jpg[/img]",
// 12 Image with data attributes
"![test|foo=bar|690x313,50%|bar=baz](upload://test.png)"
];
await fillIn(".d-editor-input", uploads.join("\n"));
assert.ok(
find(".button-wrapper").length === 0,
"it does not append scaling buttons before hovering images"
);
await triggerEvent($(".d-editor-preview img"), "mouseover");
assert.ok(
find(".button-wrapper").length === 6,
find(".button-wrapper").length === 10,
"it adds correct amount of scaling button groups"
);
uploads[0] = "![test|690x313,50%](upload://test.png)";
await click(find(".button-wrapper .scale-btn[data-scale='50']")[0]);
// Default
uploads[0] = "![test|690x313, 50%](upload://test.png)";
await click(
find(".button-wrapper[data-image-index='0'] .scale-btn[data-scale='50']")
);
assertImageResized(assert, uploads);
await triggerEvent($(".d-editor-preview img"), "mouseover");
uploads[2] = "![anotherOne|690x463,75%](upload://anotherOne.jpeg)";
await click(find(".button-wrapper .scale-btn[data-scale='75']")[1]);
// Targets the correct image if two on the same line
uploads[6] =
"![onTheSameLine1|200x200, 50%](upload://onTheSameLine1.jpeg) ![onTheSameLine2|250x250](upload://onTheSameLine2.jpeg)";
await click(
find(".button-wrapper[data-image-index='3'] .scale-btn[data-scale='50']")
);
assertImageResized(assert, uploads);
await triggerEvent($(".d-editor-preview img"), "mouseover");
uploads[7] =
"![onTheSameLine1|200x200,50%](upload://onTheSameLine1.jpeg) ![onTheSameLine2|250x250](upload://onTheSameLine2.jpeg)";
await click(find(".button-wrapper .scale-btn[data-scale='50']")[2]);
// Try the other image on the same line
uploads[6] =
"![onTheSameLine1|200x200, 50%](upload://onTheSameLine1.jpeg) ![onTheSameLine2|250x250, 75%](upload://onTheSameLine2.jpeg)";
await click(
find(".button-wrapper[data-image-index='4'] .scale-btn[data-scale='75']")
);
assertImageResized(assert, uploads);
await triggerEvent($(".d-editor-preview img"), "mouseover");
uploads[7] =
"![onTheSameLine1|200x200,50%](upload://onTheSameLine1.jpeg) ![onTheSameLine2|250x250,75%](upload://onTheSameLine2.jpeg)";
await click(find(".button-wrapper .scale-btn[data-scale='75']")[3]);
// Make sure we target the correct image if there are duplicates
uploads[7] = "![identicalImage|300x300, 50%](upload://identicalImage.png)";
await click(
find(".button-wrapper[data-image-index='5'] .scale-btn[data-scale='50']")
);
assertImageResized(assert, uploads);
await triggerEvent($(".d-editor-preview img"), "mouseover");
uploads[8] = "![identicalImage|300x300,50%](upload://identicalImage.png)";
await click(find(".button-wrapper .scale-btn[data-scale='50']")[4]);
// Try the other dupe
uploads[8] = "![identicalImage|300x300, 75%](upload://identicalImage.png)";
await click(
find(".button-wrapper[data-image-index='6'] .scale-btn[data-scale='75']")
);
assertImageResized(assert, uploads);
await triggerEvent($(".d-editor-preview img"), "mouseover");
// Don't mess with image titles
uploads[10] = `![image|690x220, 75%](upload://test.png "image title")`;
await click(
find(".button-wrapper[data-image-index='8'] .scale-btn[data-scale='75']")
);
assertImageResized(assert, uploads);
uploads[9] = "![identicalImage|300x300,75%](upload://identicalImage.png)";
await click(find(".button-wrapper .scale-btn[data-scale='75']")[5]);
// Keep data attributes
uploads[12] = `![test|foo=bar|690x313, 75%|bar=baz](upload://test.png)`;
await click(
find(".button-wrapper[data-image-index='9'] .scale-btn[data-scale='75']")
);
assertImageResized(assert, uploads);
await fillIn(
@@ -800,10 +838,25 @@ QUnit.test("Image resizing buttons", async assert => {
`
);
await triggerEvent($(".d-editor-preview img"), "mouseover");
assert.ok(
find("script").length === 0,
"it does not unescapes script tags in code blocks"
);
});
QUnit.test("can reply to a private message", async assert => {
let submitted;
/* global server */
server.post("/posts", () => {
submitted = true;
return [200, { "Content-Type": "application/json" }, {}];
});
await visit("/t/34");
await click(".topic-post:eq(0) button.reply");
await fillIn(".d-editor-input", "this is the *content* of the reply");
await click("#reply-control button.create");
assert.ok(submitted);
});
@@ -156,7 +156,7 @@ acceptance("Composer topic featured links when uncategorized is not allowed", {
});
QUnit.test("Pasting a link enables the text input area", async assert => {
updateCurrentUser({ admin: false, staff: false, trust_level: 1 });
updateCurrentUser({ moderator: false, admin: false, trust_level: 1 });
await visit("/");
await click("#create-topic");
@@ -18,7 +18,7 @@ acceptance("Composer and uncategorized is not allowed", {
});
QUnit.test("Disable body until category is selected", async assert => {
updateCurrentUser({ admin: false, staff: false, trust_level: 1 });
updateCurrentUser({ moderator: false, admin: false, trust_level: 1 });
await visit("/");
await click("#create-topic");
@@ -48,7 +48,7 @@ QUnit.test("Disable body until category is selected", async assert => {
await fillIn(".d-editor-input", "Now I can type stuff");
await categoryChooser.expand();
await categoryChooser.selectRowByValue("__none__");
await categoryChooser.selectRowByIndex(0);
assert.ok(
find(".d-editor-textarea-wrapper.disabled").length === 0,
@@ -26,7 +26,7 @@ QUnit.test("shows banner when required", async assert => {
"alert is displayed when email disabled for non-staff"
);
updateCurrentUser({ staff: true, moderator: true });
updateCurrentUser({ moderator: true });
await visit("/");
assert.ok(
exists(".alert-emails-disabled"),
@@ -1,15 +1,15 @@
import { acceptance } from "helpers/qunit-helpers";
import { IMAGE_VERSION as v } from "pretty-text/emoji/version";
import { resetCache } from "discourse/components/emoji-picker";
acceptance("EmojiPicker", {
loggedIn: true,
beforeEach() {
resetCache();
const store = Discourse.__container__.lookup("service:emoji-store");
store.reset();
}
});
QUnit.skip("emoji picker can be opened/closed", async assert => {
QUnit.test("emoji picker can be opened/closed", async assert => {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-buttons .btn.create");
@@ -32,7 +32,7 @@ QUnit.skip("emoji picker can be opened/closed", async assert => {
);
});
QUnit.skip("emojis can be hovered to display info", async assert => {
QUnit.test("emojis can be hovered to display info", async assert => {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-buttons .btn.create");
@@ -47,7 +47,7 @@ QUnit.skip("emojis can be hovered to display info", async assert => {
);
});
QUnit.skip("emoji picker triggers event when picking emoji", async assert => {
QUnit.test("emoji picker triggers event when picking emoji", async assert => {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-buttons .btn.create");
await click("button.emoji.btn");
@@ -60,7 +60,37 @@ QUnit.skip("emoji picker triggers event when picking emoji", async assert => {
);
});
QUnit.skip("emoji picker has a list of recently used emojis", async assert => {
QUnit.test(
"emoji picker adds leading whitespace before emoji",
async assert => {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-buttons .btn.create");
// Whitespace should be added on text
await fillIn(".d-editor-input", "This is a test input");
await click("button.emoji.btn");
await click(".emoji-picker button[title='grinning']");
assert.equal(
find(".d-editor-input").val(),
"This is a test input :grinning:",
"it adds the emoji code and a leading whitespace when there is text"
);
await click("button.emoji.btn");
// Whitespace should not be added on whitespace
await fillIn(".d-editor-input", "This is a test input ");
await click("button.emoji.btn");
await click(".emoji-picker button[title='grinning']");
assert.equal(
find(".d-editor-input").val(),
"This is a test input :grinning:",
"it adds the emoji code and no leading whitespace when user already entered whitespace"
);
await click("button.emoji.btn");
}
);
QUnit.test("emoji picker has a list of recently used emojis", async assert => {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-buttons .btn.create");
await click("button.emoji.btn");
@@ -106,7 +136,7 @@ QUnit.skip("emoji picker has a list of recently used emojis", async assert => {
);
});
QUnit.skip(
QUnit.test(
"emoji picker correctly orders recently used emojis",
async assert => {
await visit("/t/internationalization-localization/280");
@@ -134,7 +164,7 @@ QUnit.skip(
}
);
QUnit.skip("emoji picker lazy loads emojis", async assert => {
QUnit.test("emoji picker lazy loads emojis", async assert => {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-buttons .btn.create");
@@ -147,7 +177,7 @@ QUnit.skip("emoji picker lazy loads emojis", async assert => {
);
});
QUnit.skip("emoji picker persists state", async assert => {
QUnit.test("emoji picker persists state", async assert => {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-buttons .btn.create");
@@ -14,14 +14,6 @@ QUnit.test("emoji is cooked properly", async assert => {
.trim(),
`<p>this is an emoji <img src="/images/emoji/emoji_one/blonde_woman.png?v=${v}" title=":blonde_woman:" class="emoji" alt=":blonde_woman:"></p>`
);
await click("#reply-control .btn.create");
assert.equal(
find(".topic-post:last .cooked p")
.html()
.trim(),
`this is an emoji <img src="/images/emoji/emoji_one/blonde_woman.png?v=${v}" title=":blonde_woman:" class="emoji" alt=":blonde_woman:">`
);
});
QUnit.test("skin toned emoji is cooked properly", async assert => {
@@ -35,12 +27,4 @@ QUnit.test("skin toned emoji is cooked properly", async assert => {
.trim(),
`<p>this is an emoji <img src="/images/emoji/emoji_one/blonde_woman/5.png?v=${v}" title=":blonde_woman:t5:" class="emoji" alt=":blonde_woman:t5:"></p>`
);
await click("#reply-control .btn.create");
assert.equal(
find(".topic-post:last .cooked p")
.html()
.trim(),
`this is an emoji <img src="/images/emoji/emoji_one/blonde_woman/5.png?v=${v}" title=":blonde_woman:t5:" class="emoji" alt=":blonde_woman:t5:">`
);
});
@@ -1,7 +1,15 @@
import { acceptance, updateCurrentUser } from "helpers/qunit-helpers";
acceptance("Enforce Second Factor", {
loggedIn: true
loggedIn: true,
pretend(server, helper) {
server.post("/u/second_factors.json", () => {
return helper.response({
success: "OK",
password_required: "true"
});
});
}
});
QUnit.test("as an admin", async assert => {
@@ -27,7 +35,7 @@ QUnit.test("as an admin", async assert => {
});
QUnit.test("as a user", async assert => {
updateCurrentUser({ staff: false, admin: false });
updateCurrentUser({ moderator: false, admin: false });
await visit("/u/eviltrout/preferences/second-factor");
Discourse.SiteSettings.enforce_second_factor = "all";
@@ -49,3 +57,28 @@ QUnit.test("as a user", async assert => {
"it stays at second-factor preferences"
);
});
QUnit.test("as an anonymous user", async assert => {
updateCurrentUser({ moderator: false, admin: false, is_anonymous: true });
await visit("/u/eviltrout/preferences/second-factor");
Discourse.SiteSettings.enforce_second_factor = "all";
Discourse.SiteSettings.allow_anonymous_posting = true;
await visit("/u/eviltrout/summary");
assert.notEqual(
find(".control-label").text(),
"Password",
"it will transition from second-factor preferences"
);
await click("#toggle-hamburger-menu");
await click("a.about-link");
assert.notEqual(
find(".control-label").text(),
"Password",
"it is possible to navigate to other pages"
);
});
@@ -4,14 +4,14 @@ import DiscourseURL from "discourse/lib/url";
acceptance("Group Card - Mobile", { mobileView: true });
QUnit.skip("group card", async assert => {
await visit("/t/301/1");
await visit("/t/-/301/1");
assert.ok(
invisible("#group-card"),
invisible(".group-card"),
"mobile group card is invisible by default"
);
await click("a.mention-group:first");
assert.ok(visible("#group-card"), "mobile group card should appear");
assert.ok(visible(".group-card"), "mobile group card should appear");
sandbox.stub(DiscourseURL, "routeTo");
await click(".card-content a.group-page-link");
@@ -4,11 +4,11 @@ import DiscourseURL from "discourse/lib/url";
acceptance("Group Card");
QUnit.test("group card", async assert => {
await visit("/t/301/1");
assert.ok(invisible("#group-card"), "user card is invisible by default");
await visit("/t/-/301/1");
assert.ok(invisible(".group-card"), "user card is invisible by default");
await click("a.mention-group:first");
assert.ok(visible("#group-card"), "card should appear");
assert.ok(visible(".group-card"), "card should appear");
sandbox.stub(DiscourseURL, "routeTo");
await click(".card-content a.group-page-link");
@@ -26,7 +26,7 @@ QUnit.test("Viewing Members as anon user", async assert => {
acceptance("Group Members", { loggedIn: true });
QUnit.test("Viewing Members as a group owner", async assert => {
updateCurrentUser({ admin: false, staff: false });
updateCurrentUser({ moderator: false, admin: false });
await visit("/g/discourse");
await click(".group-members-add");
@@ -42,7 +42,7 @@ QUnit.test("As an admin", async assert => {
});
QUnit.test("As a group owner", async assert => {
updateCurrentUser({ admin: false, staff: false });
updateCurrentUser({ moderator: false, admin: false });
await visit("/g/discourse/manage/interaction");
assert.equal(
@@ -1,4 +1,5 @@
import { acceptance, updateCurrentUser } from "helpers/qunit-helpers";
import selectKit from "helpers/select-kit-helper";
acceptance("Managing Group Membership", {
loggedIn: true
@@ -65,10 +66,17 @@ QUnit.test("As an admin", async assert => {
1,
"it should display the membership request template field"
);
const emailDomains = selectKit(".group-form-automatic-membership-automatic");
await emailDomains.expand();
await emailDomains.fillInFilter("foo.com");
await emailDomains.keyboard("enter");
assert.equal(emailDomains.header().value(), "foo.com");
});
QUnit.test("As a group owner", async assert => {
updateCurrentUser({ staff: false, admin: false });
updateCurrentUser({ moderator: false, admin: false });
await visit("/g/discourse/manage/membership");
@@ -34,7 +34,7 @@ QUnit.test("As an admin", async assert => {
});
QUnit.test("As a group owner", async assert => {
updateCurrentUser({ staff: false, admin: false });
updateCurrentUser({ moderator: false, admin: false });
await visit("/g/discourse/manage/profile");
@@ -37,6 +37,7 @@ acceptance("Group Requests", {
is_group_user: true,
is_group_owner: true,
is_group_owner_display: true,
can_see_members: true,
mentionable: false,
messageable: false
},
@@ -126,5 +127,8 @@ QUnit.test("Group Requests", async assert => {
.trim(),
"denied"
);
assert.deepEqual(requests, [["19", "true"], ["20", undefined]]);
assert.deepEqual(requests, [
["19", "true"],
["20", undefined]
]);
});
+8 -15
View File
@@ -51,29 +51,22 @@ QUnit.test("Anonymous Viewing Group", async assert => {
);
assert.ok(count(".user-stream-item") > 0, "it lists stream items");
await selectKit(".group-dropdown").expand();
const groupDropdown = selectKit(".group-dropdown");
await groupDropdown.expand();
assert.equal(groupDropdown.rowByIndex(1).name(), "discourse");
assert.equal(
find(".select-kit-row")
.text()
.trim(),
"discourse",
"it displays the right row"
);
assert.equal(
find(".group-dropdown-filter")
.text()
.trim(),
I18n.t("groups.index.all").toLowerCase(),
"it displays the right header"
groupDropdown.rowByIndex(0).name(),
I18n.t("groups.index.all").toLowerCase()
);
Discourse.SiteSettings.enable_group_directory = false;
await visit("/g");
await visit("/g/discourse");
await selectKit(".group-dropdown").expand();
await groupDropdown.expand();
assert.equal(
find(".group-dropdown-filter").length,
+43 -3
View File
@@ -1,9 +1,10 @@
import { acceptance } from "helpers/qunit-helpers";
import { run } from "@ember/runloop";
import { acceptance, controllerFor } from "helpers/qunit-helpers";
import showModal from "discourse/lib/show-modal";
acceptance("Modal");
QUnit.test("modal", async assert => {
QUnit.test("modal", async function(assert) {
await visit("/");
assert.ok(
@@ -14,11 +15,15 @@ QUnit.test("modal", async assert => {
await click(".login-button");
assert.ok(find(".d-modal:visible").length === 1, "modal should appear");
let controller = controllerFor("modal");
assert.equal(controller.name, "login");
await click(".modal-outer-container");
assert.ok(
find(".d-modal:visible").length === 0,
"modal should disappear when you click outside"
);
assert.equal(controller.name, null);
await click(".login-button");
assert.ok(find(".d-modal:visible").length === 1, "modal should reappear");
@@ -33,7 +38,7 @@ QUnit.test("modal", async assert => {
'{{#d-modal-body title="" class="" dismissable=false}}test{{/d-modal-body}}'
);
Ember.run(() => showModal("not-dismissable", {}));
run(() => showModal("not-dismissable", {}));
assert.ok(find(".d-modal:visible").length === 1, "modal should appear");
@@ -48,3 +53,38 @@ QUnit.test("modal", async assert => {
"ESC should not close the modal"
);
});
acceptance("Modal Keyboard Events", { loggedIn: true });
QUnit.test("modal-keyboard-events", async function(assert) {
await visit("/t/internationalization-localization/280");
await click(".toggle-admin-menu");
await click(".topic-admin-status-update button");
await keyEvent(".d-modal", "keydown", 13);
assert.ok(
find("#modal-alert:visible").length === 1,
"hitting Enter triggers modal action"
);
assert.ok(
find(".d-modal:visible").length === 1,
"hitting Enter does not dismiss modal due to alert error"
);
await keyEvent("#main-outlet", "keydown", 27);
assert.ok(
find(".d-modal:visible").length === 0,
"ESC should close the modal"
);
await click(".topic-body button.reply");
await click(".d-editor-button-bar .btn.link");
await keyEvent(".d-modal", "keydown", 13);
assert.ok(
find(".d-modal:visible").length === 0,
"modal should disappear on hitting Enter"
);
});
@@ -13,6 +13,17 @@ QUnit.test("footer edit button", async assert => {
);
});
QUnit.test("suggested messages", async assert => {
await visit("/t/pm-for-testing/12");
assert.equal(
find("#suggested-topics .suggested-topics-title")
.text()
.trim(),
I18n.t("suggested_topics.pm_title")
);
});
acceptance("Personal Message Tagging", {
loggedIn: true,
site: { can_tag_pms: true }
@@ -1,5 +1,6 @@
import { acceptance } from "helpers/qunit-helpers";
import { extraConnectorClass } from "discourse/lib/plugin-connectors";
import { action } from "@ember/object";
const PREFIX = "javascripts/single-test/connectors";
acceptance("Plugin Outlet - Connector Class", {
@@ -12,6 +13,26 @@ acceptance("Plugin Outlet - Connector Class", {
}
});
extraConnectorClass("user-profile-primary/hi", {
setupComponent() {
this.appEvents.on("hi:sayHi", this, this.say);
},
teardownComponent() {
this.appEvents.off("hi:sayHi", this, this.say);
},
@action
say() {
this.set("hi", "hi!");
},
@action
sayHi() {
this.appEvents.trigger("hi:sayHi");
}
});
extraConnectorClass("user-profile-primary/dont-render", {
shouldRender(args) {
return args.model.get("username") !== "eviltrout";
@@ -25,6 +46,12 @@ acceptance("Plugin Outlet - Connector Class", {
<button class='say-hello' {{action "sayHello"}}></button>
<span class='hello-result'>{{hello}}</span>`
);
Ember.TEMPLATES[
`${PREFIX}/user-profile-primary/hi`
] = Ember.HTMLBars.compile(
`<button class='say-hi' {{action "sayHi"}}></button>
<span class='hi-result'>{{hi}}</span>`
);
Ember.TEMPLATES[
`${PREFIX}/user-profile-primary/dont-render`
] = Ember.HTMLBars.compile(`I'm not rendered!`);
@@ -32,6 +59,7 @@ acceptance("Plugin Outlet - Connector Class", {
afterEach() {
delete Ember.TEMPLATES[`${PREFIX}/user-profile-primary/hello`];
delete Ember.TEMPLATES[`${PREFIX}/user-profile-primary/hi`];
delete Ember.TEMPLATES[`${PREFIX}/user-profile-primary/dont-render`];
}
});
@@ -53,4 +81,7 @@ QUnit.test("Renders a template into the outlet", async assert => {
"hello!",
"actions delegate properly"
);
await click(".say-hi");
assert.equal(find(".hi-result").text(), "hi!", "actions delegate properly");
});
@@ -0,0 +1,61 @@
import { acceptance } from "helpers/qunit-helpers";
import { withPluginApi } from "discourse/lib/plugin-api";
const PREFIX = "javascripts/single-test/connectors";
acceptance("Plugin Outlet - Decorator", {
loggedIn: true,
beforeEach() {
Ember.TEMPLATES[
`${PREFIX}/discovery-list-container-top/foo`
] = Ember.HTMLBars.compile("FOO");
Ember.TEMPLATES[
`${PREFIX}/discovery-list-container-top/bar`
] = Ember.HTMLBars.compile("BAR");
withPluginApi("0.8.38", api => {
api.decoratePluginOutlet(
"discovery-list-container-top",
(elem, args) => {
if (elem.classList.contains("foo")) {
elem.style.backgroundColor = "yellow";
if (args.category) {
elem.classList.add("in-category");
} else {
elem.classList.remove("in-category");
}
}
},
{ id: "yellow-decorator" }
);
});
},
afterEach() {
delete Ember.TEMPLATES[`${PREFIX}/discovery-list-container-top/foo`];
delete Ember.TEMPLATES[`${PREFIX}/discovery-list-container-top/bar`];
}
});
QUnit.test(
"Calls the plugin callback with the rendered outlet",
async assert => {
await visit("/");
const fooConnector = find(".discovery-list-container-top-outlet.foo ")[0];
const barConnector = find(".discovery-list-container-top-outlet.bar ")[0];
assert.ok(exists(fooConnector));
assert.equal(fooConnector.style.backgroundColor, "yellow");
assert.equal(barConnector.style.backgroundColor, "");
await visit("/c/bug");
assert.ok(fooConnector.classList.contains("in-category"));
await visit("/");
assert.notOk(fooConnector.classList.contains("in-category"));
}
);
@@ -1,18 +1,26 @@
import { acceptance, updateCurrentUser } from "helpers/qunit-helpers";
import selectKit from "helpers/select-kit-helper";
import { acceptance } from "helpers/qunit-helpers";
import User from "discourse/models/user";
acceptance("User Preferences", {
loggedIn: true,
pretend(server, helper) {
server.post("/u/second_factors.json", () => {
return helper.response({
success: "OK",
password_required: "true"
});
});
server.post("/u/create_second_factor_totp.json", () => {
return helper.response({
key: "rcyryaqage3jexfj",
qr: '<div id="test-qr">qr-code</div>'
});
});
server.put("/u/second_factor.json", () => {
server.post("/u/enable_second_factor_totp.json", () => {
return helper.response({ error: "invalid token" });
});
@@ -152,11 +160,6 @@ QUnit.test("username", async assert => {
assert.ok(exists("#change_username"), "it has the input element");
});
QUnit.test("about me", async assert => {
await visit("/u/eviltrout/preferences/about-me");
assert.ok(exists(".raw-bio"), "it has the input element");
});
QUnit.test("email", async assert => {
await visit("/u/eviltrout/preferences/email");
@@ -215,12 +218,13 @@ QUnit.test("second factor", async assert => {
await fillIn("#password", "secrets");
await click(".user-preferences .btn-primary");
assert.ok(exists("#test-qr"), "shows qr code");
assert.notOk(exists("#password"), "it hides the password input");
await click(".new-totp");
assert.ok(exists("#test-qr"), "shows qr code");
await fillIn("#second-factor-token", "111111");
await click(".btn-primary");
await click(".add-totp");
assert.ok(
find(".alert-error")
@@ -230,20 +234,6 @@ QUnit.test("second factor", async assert => {
);
});
QUnit.test("second factor backup", async assert => {
await visit("/u/eviltrout/preferences/second-factor-backup");
assert.ok(
exists("#second-factor-token"),
"it has a authentication token input"
);
await fillIn("#second-factor-token", "111111");
await click(".user-preferences .btn-primary");
assert.ok(exists(".backup-codes-area"), "shows backup codes");
});
QUnit.test("default avatar selector", async assert => {
await visit("/u/eviltrout/preferences");
@@ -259,6 +249,40 @@ QUnit.test("default avatar selector", async assert => {
);
});
acceptance("Second Factor Backups", {
loggedIn: true,
pretend(server, helper) {
server.post("/u/second_factors.json", () => {
return helper.response({
success: "OK",
totps: [{ id: 1, name: "one of them" }]
});
});
server.put("/u/second_factors_backup.json", () => {
return helper.response({
backup_codes: ["dsffdsd", "fdfdfdsf", "fddsds"]
});
});
server.get("/u/eviltrout/activity.json", () => {
return helper.response({});
});
}
});
QUnit.test("second factor backup", async assert => {
updateCurrentUser({ second_factor_enabled: true });
await visit("/u/eviltrout/preferences/second-factor");
await click(".edit-2fa-backup");
assert.ok(
exists(".second-factor-backup-preferences"),
"shows the 2fa backup panel"
);
await click(".second-factor-backup-preferences .btn-primary");
assert.ok(exists(".backup-codes-area"), "shows backup codes");
});
acceptance("Avatar selector when selectable avatars is enabled", {
loggedIn: true,
settings: { selectable_avatars_enabled: true },
@@ -339,3 +363,82 @@ QUnit.test("recently connected devices", async assert => {
"it should highlight password preferences"
);
});
acceptance(
"User can select a topic to feature on profile if site setting in enabled",
{
loggedIn: true,
settings: { allow_featured_topic_on_user_profiles: true },
pretend(server, helper) {
server.put("/u/eviltrout/feature-topic", () => {
return helper.response({
success: true
});
});
}
}
);
QUnit.test("setting featured topic on profile", async assert => {
await visit("/u/eviltrout/preferences/profile");
assert.ok(
!exists(".featured-topic-link"),
"no featured topic link to present"
);
assert.ok(
!exists(".clear-feature-topic-on-profile-btn"),
"clear button not present"
);
const selectTopicBtn = find(".feature-topic-on-profile-btn:first");
assert.ok(exists(selectTopicBtn), "feature topic button is present");
await click(selectTopicBtn);
assert.ok(exists(".feature-topic-on-profile"), "topic picker modal is open");
const topicRadioBtn = find('input[name="choose_topic_id"]:first');
assert.ok(exists(topicRadioBtn), "Topic options are prefilled");
await click(topicRadioBtn);
await click(".save-featured-topic-on-profile");
assert.ok(
exists(".featured-topic-link"),
"link to featured topic is present"
);
assert.ok(
exists(".clear-feature-topic-on-profile-btn"),
"clear button is present"
);
});
acceptance("Custom User Fields", {
loggedIn: true,
site: {
user_fields: [
{
id: 30,
name: "What kind of pet do you have?",
field_type: "dropdown",
options: ["Dog", "Cat", "Hamster"],
required: true
}
]
}
});
QUnit.test("can select an option from a dropdown", async assert => {
await visit("/u/eviltrout/preferences/profile");
assert.ok(exists(".user-field"), "it has at least one user field");
await click(".user-field.dropdown");
const field = selectKit(
".user-field-what-kind-of-pet-do-you-have .combo-box"
);
await field.expand();
await field.selectRowByValue("Cat");
assert.equal(field.header().value(), "Cat", "it sets the value of the field");
});
@@ -27,3 +27,9 @@ QUnit.test("Visit reports page", async assert => {
"List of my activities"
);
});
QUnit.test("Visit report page", async assert => {
await visit("/admin/reports/staff_logins");
assert.ok(exists(".export-csv-btn"));
});
@@ -39,8 +39,11 @@ QUnit.test("Settings", async assert => {
assert.ok(find(".reviewable-score-type").length, "has a list of bonuses");
await fillIn(".reviewable-score-type:eq(0) .field input ", "0.5");
const field = selectKit(".reviewable-score-type:eq(0) .field .combo-box");
await field.expand();
await field.selectRowByValue("5");
await click(".save-settings");
assert.ok(find(".reviewable-settings .saved").length, "it saved");
});
@@ -124,7 +127,7 @@ QUnit.test("Editing a reviewable", async assert => {
let tags = selectKit(`${topic} .payload-tags .mini-tag-chooser`);
await tags.expand();
await tags.fillInFilter("monkey");
await tags.keyboard("enter");
await tags.selectRowByValue("monkey");
await fillIn(".editable-field.payload-raw textarea", "new raw contents");
await click(`${topic} .reviewable-action.save-edit`);
@@ -278,7 +278,7 @@ QUnit.test(
);
QUnit.test(
"update in:private filter through advanced search ui",
"update in:personal filter through advanced search ui",
async assert => {
await visit("/search");
await fillIn(".search-query", "none");
@@ -290,8 +290,8 @@ QUnit.test(
);
assert.equal(
find(".search-query").val(),
"none in:private",
'has updated search term to "none in:private"'
"none in:personal",
'has updated search term to "none in:personal"'
);
}
);
@@ -322,8 +322,9 @@ QUnit.test("update in filter through advanced search ui", async assert => {
await inSelector.expand();
await inSelector.selectRowByValue("bookmarks");
assert.ok(
inSelector.rowByName("I bookmarked").exists(),
assert.equal(
inSelector.header().label(),
"I bookmarked",
'has "I bookmarked" populated'
);
assert.equal(
@@ -344,8 +345,9 @@ QUnit.test("update status through advanced search ui", async assert => {
await statusSelector.expand();
await statusSelector.selectRowByValue("closed");
assert.ok(
statusSelector.rowByName("are closed").exists(),
assert.equal(
statusSelector.header().label(),
"are closed",
'has "are closed" populated'
);
assert.equal(
@@ -364,19 +366,20 @@ QUnit.test("update post time through advanced search ui", async assert => {
"it should update the search term correctly"
);
const postTimeSelector = selectKit(
".search-advanced-options .select-kit#postTime"
);
await visit("/search");
await fillIn(".search-query", "none");
await fillIn("#search-post-date .date-picker", "2016-10-05");
await fillIn("#search-post-date .date-picker", "October 5, 2016");
const postTimeSelector = selectKit(
".search-advanced-options .select-kit#postTime"
);
await postTimeSelector.expand();
await postTimeSelector.selectRowByValue("after");
assert.ok(
postTimeSelector.rowByName("after").exists(),
assert.equal(
postTimeSelector.header().label(),
"after",
'has "after" populated'
);
@@ -409,7 +412,7 @@ QUnit.test("validate advanced search when initially empty", async assert => {
await click(".search-advanced-options .in-likes");
assert.ok(
exists(".search-advanced-options .in-likes:checked"),
selectKit(".search-advanced-options .in-likes:checked"),
'has "I liked" populated'
);
assert.equal(
@@ -50,6 +50,14 @@ QUnit.test("search for a tag", async assert => {
});
QUnit.test("search scope checkbox", async assert => {
await visit("/tag/important");
await click("#search-button");
assert.ok(
exists(".search-context input:checked"),
"scope to tag checkbox is checked"
);
await click("#search-button");
await visit("/c/bug");
await click("#search-button");
assert.ok(
@@ -163,3 +171,27 @@ QUnit.test("Right filters are shown to logged-in users", async assert => {
assert.ok(exists(".search-advanced-options .in-private"));
assert.ok(exists(".search-advanced-options .in-seen"));
});
acceptance(
"Search - with tagging enabled",
Object.assign({
loggedIn: true,
searchArgs,
settings: { tagging_enabled: true }
})
);
QUnit.test("displays tags", async assert => {
await visit("/");
await click("#search-button");
await fillIn("#search-term", "dev");
await keyEvent("#search-term", "keyup", 16);
const tags = find(".search-menu .results ul li:eq(0) .discourse-tags")
.text()
.trim();
assert.equal(tags, "dev slow");
});
@@ -101,6 +101,32 @@ QUnit.test("second factor", async assert => {
);
});
QUnit.test("security key", async assert => {
await visit("/");
await click("header .login-button");
assert.ok(exists(".login-modal"), "it shows the login modal");
await fillIn("#login-account-name", "eviltrout");
await fillIn("#login-account-password", "need-security-key");
await click(".modal-footer .btn-primary");
assert.not(exists("#modal-alert:visible"), "it hides the login error");
assert.not(
exists("#credentials:visible"),
"it hides the username and password prompt"
);
assert.not(
exists("#login-second-factor:visible"),
"it does not display the second factor prompt"
);
assert.ok(
exists("#security-key:visible"),
"it shows the security key prompt"
);
assert.not(exists("#login-button:visible"), "hides the login button");
});
QUnit.test("create account", async assert => {
await visit("/");
await click("header .sign-up-button");
@@ -6,7 +6,7 @@ acceptance("Tag Hashtag", {
pretend(server, helper) {
server.get("/tags/check", () => {
return helper.response({
valid: [{ value: "monkey", url: "/tags/monkey" }]
valid: [{ value: "monkey", url: "/tag/monkey" }]
});
});
}
@@ -22,14 +22,6 @@ QUnit.test("tag is cooked properly", async assert => {
find(".d-editor-preview:visible")
.html()
.trim(),
'<p>this is a tag hashtag <a href="/tags/monkey" class="hashtag">#<span>monkey</span></a></p>'
);
await click("#reply-control .btn.create");
assert.equal(
find(".topic-post:last .cooked")
.html()
.trim(),
'<p>this is a tag hashtag <a href="/tags/monkey" class="hashtag">#<span>monkey</span></a></p>'
'<p>this is a tag hashtag <a href="/tag/monkey" class="hashtag">#<span>monkey</span></a></p>'
);
});
@@ -5,7 +5,7 @@ acceptance("Tags intersection", {
site: { can_tag_topics: true },
settings: { tagging_enabled: true },
pretend(server, helper) {
server.get("/tags/first/notifications", () => {
server.get("/tag/first/notifications", () => {
return helper.response({
tag_notification: { id: "first", notification_level: 1 }
});
+158 -12
View File
@@ -25,7 +25,10 @@ QUnit.test("list the tags in groups", async assert => {
200,
{ "Content-Type": "application/json" },
{
tags: [{ id: "planned", text: "planned", count: 7, pm_count: 0 }],
tags: [
{ id: "planned", text: "planned", count: 7, pm_count: 0 },
{ id: "private", text: "private", count: 0, pm_count: 7 }
],
extras: {
tag_groups: [
{
@@ -88,20 +91,25 @@ QUnit.test("list the tags in groups", async assert => {
.map(i => {
return $(i).attr("href");
}),
["/tags/focus", "/tags/escort"],
["/tag/focus", "/tag/escort"],
"always uses lowercase URLs for mixed case tags"
);
assert.equal(
$("a[data-tag-name='private']").attr("href"),
"/u/eviltrout/messages/tags/private",
"links to private messages"
);
});
test("new topic button is not available for staff-only tags", async assert => {
/* global server */
server.get("/tags/regular-tag/notifications", () => [
server.get("/tag/regular-tag/notifications", () => [
200,
{ "Content-Type": "application/json" },
{ tag_notification: { id: "regular-tag", notification_level: 1 } }
]);
server.get("/tags/regular-tag/l/latest.json", () => [
server.get("/tag/regular-tag/l/latest.json", () => [
200,
{ "Content-Type": "application/json" },
{
@@ -125,13 +133,13 @@ test("new topic button is not available for staff-only tags", async assert => {
}
]);
server.get("/tags/staff-only-tag/notifications", () => [
server.get("/tag/staff-only-tag/notifications", () => [
200,
{ "Content-Type": "application/json" },
{ tag_notification: { id: "staff-only-tag", notification_level: 1 } }
]);
server.get("/tags/staff-only-tag/l/latest.json", () => [
server.get("/tag/staff-only-tag/l/latest.json", () => [
200,
{ "Content-Type": "application/json" },
{
@@ -156,19 +164,157 @@ test("new topic button is not available for staff-only tags", async assert => {
}
]);
updateCurrentUser({ staff: false });
updateCurrentUser({ moderator: false, admin: false });
await visit("/tags/regular-tag");
await visit("/tag/regular-tag");
assert.ok(find("#create-topic:disabled").length === 0);
await visit("/tags/staff-only-tag");
await visit("/tag/staff-only-tag");
assert.ok(find("#create-topic:disabled").length === 1);
updateCurrentUser({ staff: true });
updateCurrentUser({ moderator: true });
await visit("/tags/regular-tag");
await visit("/tag/regular-tag");
assert.ok(find("#create-topic:disabled").length === 0);
await visit("/tags/staff-only-tag");
await visit("/tag/staff-only-tag");
assert.ok(find("#create-topic:disabled").length === 0);
});
acceptance("Tag info", {
loggedIn: true,
settings: {
tags_listed_by_group: true
},
pretend(server, helper) {
server.get("/tag/planters/notifications", () => {
return helper.response({
tag_notification: { id: "planters", notification_level: 1 }
});
});
server.get("/tag/planters/l/latest.json", () => {
return helper.response({
users: [],
primary_groups: [],
topic_list: {
can_create_topic: true,
draft: null,
draft_key: "new_topic",
draft_sequence: 1,
per_page: 30,
tags: [
{
id: 1,
name: "planters",
topic_count: 1
}
],
topics: []
}
});
});
server.get("/tag/planters/info", () => {
return helper.response({
__rest_serializer: "1",
tag_info: {
id: 12,
name: "planters",
topic_count: 1,
staff: false,
synonyms: [
{
id: "containers",
text: "containers"
},
{
id: "planter",
text: "planter"
}
],
tag_group_names: ["Gardening"],
category_ids: [7]
},
categories: [
{
id: 7,
name: "Outdoors",
color: "000",
text_color: "FFFFFF",
slug: "outdoors",
topic_count: 701,
post_count: 5320,
description: "Talk about the outdoors.",
description_text: "Talk about the outdoors.",
topic_url: "/t/category-definition-for-outdoors/1026",
read_restricted: false,
permission: null,
notification_level: null
}
]
});
});
}
});
test("tag info can show synonyms", async assert => {
updateCurrentUser({ moderator: false, admin: false });
await visit("/tag/planters");
assert.ok(find("#show-tag-info").length === 1);
await click("#show-tag-info");
assert.ok(exists(".tag-info .tag-name"), "show tag");
assert.ok(
find(".tag-info .tag-associations")
.text()
.indexOf("Gardening") >= 0,
"show tag group names"
);
assert.ok(
find(".tag-info .synonyms-list .tag-box").length === 2,
"shows the synonyms"
);
assert.ok(
find(".tag-info .badge-category").length === 1,
"show the category"
);
assert.ok(!exists("#rename-tag"), "can't rename tag");
assert.ok(!exists("#edit-synonyms"), "can't edit synonyms");
assert.ok(!exists("#delete-tag"), "can't delete tag");
});
test("admin can manage tags", async assert => {
server.delete("/tag/planters/synonyms/containers", () => [
200,
{ "Content-Type": "application/json" },
{ success: true }
]);
updateCurrentUser({ moderator: false, admin: true });
await visit("/tag/planters");
assert.ok(find("#show-tag-info").length === 1);
await click("#show-tag-info");
assert.ok(exists("#rename-tag"), "can rename tag");
assert.ok(exists("#edit-synonyms"), "can edit synonyms");
assert.ok(exists("#delete-tag"), "can delete tag");
await click("#edit-synonyms");
assert.ok(
find(".unlink-synonym:visible").length === 2,
"unlink UI is visible"
);
assert.ok(
find(".delete-synonym:visible").length === 2,
"delete UI is visible"
);
await click(".unlink-synonym:first");
assert.ok(
find(".tag-info .synonyms-list .tag-box").length === 1,
"removed a synonym"
);
});
@@ -19,10 +19,7 @@ QUnit.test("Enter without an id", async assert => {
QUnit.test("Enter a 404 topic", async assert => {
await visit("/t/not-found/404");
assert.ok(!exists("#topic"), "The topic was not rendered");
assert.ok(
find(".not-found").text() === "not found",
"it renders the error message"
);
assert.ok(exists(".topic-error"), "An error message is displayed");
});
QUnit.test("Enter without access", async assert => {
@@ -76,3 +76,32 @@ QUnit.test("Clearing state after leaving a category", async assert => {
"it doesn't expand all pinned in the latest category"
);
});
QUnit.test("Live update unread state", async assert => {
await visit("/");
assert.ok(
exists(".topic-list-item:not(.visited) a[data-topic-id='11995']"),
"shows the topic unread"
);
// Mimic a messagebus message
window.MessageBus.callbacks.filterBy("channel", "/latest").map(c =>
c.func({
message_type: "read",
topic_id: 11995,
payload: {
highest_post_number: 1,
last_read_post_number: 2,
notification_level: 1,
topic_id: 11995
}
})
);
await visit("/"); // We're already there, but use this to wait for re-render
assert.ok(
exists(".topic-list-item.visited a[data-topic-id='11995']"),
"shows the topic read"
);
});
@@ -20,7 +20,7 @@ acceptance("Topic - Edit timer", {
});
QUnit.test("default", async assert => {
updateCurrentUser({ admin: true, staff: true, canManageTopic: true });
updateCurrentUser({ moderator: true, canManageTopic: true });
const timerType = selectKit(".select-kit.timer-type");
const futureDateInputSelector = selectKit(".future-date-input-selector");
@@ -28,20 +28,20 @@ QUnit.test("default", async assert => {
await click(".toggle-admin-menu");
await click(".topic-admin-status-update button");
assert.equal(futureDateInputSelector.header().title(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().label(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().value(), null);
await click("#private-topic-timer");
assert.equal(timerType.header().title(), "Remind Me");
assert.equal(timerType.header().label(), "Remind Me");
assert.equal(timerType.header().value(), "reminder");
assert.equal(futureDateInputSelector.header().title(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().label(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().value(), null);
});
QUnit.test("autoclose - specific time", async assert => {
updateCurrentUser({ admin: true, staff: true, canManageTopic: true });
updateCurrentUser({ moderator: true, canManageTopic: true });
const futureDateInputSelector = selectKit(".future-date-input-selector");
await visit("/t/internationalization-localization");
@@ -51,7 +51,12 @@ QUnit.test("autoclose - specific time", async assert => {
await futureDateInputSelector.expand();
await futureDateInputSelector.selectRowByValue("next_week");
assert.equal(futureDateInputSelector.header().title(), "Next week");
assert.ok(
futureDateInputSelector
.header()
.label()
.includes("Next week")
);
assert.equal(futureDateInputSelector.header().value(), "next_week");
const regex = /will automatically close in/g;
@@ -62,7 +67,7 @@ QUnit.test("autoclose - specific time", async assert => {
});
QUnit.test("autoclose", async assert => {
updateCurrentUser({ admin: true, staff: true, canManageTopic: true });
updateCurrentUser({ moderator: true, canManageTopic: true });
const futureDateInputSelector = selectKit(".future-date-input-selector");
await visit("/t/internationalization-localization");
@@ -72,7 +77,12 @@ QUnit.test("autoclose", async assert => {
await futureDateInputSelector.expand();
await futureDateInputSelector.selectRowByValue("next_week");
assert.equal(futureDateInputSelector.header().title(), "Next week");
assert.ok(
futureDateInputSelector
.header()
.label()
.includes("Next week")
);
assert.equal(futureDateInputSelector.header().value(), "next_week");
const regex1 = /will automatically close in/g;
@@ -86,7 +96,12 @@ QUnit.test("autoclose", async assert => {
await fillIn(".future-date-input .date-picker", "2099-11-24");
assert.equal(futureDateInputSelector.header().title(), "Pick date and time");
assert.ok(
futureDateInputSelector
.header()
.label()
.includes("Pick date and time")
);
assert.equal(futureDateInputSelector.header().value(), "pick_date_and_time");
const regex2 = /will automatically close in/g;
@@ -100,9 +115,11 @@ QUnit.test("autoclose", async assert => {
await fillIn(".future-date-input input[type=number]", "2");
assert.equal(
futureDateInputSelector.header().title(),
"Close based on last post"
assert.ok(
futureDateInputSelector
.header()
.label()
.includes("Close based on last post")
);
assert.equal(
futureDateInputSelector.header().value(),
@@ -117,7 +134,7 @@ QUnit.test("autoclose", async assert => {
});
QUnit.test("close temporarily", async assert => {
updateCurrentUser({ admin: true, staff: true, canManageTopic: true });
updateCurrentUser({ moderator: true, canManageTopic: true });
const timerType = selectKit(".select-kit.timer-type");
const futureDateInputSelector = selectKit(".future-date-input-selector");
@@ -128,13 +145,18 @@ QUnit.test("close temporarily", async assert => {
await timerType.expand();
await timerType.selectRowByValue("open");
assert.equal(futureDateInputSelector.header().title(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().label(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().value(), null);
await futureDateInputSelector.expand();
await futureDateInputSelector.selectRowByValue("next_week");
assert.equal(futureDateInputSelector.header().title(), "Next week");
assert.ok(
futureDateInputSelector
.header()
.label()
.includes("Next week")
);
assert.equal(futureDateInputSelector.header().value(), "next_week");
const regex1 = /will automatically open in/g;
@@ -148,7 +170,7 @@ QUnit.test("close temporarily", async assert => {
await fillIn(".future-date-input .date-picker", "2099-11-24");
assert.equal(futureDateInputSelector.header().title(), "Pick date and time");
assert.equal(futureDateInputSelector.header().label(), "Pick date and time");
assert.equal(futureDateInputSelector.header().value(), "pick_date_and_time");
const regex2 = /will automatically open in/g;
@@ -159,7 +181,7 @@ QUnit.test("close temporarily", async assert => {
});
QUnit.test("schedule", async assert => {
updateCurrentUser({ admin: true, staff: true, canManageTopic: true });
updateCurrentUser({ moderator: true, canManageTopic: true });
const timerType = selectKit(".select-kit.timer-type");
const categoryChooser = selectKit(".modal-body .category-chooser");
const futureDateInputSelector = selectKit(".future-date-input-selector");
@@ -171,10 +193,10 @@ QUnit.test("schedule", async assert => {
await timerType.expand();
await timerType.selectRowByValue("publish_to_category");
assert.equal(categoryChooser.header().title(), "uncategorized");
assert.equal(categoryChooser.header().label(), "uncategorized");
assert.equal(categoryChooser.header().value(), null);
assert.equal(futureDateInputSelector.header().title(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().label(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().value(), null);
await categoryChooser.expand();
@@ -183,7 +205,12 @@ QUnit.test("schedule", async assert => {
await futureDateInputSelector.expand();
await futureDateInputSelector.selectRowByValue("next_week");
assert.equal(futureDateInputSelector.header().title(), "Next week");
assert.ok(
futureDateInputSelector
.header()
.label()
.includes("Next week")
);
assert.equal(futureDateInputSelector.header().value(), "next_week");
const regex = /will be published to #dev/g;
@@ -194,7 +221,7 @@ QUnit.test("schedule", async assert => {
});
QUnit.test("TL4 can't auto-delete", async assert => {
updateCurrentUser({ staff: false, trust_level: 4 });
updateCurrentUser({ moderator: false, admin: false, trust_level: 4 });
await visit("/t/internationalization-localization");
await click(".toggle-admin-menu");
@@ -208,7 +235,7 @@ QUnit.test("TL4 can't auto-delete", async assert => {
});
QUnit.test("auto delete", async assert => {
updateCurrentUser({ admin: true, staff: true, canManageTopic: true });
updateCurrentUser({ moderator: true, canManageTopic: true });
const timerType = selectKit(".select-kit.timer-type");
const futureDateInputSelector = selectKit(".future-date-input-selector");
@@ -219,13 +246,18 @@ QUnit.test("auto delete", async assert => {
await timerType.expand();
await timerType.selectRowByValue("delete");
assert.equal(futureDateInputSelector.header().title(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().label(), "Select a timeframe");
assert.equal(futureDateInputSelector.header().value(), null);
await futureDateInputSelector.expand();
await futureDateInputSelector.selectRowByValue("two_weeks");
assert.equal(futureDateInputSelector.header().title(), "Two Weeks");
assert.ok(
futureDateInputSelector
.header()
.label()
.includes("Two Weeks")
);
assert.equal(futureDateInputSelector.header().value(), "two_weeks");
const regex = /will be automatically deleted/g;
@@ -238,7 +270,7 @@ QUnit.test("auto delete", async assert => {
QUnit.test(
"Manually closing before the timer will clear the status text",
async assert => {
updateCurrentUser({ admin: true, staff: true, canManageTopic: true });
updateCurrentUser({ moderator: true, canManageTopic: true });
const futureDateInputSelector = selectKit(".future-date-input-selector");
await visit("/t/internationalization-localization");
@@ -263,3 +295,22 @@ QUnit.test(
assert.notOk(regex.test(newTopicStatusInfo));
}
);
QUnit.test("Inline delete timer", async assert => {
updateCurrentUser({ moderator: true, canManageTopic: true });
const futureDateInputSelector = selectKit(".future-date-input-selector");
await visit("/t/internationalization-localization");
await click(".toggle-admin-menu");
await click(".topic-admin-status-update button");
await futureDateInputSelector.expand();
await futureDateInputSelector.selectRowByValue("next_week");
await click(".modal-footer button.btn-primary");
const removeTimerButton = find(".topic-status-info .topic-timer-remove");
assert.equal(removeTimerButton.attr("title"), "remove timer");
await click(".topic-status-info .topic-timer-remove");
const topicStatusInfo = find(".topic-status-info .topic-timer-remove");
assert.equal(topicStatusInfo.length, 0);
});
@@ -18,28 +18,28 @@ QUnit.test("default", async assert => {
await click(".selected-posts .move-to-topic");
assert.ok(
find(".move-to-modal .title")
find(".choose-topic-modal .title")
.html()
.includes(I18n.t("topic.move_to.title")),
"it opens move to modal"
);
assert.ok(
find(".move-to-modal .radios")
find(".choose-topic-modal .radios")
.html()
.includes(I18n.t("topic.split_topic.radio_label")),
"it shows an option to move to new topic"
);
assert.ok(
find(".move-to-modal .radios")
find(".choose-topic-modal .radios")
.html()
.includes(I18n.t("topic.merge_topic.radio_label")),
"it shows an option to move to existing topic"
);
assert.ok(
find(".move-to-modal .radios")
find(".choose-topic-modal .radios")
.html()
.includes(I18n.t("topic.move_to_new_message.radio_label")),
"it shows an option to move to new message"
@@ -54,28 +54,28 @@ QUnit.test("moving all posts", async assert => {
await click(".selected-posts .move-to-topic");
assert.ok(
find(".move-to-modal .title")
find(".choose-topic-modal .title")
.html()
.includes(I18n.t("topic.move_to.title")),
"it opens move to modal"
);
assert.not(
find(".move-to-modal .radios")
find(".choose-topic-modal .radios")
.html()
.includes(I18n.t("topic.split_topic.radio_label")),
"it does not show an option to move to new topic"
);
assert.ok(
find(".move-to-modal .radios")
find(".choose-topic-modal .radios")
.html()
.includes(I18n.t("topic.merge_topic.radio_label")),
"it shows an option to move to existing topic"
);
assert.not(
find(".move-to-modal .radios")
find(".choose-topic-modal .radios")
.html()
.includes(I18n.t("topic.move_to_new_message.radio_label")),
"it does not show an option to move to new message"
@@ -99,21 +99,21 @@ QUnit.test("moving posts from personal message", async assert => {
await click(".selected-posts .move-to-topic");
assert.ok(
find(".move-to-modal .title")
find(".choose-topic-modal .title")
.html()
.includes(I18n.t("topic.move_to.title")),
"it opens move to modal"
);
assert.ok(
find(".move-to-modal .radios")
find(".choose-topic-modal .radios")
.html()
.includes(I18n.t("topic.move_to_new_message.radio_label")),
"it shows an option to move to new message"
);
assert.ok(
find(".move-to-modal .radios")
find(".choose-topic-modal .radios")
.html()
.includes(I18n.t("topic.move_to_existing_message.radio_label")),
"it shows an option to move to existing message"
@@ -26,8 +26,33 @@ QUnit.test("Updating topic notification level", async assert => {
await notificationOptions.selectRowByValue("3");
assert.equal(
notificationOptions.selectedRow().name(),
notificationOptions.header().label(),
"Watching",
"it should display the right notification level"
);
const timelineNotificationOptions = selectKit(
".topic-timeline .widget-component-connector .topic-notifications-options"
);
assert.equal(
timelineNotificationOptions.header().value(),
"3",
"it should display the right notification level"
);
await timelineNotificationOptions.expand();
await timelineNotificationOptions.selectRowByValue("0");
assert.equal(
timelineNotificationOptions.header().value(),
"0",
"it should display the right notification level"
);
assert.equal(
notificationOptions.header().label(),
"Muted",
"it should display the right notification level"
);
});
+89 -14
View File
@@ -22,9 +22,7 @@ QUnit.test("Reply as new topic", async assert => {
find(".d-editor-input")
.val()
.trim(),
`Continuing the discussion from [Internationalization / localization](${
window.location.origin
}/t/internationalization-localization/280):`,
`Continuing the discussion from [Internationalization / localization](${window.location.origin}/t/internationalization-localization/280):`,
"it fills composer with the ring string"
);
assert.equal(
@@ -47,9 +45,7 @@ QUnit.test("Reply as new message", async assert => {
find(".d-editor-input")
.val()
.trim(),
`Continuing the discussion from [PM for testing](${
window.location.origin
}/t/pm-for-testing/12):`,
`Continuing the discussion from [PM for testing](${window.location.origin}/t/pm-for-testing/12):`,
"it fills composer with the ring string"
);
@@ -190,6 +186,46 @@ QUnit.test("Updating the topic title with unicode emojis", async assert => {
);
});
QUnit.test(
"Updating the topic title with unicode emojis without whitespaces",
async assert => {
Discourse.SiteSettings.enable_inline_emoji_translation = true;
await visit("/t/internationalization-localization/280");
await click("#topic-title .d-icon-pencil-alt");
await fillIn("#edit-title", "Test🙂Title");
await click("#topic-title .submit-edit");
assert.equal(
find(".fancy-title")
.html()
.trim(),
`Test<img src="/images/emoji/emoji_one/slightly_smiling_face.png?v=${v}" title="slightly_smiling_face" alt="slightly_smiling_face" class="emoji">Title`,
"it displays the new title with escaped unicode emojis"
);
}
);
QUnit.test("Suggested topics", async assert => {
await visit("/t/internationalization-localization/280");
assert.equal(
find("#suggested-topics .suggested-topics-title")
.text()
.trim(),
I18n.t("suggested_topics.title")
);
});
QUnit.skip("Deleting a topic", async assert => {
await visit("/t/internationalization-localization/280");
await click(".topic-post:eq(0) button.show-more-actions");
await click(".widget-button.delete");
assert.ok(exists(".widget-button.recover"), "it shows the recover button");
});
acceptance("Topic featured links", {
loggedIn: true,
settings: {
@@ -199,7 +235,7 @@ acceptance("Topic featured links", {
});
QUnit.test("remove featured link", async assert => {
await visit("/t/299/1");
await visit("/t/-/299/1");
assert.ok(
exists(".title-wrapper .topic-featured-link"),
"link is shown with topic title"
@@ -217,6 +253,20 @@ QUnit.test("remove featured link", async assert => {
// assert.ok(!exists('.title-wrapper .topic-featured-link'), 'link is gone');
});
QUnit.test("Converting to a public topic", async assert => {
await visit("/t/test-pm/34");
assert.ok(exists(".private_message"));
await click(".toggle-admin-menu");
await click(".topic-admin-convert button");
let categoryChooser = selectKit(".convert-to-public-topic .category-chooser");
await categoryChooser.expand();
await categoryChooser.selectRowByValue(21);
await click(".convert-to-public-topic .btn-primary");
assert.ok(!exists(".private_message"));
});
QUnit.test("Unpinning unlisted topic", async assert => {
await visit("/t/internationalization-localization/280");
@@ -245,13 +295,6 @@ QUnit.test("selecting posts", async assert => {
exists(".select-all"),
"it should allow users to select all the posts"
);
await click(".toggle-admin-menu");
assert.ok(
exists(".selected-posts.hidden"),
"it should hide the multi select menu"
);
});
QUnit.test("select below", async assert => {
@@ -283,3 +326,35 @@ QUnit.test("View Hidden Replies", async assert => {
assert.equal(find(".gap").length, 0, "it hides gap");
});
QUnit.test("Quoting a quote keeps the original poster name", async assert => {
await visit("/t/internationalization-localization/280");
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents($("#post_5 blockquote")[0]);
selection.removeAllRanges();
selection.addRange(range);
await click(".quote-button");
assert.ok(
find(".d-editor-input")
.val()
.indexOf('quote="codinghorror said, post:3, topic:280"') !== -1
);
});
acceptance("Topic + Post Bookmarks with Reminders", {
loggedIn: true,
settings: {
enable_bookmarks_with_reminders: true
}
});
QUnit.test("Bookmarks Modal", async assert => {
await visit("/t/internationalization-localization/280");
await click(".topic-post:first-child button.show-more-actions");
await click(".topic-post:first-child button.bookmark");
assert.ok(exists("#bookmark-reminder-modal"), "it shows the bookmark modal");
});
@@ -6,12 +6,12 @@ acceptance("User Card - Mobile", { mobileView: true });
QUnit.skip("user card", async assert => {
await visit("/t/internationalization-localization/280");
assert.ok(
invisible("#user-card"),
invisible(".user-card"),
"mobile user card is invisible by default"
);
await click("a[data-user-card=eviltrout]:first");
assert.ok(visible("#user-card"), "mobile user card should appear");
assert.ok(visible(".user-card"), "mobile user card should appear");
sandbox.stub(DiscourseURL, "routeTo");
await click(".card-content a.user-profile-link");
@@ -1,14 +1,21 @@
import { acceptance } from "helpers/qunit-helpers";
import DiscourseURL from "discourse/lib/url";
acceptance("User Card");
acceptance("User Card", { loggedIn: true });
QUnit.test("user card", async assert => {
await visit("/t/internationalization-localization/280");
assert.ok(invisible("#user-card"), "user card is invisible by default");
assert.ok(invisible(".user-card"), "user card is invisible by default");
await click("a[data-user-card=eviltrout]:first");
assert.ok(visible("#user-card"), "card should appear");
assert.ok(visible(".user-card"), "card should appear");
assert.equal(
find(".user-card .username")
.text()
.trim(),
"eviltrout",
"user card contains the data"
);
sandbox.stub(DiscourseURL, "routeTo");
await click(".card-content a.user-profile-link");
@@ -16,4 +23,34 @@ QUnit.test("user card", async assert => {
DiscourseURL.routeTo.calledWith("/u/eviltrout"),
"it should navigate to the user profile"
);
await click("a[data-user-card=charlie]:first");
assert.ok(visible(".user-card"), "card should appear");
assert.equal(
find(".user-card .username")
.text()
.trim(),
"charlie",
"user card contains the data"
);
await click(".card-content .compose-pm button");
assert.ok(
invisible(".user-card"),
"user card dismissed after hitting Message button"
);
const mention = find("a.mention");
const icon = document.createElement("span");
icon.classList.add("icon");
mention.append(icon);
await click("a.mention .icon");
assert.ok(visible(".user-card"), "card should appear");
assert.equal(
find(".user-card .username")
.text()
.trim(),
"eviltrout",
"user card contains the data"
);
});
@@ -12,3 +12,16 @@ QUnit.test("Stream", async assert => {
"draft removed, list length diminished by one"
);
});
QUnit.test("Stream - resume draft", async assert => {
await visit("/u/eviltrout/activity/drafts");
assert.ok(find(".user-stream-item").length > 0, "has drafts");
await click(".user-stream-item .resume-draft");
assert.equal(
find(".d-editor-input")
.val()
.trim(),
"A fun new topic for testing drafts."
);
});