Merge master
This commit is contained in:
@@ -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"
|
||||
);
|
||||
});
|
||||
@@ -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,
|
||||
"doesn’t 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,
|
||||
"doesn’t 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
|
||||
"",
|
||||
"[img]http://example.com/image.jpg[/img]",
|
||||
"",
|
||||
"",
|
||||
// 1 Image with scaling percentage, should work
|
||||
"",
|
||||
// 2 image with scaling percentage and a proceeding whitespace, should work
|
||||
"",
|
||||
// 3 No dimensions, should not work
|
||||
"",
|
||||
// 4 Wrapped in backquetes should not work
|
||||
"``",
|
||||
"",
|
||||
// 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
|
||||
" ",
|
||||
// 7 & 8 Identical images - both should work
|
||||
"",
|
||||
""
|
||||
"",
|
||||
// 9 Image with whitespaces in alt - should work
|
||||
"",
|
||||
// 10 Image with markdown title - should work
|
||||
``,
|
||||
// 11 bbcode - should not work
|
||||
"[img]http://example.com/image.jpg[/img]",
|
||||
// 12 Image with data attributes
|
||||
""
|
||||
];
|
||||
|
||||
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] = "";
|
||||
await click(find(".button-wrapper .scale-btn[data-scale='50']")[0]);
|
||||
// Default
|
||||
uploads[0] = "";
|
||||
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] = "";
|
||||
await click(find(".button-wrapper .scale-btn[data-scale='75']")[1]);
|
||||
// Targets the correct image if two on the same line
|
||||
uploads[6] =
|
||||
" ";
|
||||
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] =
|
||||
" ";
|
||||
await click(find(".button-wrapper .scale-btn[data-scale='50']")[2]);
|
||||
// Try the other image on the same line
|
||||
uploads[6] =
|
||||
" ";
|
||||
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] =
|
||||
" ";
|
||||
await click(find(".button-wrapper .scale-btn[data-scale='75']")[3]);
|
||||
// Make sure we target the correct image if there are duplicates
|
||||
uploads[7] = "";
|
||||
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] = "";
|
||||
await click(find(".button-wrapper .scale-btn[data-scale='50']")[4]);
|
||||
// Try the other dupe
|
||||
uploads[8] = "";
|
||||
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] = ``;
|
||||
await click(
|
||||
find(".button-wrapper[data-image-index='8'] .scale-btn[data-scale='75']")
|
||||
);
|
||||
assertImageResized(assert, uploads);
|
||||
|
||||
uploads[9] = "";
|
||||
await click(find(".button-wrapper .scale-btn[data-scale='75']")[5]);
|
||||
// Keep data attributes
|
||||
uploads[12] = ``;
|
||||
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]
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 }
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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."
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import EmberObject from "@ember/object";
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
@@ -20,7 +21,7 @@ componentTest("default", {
|
||||
|
||||
this.set(
|
||||
"setting",
|
||||
Ember.Object.create({
|
||||
EmberObject.create({
|
||||
allowsNone: undefined,
|
||||
category: "foo",
|
||||
default: "",
|
||||
@@ -32,7 +33,7 @@ componentTest("default", {
|
||||
setting: "foo_bar",
|
||||
type: "group_list",
|
||||
validValues: undefined,
|
||||
value: "Donuts"
|
||||
value: "1"
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -42,16 +43,16 @@ componentTest("default", {
|
||||
|
||||
assert.equal(
|
||||
subject.header().value(),
|
||||
"Donuts",
|
||||
"1",
|
||||
"it selects the setting's value"
|
||||
);
|
||||
|
||||
await subject.expand();
|
||||
await subject.selectRowByValue("Cheese cake");
|
||||
await subject.selectRowByValue("2");
|
||||
|
||||
assert.equal(
|
||||
subject.header().value(),
|
||||
"Donuts,Cheese cake",
|
||||
"1,2",
|
||||
"it allows to select a setting from the list of choices"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -71,11 +71,14 @@ componentTest("with children", {
|
||||
assert.deepEqual(
|
||||
find(".components")
|
||||
.text()
|
||||
.trim(),
|
||||
.trim()
|
||||
.split(",")
|
||||
.map(n => n.trim())
|
||||
.join(","),
|
||||
childrenList
|
||||
.splice(0, 4)
|
||||
.map(theme => theme.get("name"))
|
||||
.join(", "),
|
||||
.join(","),
|
||||
"lists the first 4 children"
|
||||
);
|
||||
assert.deepEqual(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
import { default as Theme, THEMES, COMPONENTS } from "admin/models/theme";
|
||||
import Theme, { THEMES, COMPONENTS } from "admin/models/theme";
|
||||
|
||||
moduleForComponent("themes-list", { integration: true });
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import AdminUser from "admin/models/admin-user";
|
||||
import ApiKey from "admin/models/api-key";
|
||||
|
||||
QUnit.module("model:admin-user");
|
||||
|
||||
QUnit.test("generate key", function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var adminUser = AdminUser.create({ id: 333 });
|
||||
assert.ok(!adminUser.get("api_key"), "it has no api key by default");
|
||||
return adminUser.generateApiKey().then(function() {
|
||||
assert.present(adminUser.get("api_key"), "it has an api_key now");
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("revoke key", function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var apiKey = ApiKey.create({ id: 1234, key: "asdfasdf" }),
|
||||
adminUser = AdminUser.create({ id: 333, api_key: apiKey });
|
||||
|
||||
assert.equal(
|
||||
adminUser.get("api_key"),
|
||||
apiKey,
|
||||
"it has the api key in the beginning"
|
||||
);
|
||||
return adminUser.revokeApiKey().then(function() {
|
||||
assert.blank(adminUser.get("api_key"), "it cleared the api_key");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
import EmberObject from "@ember/object";
|
||||
|
||||
moduleForComponent("badge-title", { integration: true });
|
||||
|
||||
componentTest("badge title", {
|
||||
template:
|
||||
"{{badge-title selectableUserBadges=selectableUserBadges user=user}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("subject", selectKit());
|
||||
this.set("selectableUserBadges", [
|
||||
EmberObject.create({
|
||||
badge: { name: "(none)" }
|
||||
}),
|
||||
EmberObject.create({
|
||||
id: 42,
|
||||
badge_id: 102,
|
||||
badge: { name: "Test" }
|
||||
})
|
||||
]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
/* global server */
|
||||
server.put("/u/eviltrout/preferences/badge_title", () => [
|
||||
200,
|
||||
{ "Content-Type": "application/json" },
|
||||
{}
|
||||
]);
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue(42);
|
||||
await click(".btn");
|
||||
assert.equal(this.currentUser.title, "Test");
|
||||
}
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
moduleForComponent("categories-admin-dropdown", { integration: true });
|
||||
|
||||
componentTest("default", {
|
||||
template: "{{categories-admin-dropdown}}",
|
||||
|
||||
async test(assert) {
|
||||
const subject = selectKit();
|
||||
|
||||
assert.equal(subject.el().find(".d-icon-bars").length, 1);
|
||||
|
||||
await subject.expand();
|
||||
|
||||
assert.equal(subject.rowByValue("create").name(), "New Category");
|
||||
}
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
moduleForComponent("category-drop", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("subcatgories - no selection", {
|
||||
template:
|
||||
"{{category-drop onSelect=onSelect category=category parentCategory=parentCategory categories=childCategories subCategory=true noSubcategories=false}}",
|
||||
|
||||
beforeEach() {
|
||||
const parentCategory = Category.findById(2);
|
||||
|
||||
const childCategories = this.site.get("categoriesList").filter(c => {
|
||||
return c.get("parentCategory") === parentCategory;
|
||||
});
|
||||
|
||||
this.set("childCategories", childCategories);
|
||||
this.set("parentCategory", parentCategory);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(
|
||||
this.subject.header().title(),
|
||||
I18n.t("categories.all_subcategories")
|
||||
);
|
||||
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(0).name(),
|
||||
I18n.t("categories.no_subcategory")
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(1).name(),
|
||||
this.get("childCategories.firstObject.name")
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("subcatgories - selection", {
|
||||
template:
|
||||
"{{category-drop onSelect=onSelect category=category parentCategory=parentCategory categories=childCategories subCategory=true noSubcategories=false}}",
|
||||
|
||||
beforeEach() {
|
||||
const parentCategory = Category.findById(2);
|
||||
|
||||
const childCategories = this.site.get("categoriesList").filter(c => {
|
||||
return c.get("parentCategory") === parentCategory;
|
||||
});
|
||||
|
||||
this.set("childCategories", childCategories);
|
||||
this.set("category", childCategories.get("firstObject"));
|
||||
this.set("parentCategory", parentCategory);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(
|
||||
this.subject.header().title(),
|
||||
this.get("childCategories.firstObject.name")
|
||||
);
|
||||
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(0).name(),
|
||||
I18n.t("categories.all_subcategories")
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(1).name(),
|
||||
I18n.t("categories.no_subcategory")
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
moduleForComponent("category-selector", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("default", {
|
||||
template: "{{category-selector categories=categories}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("categories", [Category.findById(2)]);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().value(), 2);
|
||||
assert.notOk(
|
||||
this.subject.rowByValue(2).exists(),
|
||||
"selected categories are not in the list"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with blacklist", {
|
||||
template: "{{category-selector categories=categories blacklist=blacklist}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("categories", [Category.findById(2)]);
|
||||
this.set("blacklist", [Category.findById(8)]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.ok(
|
||||
this.subject.rowByValue(6).exists(),
|
||||
"not blacklisted categories are in the list"
|
||||
);
|
||||
assert.notOk(
|
||||
this.subject.rowByValue(8).exists(),
|
||||
"blacklisted categories are not in the list"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("interactions", {
|
||||
template: "{{category-selector categories=categories}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("categories", [Category.findById(2), Category.findById(6)]);
|
||||
},
|
||||
|
||||
skip: true,
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue(8);
|
||||
|
||||
assert.equal(
|
||||
this.subject.header().value(),
|
||||
"2,6,8",
|
||||
"it adds the selected category"
|
||||
);
|
||||
assert.equal(this.categories.length, 3);
|
||||
|
||||
await this.subject.expand();
|
||||
|
||||
await this.subject.keyboard("backspace");
|
||||
await this.subject.keyboard("backspace");
|
||||
|
||||
assert.equal(
|
||||
this.subject.header().value(),
|
||||
"2,6",
|
||||
"it removes the last selected category"
|
||||
);
|
||||
assert.equal(this.categories.length, 2);
|
||||
}
|
||||
});
|
||||
@@ -1,203 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("combo-box", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("default", {
|
||||
template: "{{combo-box content=items}}",
|
||||
beforeEach() {
|
||||
this.set("items", [{ id: 1, name: "hello" }, { id: 2, name: "world" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.header().name(), "hello");
|
||||
assert.equal(this.subject.rowByValue(1).name(), "hello");
|
||||
assert.equal(this.subject.rowByValue(2).name(), "world");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with valueAttribute", {
|
||||
template: '{{combo-box content=items valueAttribute="value"}}',
|
||||
beforeEach() {
|
||||
this.set("items", [
|
||||
{ value: 0, name: "hello" },
|
||||
{ value: 1, name: "world" }
|
||||
]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rowByValue(0).name(), "hello");
|
||||
assert.equal(this.subject.rowByValue(1).name(), "world");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with nameProperty", {
|
||||
template: '{{combo-box content=items nameProperty="text"}}',
|
||||
beforeEach() {
|
||||
this.set("items", [{ id: 0, text: "hello" }, { id: 1, text: "world" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rowByValue(0).name(), "hello");
|
||||
assert.equal(this.subject.rowByValue(1).name(), "world");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with an array as content", {
|
||||
template: "{{combo-box content=items value=value}}",
|
||||
beforeEach() {
|
||||
this.set("items", ["evil", "trout", "hat"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rowByValue("evil").name(), "evil");
|
||||
assert.equal(this.subject.rowByValue("trout").name(), "trout");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with value and none as a string", {
|
||||
template: '{{combo-box content=items none="test.none" value=value}}',
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { none: "none" };
|
||||
this.set("items", ["evil", "trout", "hat"]);
|
||||
this.set("value", "trout");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.noneRow().name(), "none");
|
||||
assert.equal(this.subject.rowByValue("evil").name(), "evil");
|
||||
assert.equal(this.subject.rowByValue("trout").name(), "trout");
|
||||
assert.equal(this.subject.header().name(), "trout");
|
||||
assert.equal(this.value, "trout");
|
||||
|
||||
await this.subject.selectNoneRow();
|
||||
|
||||
assert.equal(this.value, null);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with value and none as an object", {
|
||||
template: "{{combo-box content=items none=none value=value}}",
|
||||
beforeEach() {
|
||||
this.set("none", { id: "something", name: "none" });
|
||||
this.set("items", ["evil", "trout", "hat"]);
|
||||
this.set("value", "evil");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.noneRow().name(), "none");
|
||||
assert.equal(this.subject.rowByValue("evil").name(), "evil");
|
||||
assert.equal(this.subject.rowByValue("trout").name(), "trout");
|
||||
assert.equal(this.subject.header().name(), "evil");
|
||||
assert.equal(this.value, "evil");
|
||||
|
||||
await this.subject.selectNoneRow();
|
||||
|
||||
assert.equal(this.value, null);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with no value and none as an object", {
|
||||
template: "{{combo-box content=items none=none value=value}}",
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { none: "none" };
|
||||
this.set("none", { id: "something", name: "none" });
|
||||
this.set("items", ["evil", "trout", "hat"]);
|
||||
this.set("value", null);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.header().name(), "none");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with no value and none string", {
|
||||
template: "{{combo-box content=items none=none value=value}}",
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { none: "none" };
|
||||
this.set("none", "test.none");
|
||||
this.set("items", ["evil", "trout", "hat"]);
|
||||
this.set("value", null);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.header().name(), "none");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with no value and no none", {
|
||||
template: "{{combo-box content=items value=value}}",
|
||||
beforeEach() {
|
||||
this.set("items", ["evil", "trout", "hat"]);
|
||||
this.set("value", null);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.header().name(),
|
||||
"evil",
|
||||
"it sets the first row as value"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with empty string as value", {
|
||||
template: "{{combo-box content=items value=value}}",
|
||||
beforeEach() {
|
||||
this.set("items", ["evil", "trout", "hat"]);
|
||||
this.set("value", "");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.header().name(),
|
||||
"evil",
|
||||
"it sets the first row as value"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with noneLabel", {
|
||||
template:
|
||||
"{{combo-box content=items allowAutoSelectFirst=false noneLabel=noneLabel}}",
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { none: "none" };
|
||||
this.set("items", ["evil", "trout", "hat"]);
|
||||
this.set("noneLabel", "test.none");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.header().name(),
|
||||
"none",
|
||||
"it displays noneLabel as the header name"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -43,3 +43,14 @@ componentTest("form attribute", {
|
||||
assert.ok(exists("button[form=login-form]"), "it has the form attribute");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("link-styled button", {
|
||||
template: '{{d-button display="link"}}',
|
||||
|
||||
test(assert) {
|
||||
assert.ok(
|
||||
find("button.btn-link:not(.btn)").length,
|
||||
"it has the right classes"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { next } from "@ember/runloop";
|
||||
import componentTest from "helpers/component-test";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import formatTextWithSelection from "helpers/d-editor-helper";
|
||||
@@ -200,62 +201,6 @@ testCase(`italic with a multiline selection`, async function(assert, textarea) {
|
||||
assert.equal(textarea.selectionEnd, 12);
|
||||
});
|
||||
|
||||
testCase("link modal (cancel)", async function(assert) {
|
||||
assert.equal(find(".insert-link.hidden").length, 1);
|
||||
|
||||
await click("button.link");
|
||||
assert.equal(find(".insert-link.hidden").length, 0);
|
||||
|
||||
await click(".insert-link button.btn-danger");
|
||||
assert.equal(find(".insert-link.hidden").length, 1);
|
||||
assert.equal(this.value, "hello world.");
|
||||
});
|
||||
|
||||
testCase("link modal (simple link)", async function(assert, textarea) {
|
||||
await click("button.link");
|
||||
|
||||
const url = "http://eviltrout.com";
|
||||
|
||||
await fillIn(".insert-link input.link-url", url);
|
||||
await click(".insert-link button.btn-primary");
|
||||
assert.equal(find(".insert-link.hidden").length, 1);
|
||||
assert.equal(this.value, `hello world.[${url}](${url})`);
|
||||
assert.equal(textarea.selectionStart, 13);
|
||||
assert.equal(textarea.selectionEnd, 13 + url.length);
|
||||
});
|
||||
|
||||
testCase("link modal auto http addition", async function(assert) {
|
||||
await click("button.link");
|
||||
await fillIn(".insert-link input.link-url", "sam.com");
|
||||
await click(".insert-link button.btn-primary");
|
||||
assert.equal(this.value, `hello world.[sam.com](http://sam.com)`);
|
||||
});
|
||||
|
||||
testCase("link modal (simple link) with selected text", async function(
|
||||
assert,
|
||||
textarea
|
||||
) {
|
||||
textarea.selectionStart = 0;
|
||||
textarea.selectionEnd = 12;
|
||||
|
||||
await click("button.link");
|
||||
assert.equal(find("input.link-text")[0].value, "hello world.");
|
||||
|
||||
await fillIn(".insert-link input.link-url", "http://eviltrout.com");
|
||||
await click(".insert-link button.btn-primary");
|
||||
assert.equal(find(".insert-link.hidden").length, 1);
|
||||
assert.equal(this.value, "[hello world.](http://eviltrout.com)");
|
||||
});
|
||||
|
||||
testCase("link modal (link with description)", async function(assert) {
|
||||
await click("button.link");
|
||||
await fillIn(".insert-link input.link-url", "http://eviltrout.com");
|
||||
await fillIn(".insert-link input.link-text", "evil trout");
|
||||
await click(".insert-link button.btn-primary");
|
||||
assert.equal(find(".insert-link.hidden").length, 1);
|
||||
assert.equal(this.value, "hello world.[evil trout](http://eviltrout.com)");
|
||||
});
|
||||
|
||||
componentTest("advanced code", {
|
||||
template: "{{d-editor value=value}}",
|
||||
beforeEach() {
|
||||
@@ -673,7 +618,6 @@ testCase(`doesn't jump to bottom with long text`, async function(
|
||||
});
|
||||
|
||||
componentTest("emoji", {
|
||||
skip: true,
|
||||
template: "{{d-editor value=value}}",
|
||||
beforeEach() {
|
||||
// Test adding a custom button
|
||||
@@ -696,7 +640,7 @@ componentTest("emoji", {
|
||||
await click(
|
||||
'.emoji-picker .section[data-section="smileys_&_emotion"] button.emoji[title="grinning"]'
|
||||
);
|
||||
assert.equal(this.value, "hello world.:grinning:");
|
||||
assert.equal(this.value, "hello world. :grinning:");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -704,7 +648,7 @@ testCase("replace-text event by default", async function(assert) {
|
||||
this.set("value", "red green blue");
|
||||
|
||||
await this.container
|
||||
.lookup("app-events:main")
|
||||
.lookup("service:app-events")
|
||||
.trigger("composer:replace-text", "green", "yellow");
|
||||
|
||||
assert.equal(this.value, "red green blue");
|
||||
@@ -714,7 +658,7 @@ composerTestCase("replace-text event for composer", async function(assert) {
|
||||
this.set("value", "red green blue");
|
||||
|
||||
await this.container
|
||||
.lookup("app-events:main")
|
||||
.lookup("service:app-events")
|
||||
.trigger("composer:replace-text", "green", "yellow");
|
||||
|
||||
assert.equal(this.value, "red yellow blue");
|
||||
@@ -800,10 +744,10 @@ composerTestCase("replace-text event for composer", async function(assert) {
|
||||
setTextareaSelection(textarea, start, start + len);
|
||||
|
||||
this.container
|
||||
.lookup("app-events:main")
|
||||
.lookup("service:app-events")
|
||||
.trigger("composer:replace-text", "green", "yellow", { forceFocus: true });
|
||||
|
||||
Ember.run.next(() => {
|
||||
next(() => {
|
||||
let expect = formatTextWithSelection(AFTER, CASE.after);
|
||||
let actual = formatTextWithSelection(
|
||||
this.value,
|
||||
|
||||
@@ -25,7 +25,7 @@ componentTest("with replacement", {
|
||||
.trim();
|
||||
assert.equal(
|
||||
html,
|
||||
'<svg class="fa d-icon d-icon-d-watching svg-icon svg-string" xmlns="http://www.w3.org/2000/svg"><use xlink:href="#exclamation-circle"></use></svg>'
|
||||
'<svg class="fa d-icon d-icon-d-watching svg-icon svg-string" xmlns="http://www.w3.org/2000/svg"><use xlink:href="#discourse-bell-exclamation"></use></svg>'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("date-input", { integration: true });
|
||||
|
||||
function dateInput() {
|
||||
return find(".date-picker");
|
||||
}
|
||||
|
||||
function setDate(date) {
|
||||
this.set("date", date);
|
||||
}
|
||||
|
||||
async function pika(year, month, day) {
|
||||
await click(
|
||||
`.pika-button.pika-day[data-pika-year="${year}"][data-pika-month="${month}"][data-pika-day="${day}"]`
|
||||
);
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
const DEFAULT_DATE = new Date(2019, 0, 29);
|
||||
|
||||
componentTest("default", {
|
||||
template: `{{date-input date=date}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(dateInput().val(), "January 29, 2019");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("prevents mutations", {
|
||||
template: `{{date-input date=date onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE });
|
||||
this.set("onChange", noop);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(dateInput());
|
||||
await pika(2019, 0, 2);
|
||||
|
||||
assert.ok(this.date.getTime() === DEFAULT_DATE.getTime());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("allows mutations through actions", {
|
||||
template: `{{date-input date=date onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE });
|
||||
this.set("onChange", setDate);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(dateInput());
|
||||
await pika(2019, 0, 2);
|
||||
|
||||
assert.ok(this.date.getTime() === new Date(2019, 0, 2).getTime());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("date-time-input-range", { integration: true });
|
||||
|
||||
function fromDateInput() {
|
||||
return find(".from .date-picker");
|
||||
}
|
||||
|
||||
function fromHoursInput() {
|
||||
return find(".from .field.hours");
|
||||
}
|
||||
|
||||
function fromMinutesInput() {
|
||||
return find(".from .field.minutes");
|
||||
}
|
||||
|
||||
function toDateInput() {
|
||||
return find(".to .date-picker");
|
||||
}
|
||||
|
||||
function toHoursInput() {
|
||||
return find(".to .field.hours");
|
||||
}
|
||||
|
||||
function toMinutesInput() {
|
||||
return find(".to .field.minutes");
|
||||
}
|
||||
|
||||
function setDates(dates) {
|
||||
this.setProperties(dates);
|
||||
}
|
||||
|
||||
async function pika(year, month, day) {
|
||||
await click(
|
||||
`.pika-button.pika-day[data-pika-year="${year}"][data-pika-month="${month}"][data-pika-day="${day}"]`
|
||||
);
|
||||
}
|
||||
|
||||
const DEFAULT_DATE_TIME = new Date(2019, 0, 29, 14, 45);
|
||||
|
||||
componentTest("default", {
|
||||
template: `{{date-time-input-range from=from to=to}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ from: DEFAULT_DATE_TIME, to: null });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(fromDateInput().val(), "January 29, 2019");
|
||||
assert.equal(fromHoursInput().val(), "14");
|
||||
assert.equal(fromMinutesInput().val(), "45");
|
||||
|
||||
assert.equal(toDateInput().val(), "");
|
||||
assert.equal(toHoursInput().val(), "");
|
||||
assert.equal(toMinutesInput().val(), "");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("can switch panels", {
|
||||
template: `{{date-time-input-range}}`,
|
||||
|
||||
async test(assert) {
|
||||
assert.ok(exists(".panel.from.visible"));
|
||||
assert.notOk(exists(".panel.to.visible"));
|
||||
|
||||
await click(".panels button.to-panel");
|
||||
|
||||
assert.ok(exists(".panel.to.visible"));
|
||||
assert.notOk(exists(".panel.from.visible"));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("prevents toDate to be before fromDate", {
|
||||
template: `{{date-time-input-range from=from to=to onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({
|
||||
from: DEFAULT_DATE_TIME,
|
||||
to: DEFAULT_DATE_TIME,
|
||||
onChange: setDates
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.notOk(exists(".error"), "it begins with no error");
|
||||
|
||||
await click(".panels button.to-panel");
|
||||
await click(toDateInput());
|
||||
await pika(2019, 0, 1);
|
||||
|
||||
assert.ok(exists(".error"), "it shows an error");
|
||||
assert.deepEqual(this.to, DEFAULT_DATE_TIME, "it didnt trigger a mutation");
|
||||
|
||||
await click(".panels button.to-panel");
|
||||
await click(toDateInput());
|
||||
await pika(2019, 0, 30);
|
||||
|
||||
assert.notOk(exists(".error"), "it removes the error");
|
||||
assert.deepEqual(
|
||||
this.to,
|
||||
new Date(2019, 0, 30, 14, 45),
|
||||
"it has changed the date"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("date-time-input", { integration: true });
|
||||
|
||||
function dateInput() {
|
||||
return find(".date-picker");
|
||||
}
|
||||
|
||||
function hoursInput() {
|
||||
return find(".field.hours");
|
||||
}
|
||||
|
||||
function minutesInput() {
|
||||
return find(".field.minutes");
|
||||
}
|
||||
|
||||
function setDate(date) {
|
||||
this.set("date", date);
|
||||
}
|
||||
|
||||
async function pika(year, month, day) {
|
||||
await click(
|
||||
`.pika-button.pika-day[data-pika-year="${year}"][data-pika-month="${month}"][data-pika-day="${day}"]`
|
||||
);
|
||||
}
|
||||
|
||||
const DEFAULT_DATE_TIME = new Date(2019, 0, 29, 14, 45);
|
||||
|
||||
componentTest("default", {
|
||||
template: `{{date-time-input date=date}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE_TIME });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(dateInput().val(), "January 29, 2019");
|
||||
assert.equal(hoursInput().val(), "14");
|
||||
assert.equal(minutesInput().val(), "45");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("prevents mutations", {
|
||||
template: `{{date-time-input date=date}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE_TIME });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(dateInput());
|
||||
await pika(2019, 0, 2);
|
||||
|
||||
assert.ok(this.date.getTime() === DEFAULT_DATE_TIME.getTime());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("allows mutations through actions", {
|
||||
template: `{{date-time-input date=date onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE_TIME });
|
||||
this.set("onChange", setDate);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(dateInput());
|
||||
await pika(2019, 0, 2);
|
||||
|
||||
assert.ok(this.date.getTime() === new Date(2019, 0, 2, 14, 45).getTime());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("can hide time", {
|
||||
template: `{{date-time-input date=date showTime=false}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE_TIME });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.notOk(exists(hoursInput()));
|
||||
}
|
||||
});
|
||||
@@ -1,94 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("list-setting", { integration: true });
|
||||
|
||||
componentTest("default", {
|
||||
template: "{{list-setting settingValue=settingValue choices=choices}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("settingValue", "bold|italic");
|
||||
this.set("choices", ["bold", "italic", "underline"]);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(
|
||||
selectKit()
|
||||
.header()
|
||||
.title(),
|
||||
"bold,italic"
|
||||
);
|
||||
assert.equal(
|
||||
selectKit()
|
||||
.header()
|
||||
.value(),
|
||||
"bold,italic"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with empty string as value", {
|
||||
template: "{{list-setting settingValue=settingValue}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("settingValue", "");
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(
|
||||
selectKit()
|
||||
.header()
|
||||
.value(),
|
||||
""
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with only setting value", {
|
||||
template: "{{list-setting settingValue=settingValue}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("settingValue", "bold|italic");
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(
|
||||
selectKit()
|
||||
.header()
|
||||
.value(),
|
||||
"bold,italic"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("interactions", {
|
||||
template: "{{list-setting settingValue=settingValue choices=choices}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("settingValue", "bold|italic");
|
||||
this.set("choices", ["bold", "italic", "underline"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
const listSetting = selectKit();
|
||||
|
||||
await listSetting.expand();
|
||||
await listSetting.selectRowByValue("underline");
|
||||
|
||||
assert.equal(listSetting.header().value(), "bold,italic,underline");
|
||||
|
||||
await listSetting.expand();
|
||||
await listSetting.fillInFilter("strike");
|
||||
|
||||
assert.equal(listSetting.highlightedRow().value(), "strike");
|
||||
|
||||
await listSetting.keyboard("enter");
|
||||
|
||||
assert.equal(listSetting.header().value(), "bold,italic,underline,strike");
|
||||
|
||||
await listSetting.keyboard("backspace");
|
||||
await listSetting.keyboard("backspace");
|
||||
|
||||
assert.equal(listSetting.header().value(), "bold,italic,underline");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { configureEyeline } from "discourse/lib/eyeline";
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("load-more", { integration: true });
|
||||
|
||||
componentTest("updates once after initialization", {
|
||||
template: `
|
||||
{{#load-more selector=".numbers tr" action=loadMore}}
|
||||
<table class="numbers"><tr></tr></table>
|
||||
{{/load-more}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.set("loadMore", () => this.set("loadedMore", true));
|
||||
configureEyeline({
|
||||
skipUpdate: false,
|
||||
rootElement: Discourse.rootElement
|
||||
});
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
configureEyeline();
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(this.loadedMore);
|
||||
}
|
||||
});
|
||||
@@ -1,115 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("mini-tag-chooser", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("default", {
|
||||
template: "{{mini-tag-chooser allowAny=true filterable=true tags=tags}}",
|
||||
|
||||
beforeEach() {
|
||||
this.siteSettings.max_tag_length = 24;
|
||||
this.siteSettings.force_lowercase_tags = true;
|
||||
|
||||
this.site.set("can_create_tag", true);
|
||||
this.set("tags", ["jeff", "neil", "arpit"]);
|
||||
|
||||
const response = object => {
|
||||
return [200, { "Content-Type": "application/json" }, object];
|
||||
};
|
||||
|
||||
// prettier-ignore
|
||||
server.get("/tags/filter/search", (params) => { //eslint-disable-line
|
||||
if (params.queryParams.q === "régis") {
|
||||
return response({
|
||||
results: [{ text: "régis", count: 5 }]
|
||||
});
|
||||
}
|
||||
|
||||
if (params.queryParams.q.toLowerCase() === "joffrey" || params.queryParams.q === "invalid'tag" || params.queryParams.q === "01234567890123456789012345") {
|
||||
return response({results: []});
|
||||
}
|
||||
|
||||
return response({
|
||||
results: [{ text: "bianca", count: 3 }, { text: "régis", count: 5 }]
|
||||
});
|
||||
});
|
||||
},
|
||||
skip: true,
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(0).name(),
|
||||
"bianca",
|
||||
"it has the correct tag"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(1).name(),
|
||||
"régis",
|
||||
"it has the correct tag"
|
||||
);
|
||||
|
||||
await this.subject.fillInFilter("régis");
|
||||
await this.subject.keyboard("enter");
|
||||
assert.deepEqual(
|
||||
this.tags,
|
||||
["jeff", "neil", "arpit", "régis"],
|
||||
"it selects the tag"
|
||||
);
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("joffrey");
|
||||
await this.subject.keyboard("enter");
|
||||
assert.deepEqual(
|
||||
this.tags,
|
||||
["jeff", "neil", "arpit", "régis", "joffrey"],
|
||||
"it creates the tag"
|
||||
);
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("Joffrey");
|
||||
await this.subject.keyboard("enter");
|
||||
await this.subject.collapse();
|
||||
assert.deepEqual(
|
||||
this.tags,
|
||||
["jeff", "neil", "arpit", "régis", "joffrey"],
|
||||
"it does not allow case insensitive duplicate tags"
|
||||
);
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("invalid' Tag");
|
||||
await this.subject.keyboard("enter");
|
||||
assert.deepEqual(
|
||||
this.tags,
|
||||
["jeff", "neil", "arpit", "régis", "joffrey", "invalid-tag"],
|
||||
"it strips invalid characters in tag"
|
||||
);
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("01234567890123456789012345");
|
||||
await this.subject.keyboard("enter");
|
||||
assert.deepEqual(
|
||||
this.tags,
|
||||
["jeff", "neil", "arpit", "régis", "joffrey", "invalid-tag"],
|
||||
"it does not allow creating long tags"
|
||||
);
|
||||
|
||||
await click(
|
||||
this.subject
|
||||
.el()
|
||||
.find(".selected-tag")
|
||||
.last()
|
||||
);
|
||||
assert.deepEqual(
|
||||
this.tags,
|
||||
["jeff", "neil", "arpit", "régis", "joffrey"],
|
||||
"it removes the tag"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,320 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import { clearCallbacks } from "select-kit/mixins/plugin-api";
|
||||
|
||||
moduleForComponent("multi-select", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with objects and values", {
|
||||
template: "{{multi-select content=items values=values}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("items", [{ id: 1, name: "hello" }, { id: 2, name: "world" }]);
|
||||
this.set("values", [1, 2]);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().value(), "1,2");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with title", {
|
||||
template: '{{multi-select title=(i18n "test.title")}}',
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { title: "My title" };
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(
|
||||
selectKit()
|
||||
.header()
|
||||
.title(),
|
||||
"My title"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("interactions", {
|
||||
template: "{{multi-select none=none content=items values=values}}",
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { none: "none" };
|
||||
this.set("items", [
|
||||
{ id: 1, name: "regis" },
|
||||
{ id: 2, name: "sam" },
|
||||
{ id: 3, name: "robin" }
|
||||
]);
|
||||
this.set("values", [1, 2]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.highlightedRow().name(),
|
||||
"robin",
|
||||
"it highlights the first content row"
|
||||
);
|
||||
|
||||
await this.set("none", "test.none");
|
||||
|
||||
assert.ok(this.subject.noneRow().exists());
|
||||
assert.equal(
|
||||
this.subject.highlightedRow().name(),
|
||||
"robin",
|
||||
"it highlights the first content row"
|
||||
);
|
||||
|
||||
await this.subject.selectRowByValue(3);
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.highlightedRow().name(),
|
||||
"none",
|
||||
"it highlights none row if no content"
|
||||
);
|
||||
|
||||
await this.subject.fillInFilter("joffrey");
|
||||
|
||||
assert.equal(
|
||||
this.subject.highlightedRow().name(),
|
||||
"joffrey",
|
||||
"it highlights create row when filling filter"
|
||||
);
|
||||
|
||||
await this.subject.keyboard("enter");
|
||||
|
||||
assert.equal(
|
||||
this.subject.highlightedRow().name(),
|
||||
"none",
|
||||
"it highlights none row after creating content and no content left"
|
||||
);
|
||||
|
||||
await this.subject.keyboard("backspace");
|
||||
|
||||
const $lastSelectedName = this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".selected-name")
|
||||
.last();
|
||||
assert.equal($lastSelectedName.attr("data-name"), "joffrey");
|
||||
assert.ok(
|
||||
$lastSelectedName.hasClass("is-highlighted"),
|
||||
"it highlights the last selected name when using backspace"
|
||||
);
|
||||
|
||||
await this.subject.keyboard("backspace");
|
||||
|
||||
const $lastSelectedName1 = this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".selected-name")
|
||||
.last();
|
||||
assert.equal(
|
||||
$lastSelectedName1.attr("data-name"),
|
||||
"robin",
|
||||
"it removes the previous highlighted selected content"
|
||||
);
|
||||
assert.notOk(
|
||||
this.subject.rowByValue("joffrey").exists(),
|
||||
"generated content shouldn’t appear in content when removed"
|
||||
);
|
||||
|
||||
await this.subject.keyboard("selectAll");
|
||||
|
||||
const $highlightedSelectedNames2 = this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".selected-name.is-highlighted");
|
||||
assert.equal(
|
||||
$highlightedSelectedNames2.length,
|
||||
3,
|
||||
"it highlights each selected name"
|
||||
);
|
||||
|
||||
await this.subject.keyboard("backspace");
|
||||
|
||||
const $selectedNames = this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".selected-name");
|
||||
assert.equal($selectedNames.length, 0, "it removed all selected content");
|
||||
|
||||
assert.ok(this.subject.isFocused());
|
||||
assert.ok(this.subject.isExpanded());
|
||||
|
||||
await this.subject.keyboard("escape");
|
||||
|
||||
assert.ok(this.subject.isFocused());
|
||||
assert.notOk(this.subject.isExpanded());
|
||||
|
||||
await this.subject.keyboard("escape");
|
||||
|
||||
assert.notOk(this.subject.isFocused());
|
||||
assert.notOk(this.subject.isExpanded());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with limitMatches", {
|
||||
template: "{{multi-select content=content limitMatches=2}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["sam", "jeff", "neil"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.el().find(".select-kit-row").length, 2);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with minimum", {
|
||||
template: "{{multi-select content=content minimum=1}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["sam", "jeff", "neil"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.validationMessage(), "Select at least 1 item.");
|
||||
|
||||
await this.subject.selectRowByValue("sam");
|
||||
|
||||
assert.equal(this.subject.header().label(), "sam");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with minimumLabel", {
|
||||
template:
|
||||
'{{multi-select content=content minimum=1 minimumLabel="test.minimum"}}',
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { minimum: "min %{count}" };
|
||||
this.set("content", ["sam", "jeff", "neil"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.validationMessage(), "min 1");
|
||||
|
||||
await this.subject.selectRowByValue("jeff");
|
||||
|
||||
assert.equal(this.subject.header().label(), "jeff");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with forceEscape", {
|
||||
template: "{{multi-select content=content forceEscape=true}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["<div>sam</div>"]);
|
||||
},
|
||||
skip: true,
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const row = this.subject.rowByIndex(0);
|
||||
assert.equal(
|
||||
row
|
||||
.el()
|
||||
.find(".name")
|
||||
.html()
|
||||
.trim(),
|
||||
"<div>sam</div>"
|
||||
);
|
||||
|
||||
await this.subject.fillInFilter("<div>jeff</div>");
|
||||
await this.subject.keyboard("enter");
|
||||
|
||||
assert.equal(
|
||||
this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".name")
|
||||
.html()
|
||||
.trim(),
|
||||
"<div>jeff</div>"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with forceEscape", {
|
||||
template: "{{multi-select content=content forceEscape=false}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["<div>sam</div>"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const row = this.subject.rowByIndex(0);
|
||||
assert.equal(
|
||||
row
|
||||
.el()
|
||||
.find(".name")
|
||||
.html()
|
||||
.trim(),
|
||||
"<div>sam</div>"
|
||||
);
|
||||
|
||||
await this.subject.fillInFilter("<div>jeff</div>");
|
||||
await this.subject.keyboard("enter");
|
||||
|
||||
assert.equal(
|
||||
this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".name")
|
||||
.html()
|
||||
.trim(),
|
||||
"<div>jeff</div>"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("support modifying on select behavior through plugin api", {
|
||||
template:
|
||||
'<span class="on-select-test"></span>{{multi-select content=content}}',
|
||||
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.13", api => {
|
||||
api.modifySelectKit("select-kit").onSelect((context, value) => {
|
||||
find(".on-select-test").html(value);
|
||||
});
|
||||
});
|
||||
|
||||
this.set("content", [
|
||||
{ id: "1", name: "robin" },
|
||||
{ id: "2", name: "arpit", __sk_row_type: "noopRow" }
|
||||
]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue(1);
|
||||
|
||||
assert.equal(find(".on-select-test").html(), "1");
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue(2);
|
||||
|
||||
assert.equal(
|
||||
find(".on-select-test").html(),
|
||||
"2",
|
||||
"it calls onSelect for noopRows"
|
||||
);
|
||||
|
||||
clearCallbacks();
|
||||
}
|
||||
});
|
||||
+44
-33
@@ -1,25 +1,34 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
import { testSelectKitModule } from "./select-kit-test-helper";
|
||||
|
||||
moduleForComponent("category-chooser", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
testSelectKitModule("category-chooser");
|
||||
|
||||
function template(options = []) {
|
||||
return `
|
||||
{{category-chooser
|
||||
value=value
|
||||
options=(hash
|
||||
${options.join("\n")}
|
||||
)
|
||||
}}
|
||||
`;
|
||||
}
|
||||
|
||||
componentTest("with value", {
|
||||
template: "{{category-chooser value=2}}",
|
||||
template: template(),
|
||||
|
||||
test(assert) {
|
||||
beforeEach() {
|
||||
this.set("value", 2);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(this.subject.header().value(), 2);
|
||||
assert.equal(this.subject.header().title(), "feature");
|
||||
assert.equal(this.subject.header().label(), "feature");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with excludeCategoryId", {
|
||||
template: "{{category-chooser excludeCategoryId=2}}",
|
||||
|
||||
template: template(["excludeCategoryId=2"]),
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
@@ -28,7 +37,7 @@ componentTest("with excludeCategoryId", {
|
||||
});
|
||||
|
||||
componentTest("with scopedCategoryId", {
|
||||
template: "{{category-chooser scopedCategoryId=2}}",
|
||||
template: template(["scopedCategoryId=2"]),
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
@@ -43,16 +52,20 @@ componentTest("with scopedCategoryId", {
|
||||
"My idea here is to have mini specs for features we would like built but have no bandwidth to build"
|
||||
);
|
||||
assert.equal(this.subject.rowByIndex(1).value(), 26);
|
||||
assert.equal(this.subject.rows().length, 2);
|
||||
assert.equal(this.subject.rows().length, 2, "default content is scoped");
|
||||
|
||||
await this.subject.fillInFilter("spec");
|
||||
await this.subject.fillInFilter("bug");
|
||||
|
||||
assert.equal(this.subject.rows().length, 1);
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(0).name(),
|
||||
"bug",
|
||||
"search finds outside of scope"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with allowUncategorized=null", {
|
||||
template: "{{category-chooser allowUncategorized=null}}",
|
||||
template: template(["allowUncategorized=null"]),
|
||||
|
||||
beforeEach() {
|
||||
this.siteSettings.allow_uncategorized_topics = false;
|
||||
@@ -60,12 +73,12 @@ componentTest("with allowUncategorized=null", {
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().value(), null);
|
||||
assert.equal(this.subject.header().title(), "category");
|
||||
assert.equal(this.subject.header().label(), "category…");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with allowUncategorized=null rootNone=true", {
|
||||
template: "{{category-chooser allowUncategorized=null rootNone=true}}",
|
||||
template: template(["allowUncategorized=null", "none=true"]),
|
||||
|
||||
beforeEach() {
|
||||
this.siteSettings.allow_uncategorized_topics = false;
|
||||
@@ -73,13 +86,12 @@ componentTest("with allowUncategorized=null rootNone=true", {
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().value(), null);
|
||||
assert.equal(this.subject.header().title(), "category");
|
||||
assert.equal(this.subject.header().label(), "(no category)");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with disallowed uncategorized, rootNone and rootNoneLabel", {
|
||||
template:
|
||||
'{{category-chooser allowUncategorized=null rootNone=true rootNoneLabel="test.root"}}',
|
||||
componentTest("with disallowed uncategorized, none", {
|
||||
template: template(["allowUncategorized=null", "none='test.root'"]),
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { root: "root none label" };
|
||||
@@ -88,12 +100,12 @@ componentTest("with disallowed uncategorized, rootNone and rootNoneLabel", {
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().value(), null);
|
||||
assert.equal(this.subject.header().title(), "category");
|
||||
assert.equal(this.subject.header().label(), "root none label");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with allowed uncategorized", {
|
||||
template: "{{category-chooser allowUncategorized=true}}",
|
||||
template: template(["allowUncategorized=true"]),
|
||||
|
||||
beforeEach() {
|
||||
this.siteSettings.allow_uncategorized_topics = true;
|
||||
@@ -101,12 +113,12 @@ componentTest("with allowed uncategorized", {
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().value(), null);
|
||||
assert.equal(this.subject.header().title(), "uncategorized");
|
||||
assert.equal(this.subject.header().label(), "uncategorized");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with allowed uncategorized and rootNone", {
|
||||
template: "{{category-chooser allowUncategorized=true rootNone=true}}",
|
||||
componentTest("with allowed uncategorized and none=true", {
|
||||
template: template(["allowUncategorized=true", "none=true"]),
|
||||
|
||||
beforeEach() {
|
||||
this.siteSettings.allow_uncategorized_topics = true;
|
||||
@@ -114,13 +126,12 @@ componentTest("with allowed uncategorized and rootNone", {
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().value(), null);
|
||||
assert.equal(this.subject.header().title(), "(no category)");
|
||||
assert.equal(this.subject.header().label(), "(no category)");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with allowed uncategorized rootNone and rootNoneLabel", {
|
||||
template:
|
||||
'{{category-chooser allowUncategorized=true rootNone=true rootNoneLabel="test.root"}}',
|
||||
componentTest("with allowed uncategorized and none", {
|
||||
template: template(["allowUncategorized=true", "none='test.root'"]),
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { root: "root none label" };
|
||||
@@ -129,6 +140,6 @@ componentTest("with allowed uncategorized rootNone and rootNoneLabel", {
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().value(), null);
|
||||
assert.equal(this.subject.header().title(), "root none label");
|
||||
assert.equal(this.subject.header().label(), "root none label");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,337 @@
|
||||
import Category from "discourse/models/category";
|
||||
import componentTest from "helpers/component-test";
|
||||
import { testSelectKitModule } from "./select-kit-test-helper";
|
||||
import {
|
||||
NO_CATEGORIES_ID,
|
||||
ALL_CATEGORIES_ID
|
||||
} from "select-kit/components/category-drop";
|
||||
import { set } from "@ember/object";
|
||||
|
||||
testSelectKitModule("category-drop");
|
||||
|
||||
function initCategories(context) {
|
||||
const categories = context.site.categoriesList;
|
||||
context.setProperties({
|
||||
category: categories.firstObject,
|
||||
categories
|
||||
});
|
||||
}
|
||||
|
||||
function initCategoriesWithParentCategory(context) {
|
||||
const parentCategory = Category.findById(2);
|
||||
const childCategories = context.site.categoriesList.filter(c => {
|
||||
return c.parentCategory === parentCategory;
|
||||
});
|
||||
|
||||
context.setProperties({
|
||||
parentCategory,
|
||||
category: null,
|
||||
categories: childCategories
|
||||
});
|
||||
}
|
||||
|
||||
function template(options = []) {
|
||||
return `
|
||||
{{category-drop
|
||||
category=category
|
||||
categories=categories
|
||||
parentCategory=parentCategory
|
||||
options=(hash
|
||||
${options.join("\n")}
|
||||
)
|
||||
}}
|
||||
`;
|
||||
}
|
||||
|
||||
componentTest("caretUpIcon", {
|
||||
template: `
|
||||
{{category-drop
|
||||
category=value
|
||||
categories=content
|
||||
}}
|
||||
`,
|
||||
|
||||
async test(assert) {
|
||||
const $header = this.subject.header().el();
|
||||
|
||||
assert.ok(
|
||||
exists($header.find(`.d-icon-caret-right`)),
|
||||
"it uses the correct default icon"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("none", {
|
||||
template: `
|
||||
{{category-drop
|
||||
category=value
|
||||
categories=content
|
||||
}}
|
||||
`,
|
||||
|
||||
async test(assert) {
|
||||
const text = this.subject.header().label();
|
||||
assert.equal(
|
||||
text,
|
||||
I18n.t("category.all").toLowerCase(),
|
||||
"it uses the noneLabel"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("[not staff - TL0] displayCategoryDescription", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
set(this.currentUser, "staff", false);
|
||||
set(this.currentUser, "trustLevel", 0);
|
||||
|
||||
initCategories(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const row = this.subject.rowByValue(this.category.id);
|
||||
assert.ok(
|
||||
exists(row.el().find(".category-desc")),
|
||||
"it shows category description for newcomers"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("[not staff - TL1] displayCategoryDescription", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
set(this.currentUser, "staff", false);
|
||||
set(this.currentUser, "trustLevel", 1);
|
||||
|
||||
initCategories(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const row = this.subject.rowByValue(this.category.id);
|
||||
assert.ok(
|
||||
exists(row.el().find(".category-desc")),
|
||||
"it doesn't show category description for TL0+"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("[staff - TL0] displayCategoryDescription", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
set(this.currentUser, "staff", true);
|
||||
set(this.currentUser, "trustLevel", 0);
|
||||
|
||||
initCategories(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const row = this.subject.rowByValue(this.category.id);
|
||||
assert.ok(
|
||||
exists(row.el().find(".category-desc")),
|
||||
"it doesn't show category description for staff"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("hideParentCategory (default: false)", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
initCategories(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const row = this.subject.rowByValue(this.category.id);
|
||||
assert.equal(row.value(), this.category.id);
|
||||
assert.equal(this.category.parent_category_id, null);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("hideParentCategory (true)", {
|
||||
template: template(["hideParentCategory=true"]),
|
||||
|
||||
beforeEach() {
|
||||
initCategoriesWithParentCategory(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const parentRow = this.subject.rowByValue(this.parentCategory.id);
|
||||
assert.notOk(parentRow.exists(), "the parent row is not showing");
|
||||
|
||||
const childCategory = this.categories.firstObject;
|
||||
const childCategoryId = childCategory.id;
|
||||
const childRow = this.subject.rowByValue(childCategoryId);
|
||||
assert.ok(childRow.exists(), "the child row is showing");
|
||||
|
||||
const $categoryStatus = childRow.el().find(".category-status");
|
||||
assert.ok(
|
||||
$categoryStatus
|
||||
.text()
|
||||
.trim()
|
||||
.match(/^spec/)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("allowUncategorized (default: true)", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
initCategories(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const uncategorizedCategoryId = this.site.uncategorized_category_id;
|
||||
const row = this.subject.rowByValue(uncategorizedCategoryId);
|
||||
assert.ok(row.exists(), "the uncategorized row is showing");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("allowUncategorized (false)", {
|
||||
template: template(["allowUncategorized=false"]),
|
||||
|
||||
beforeEach() {
|
||||
initCategories(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const uncategorizedCategoryId = this.site.uncategorized_category_id;
|
||||
const row = this.subject.rowByValue(uncategorizedCategoryId);
|
||||
assert.notOk(row.exists(), "the uncategorized row is not showing");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("countSubcategories (default: false)", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
initCategories(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const category = Category.findById(7);
|
||||
const row = this.subject.rowByValue(category.id);
|
||||
const topicCount = row
|
||||
.el()
|
||||
.find(".topic-count")
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
assert.equal(
|
||||
topicCount,
|
||||
"× 481",
|
||||
"it doesn't include the topic count of subcategories"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("countSubcategories (true)", {
|
||||
template: template(["countSubcategories=true"]),
|
||||
|
||||
beforeEach() {
|
||||
initCategories(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const category = Category.findById(7);
|
||||
const row = this.subject.rowByValue(category.id);
|
||||
const topicCount = row
|
||||
.el()
|
||||
.find(".topic-count")
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
assert.equal(
|
||||
topicCount,
|
||||
"× 584",
|
||||
"it includes the topic count of subcategories"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("shortcuts:default", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
initCategories(this);
|
||||
this.set("category", null);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(0).value(),
|
||||
this.categories.firstObject.id,
|
||||
"Shortcuts are not prepended when no category is selected"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("shortcuts:category is set", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
initCategories(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rowByIndex(0).value(), ALL_CATEGORIES_ID);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("shortcuts with parentCategory/subCategory=true:default", {
|
||||
template: template(["subCategory=true"]),
|
||||
|
||||
beforeEach() {
|
||||
initCategoriesWithParentCategory(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rowByIndex(0).value(), NO_CATEGORIES_ID);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest(
|
||||
"shortcuts with parentCategory/subCategory=true:category is selected",
|
||||
{
|
||||
template: template(["subCategory=true"]),
|
||||
|
||||
beforeEach() {
|
||||
initCategoriesWithParentCategory(this);
|
||||
this.set("category", this.categories.firstObject);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rowByIndex(0).value(), ALL_CATEGORIES_ID);
|
||||
assert.equal(this.subject.rowByIndex(1).value(), NO_CATEGORIES_ID);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,102 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("select-kit/combo-box", {
|
||||
integration: true,
|
||||
beforeEach() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
const DEFAULT_CONTENT = [
|
||||
{ id: 1, name: "foo" },
|
||||
{ id: 2, name: "bar" },
|
||||
{ id: 3, name: "baz" }
|
||||
];
|
||||
|
||||
const DEFAULT_VALUE = 1;
|
||||
|
||||
const setDefaultState = (ctx, options) => {
|
||||
const properties = Object.assign(
|
||||
{
|
||||
content: DEFAULT_CONTENT,
|
||||
value: DEFAULT_VALUE
|
||||
},
|
||||
options || {}
|
||||
);
|
||||
ctx.setProperties(properties);
|
||||
};
|
||||
|
||||
componentTest("options.clearable", {
|
||||
template: `
|
||||
{{combo-box
|
||||
value=value
|
||||
content=content
|
||||
onChange=onChange
|
||||
options=(hash clearable=clearable)
|
||||
}}
|
||||
`,
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this, {
|
||||
clearable: true,
|
||||
onChange: value => {
|
||||
this.set("value", value);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
const $header = this.subject.header();
|
||||
|
||||
assert.ok(
|
||||
exists($header.el().find(".btn-clear")),
|
||||
"it shows the clear button"
|
||||
);
|
||||
assert.equal($header.value(), DEFAULT_VALUE);
|
||||
|
||||
await click($header.el().find(".btn-clear"));
|
||||
|
||||
assert.notOk(
|
||||
exists($header.el().find(".btn-clear")),
|
||||
"it hides the clear button"
|
||||
);
|
||||
assert.equal($header.value(), null);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("options.{caretUpIcon,caretDownIcon}", {
|
||||
template: `
|
||||
{{combo-box
|
||||
value=value
|
||||
content=content
|
||||
options=(hash
|
||||
caretUpIcon=caretUpIcon
|
||||
caretDownIcon=caretDownIcon
|
||||
)
|
||||
}}
|
||||
`,
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this, {
|
||||
caretUpIcon: "pencil-alt",
|
||||
caretDownIcon: "trash-alt"
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
const $header = this.subject.header().el();
|
||||
|
||||
assert.ok(
|
||||
exists($header.find(`.d-icon-${this.caretDownIcon}`)),
|
||||
"it uses the icon provided"
|
||||
);
|
||||
|
||||
await this.subject.expand();
|
||||
|
||||
assert.ok(
|
||||
exists($header.find(`.d-icon-${this.caretUpIcon}`)),
|
||||
"it uses the icon provided"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,111 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("select-kit/dropdown-select-box", {
|
||||
integration: true,
|
||||
beforeEach() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
const DEFAULT_CONTENT = [
|
||||
{ id: 1, name: "foo" },
|
||||
{ id: 2, name: "bar" },
|
||||
{ id: 3, name: "baz" }
|
||||
];
|
||||
|
||||
const DEFAULT_VALUE = 1;
|
||||
|
||||
const setDefaultState = (ctx, options) => {
|
||||
const properties = Object.assign(
|
||||
{
|
||||
content: DEFAULT_CONTENT,
|
||||
value: DEFAULT_VALUE,
|
||||
onChange: value => {
|
||||
this.set("value", value);
|
||||
}
|
||||
},
|
||||
options || {}
|
||||
);
|
||||
ctx.setProperties(properties);
|
||||
};
|
||||
|
||||
componentTest("selection behavior", {
|
||||
template: `
|
||||
{{dropdown-select-box
|
||||
value=value
|
||||
content=content
|
||||
}}
|
||||
`,
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
assert.ok(this.subject.isExpanded());
|
||||
|
||||
await this.subject.selectRowByValue(DEFAULT_VALUE);
|
||||
assert.notOk(
|
||||
this.subject.isExpanded(),
|
||||
"it collapses the dropdown on select"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("options.showFullTitle=false", {
|
||||
template: `
|
||||
{{dropdown-select-box
|
||||
value=value
|
||||
content=content
|
||||
options=(hash
|
||||
showFullTitle=showFullTitle
|
||||
)
|
||||
}}
|
||||
`,
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this, { showFullTitle: false });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.ok(
|
||||
!exists(
|
||||
this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".selected-name .body")
|
||||
),
|
||||
"it hides the text of the selected item"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("options.showFullTitle=true", {
|
||||
template: `
|
||||
{{dropdown-select-box
|
||||
value=value
|
||||
content=content
|
||||
options=(hash
|
||||
showFullTitle=showFullTitle
|
||||
)
|
||||
}}
|
||||
`,
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this, { showFullTitle: true });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.ok(
|
||||
exists(
|
||||
this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".selected-name")
|
||||
),
|
||||
"it shows the text of the selected item"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
import { testSelectKitModule } from "./select-kit-test-helper";
|
||||
|
||||
testSelectKitModule("list-setting");
|
||||
|
||||
function template(options = []) {
|
||||
return `
|
||||
{{list-setting
|
||||
value=value
|
||||
choices=choices
|
||||
options=(hash
|
||||
${options.join("\n")}
|
||||
)
|
||||
}}
|
||||
`;
|
||||
}
|
||||
|
||||
componentTest("default", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", ["bold", "italic"]);
|
||||
this.set("choices", ["bold", "italic", "underline"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(this.subject.header().name(), "bold,italic");
|
||||
assert.equal(this.subject.header().value(), "bold,italic");
|
||||
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rows().length, 1);
|
||||
assert.equal(this.subject.rowByIndex(0).value(), "underline");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
import { testSelectKitModule } from "./select-kit-test-helper";
|
||||
|
||||
testSelectKitModule("mini-tag-chooser");
|
||||
|
||||
function template() {
|
||||
return `{{mini-tag-chooser value=value}}`;
|
||||
}
|
||||
|
||||
componentTest("displays tags", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", ["foo", "bar"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(this.subject.header().value(), "foo,bar");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("create a tag", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", ["foo", "bar"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(this.subject.header().value(), "foo,bar");
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("monkey");
|
||||
await this.subject.keyboard("enter");
|
||||
|
||||
assert.equal(this.subject.header().value(), "foo,bar,monkey");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("max_tags_per_topic", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", ["foo", "bar"]);
|
||||
this.siteSettings.max_tags_per_topic = 2;
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(this.subject.header().value(), "foo,bar");
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("baz");
|
||||
await this.subject.keyboard("enter");
|
||||
|
||||
const error = find(".select-kit-error").text();
|
||||
assert.equal(
|
||||
error,
|
||||
I18n.t("select_kit.max_content_reached", {
|
||||
count: this.siteSettings.max_tags_per_topic
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
import { testSelectKitModule } from "./select-kit-test-helper";
|
||||
|
||||
testSelectKitModule("multi-select");
|
||||
|
||||
function template(options = []) {
|
||||
return `
|
||||
{{multi-select
|
||||
value=value
|
||||
content=content
|
||||
options=(hash
|
||||
${options.join("\n")}
|
||||
)
|
||||
}}
|
||||
`;
|
||||
}
|
||||
|
||||
const DEFAULT_CONTENT = [
|
||||
{ id: 1, name: "foo" },
|
||||
{ id: 2, name: "bar" },
|
||||
{ id: 3, name: "baz" }
|
||||
];
|
||||
|
||||
const setDefaultState = (ctx, options) => {
|
||||
const properties = Object.assign(
|
||||
{
|
||||
content: DEFAULT_CONTENT,
|
||||
value: null
|
||||
},
|
||||
options || {}
|
||||
);
|
||||
ctx.setProperties(properties);
|
||||
};
|
||||
|
||||
componentTest("content", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const content = this.subject.displayedContent();
|
||||
assert.equal(content.length, 3, "it shows rows");
|
||||
assert.equal(
|
||||
content[0].name,
|
||||
this.content.firstObject.name,
|
||||
"it has the correct name"
|
||||
);
|
||||
assert.equal(
|
||||
content[0].id,
|
||||
this.content.firstObject.id,
|
||||
"it has the correct value"
|
||||
);
|
||||
assert.equal(
|
||||
this.subject.header().value(),
|
||||
null,
|
||||
"it doesn't set a value from the content"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
import { testSelectKitModule, setDefaultState } from "./select-kit-test-helper";
|
||||
|
||||
testSelectKitModule("notifications-button");
|
||||
|
||||
componentTest("default", {
|
||||
template: `
|
||||
{{notifications-button
|
||||
value=value
|
||||
options=(hash
|
||||
i18nPrefix=i18nPrefix
|
||||
i18nPostfix=i18nPostfix
|
||||
)
|
||||
}}
|
||||
`,
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", 1);
|
||||
|
||||
setDefaultState(this, 1, { i18nPrefix: "pre", i18nPostfix: "post" });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.ok(this.subject.header().value());
|
||||
|
||||
assert.ok(
|
||||
this.subject
|
||||
.header()
|
||||
.label()
|
||||
.includes(`${this.i18nPrefix}.regular${this.i18nPostfix}`),
|
||||
"it shows the regular choice when value is not set"
|
||||
);
|
||||
|
||||
const icon = this.subject.header().icon()[0];
|
||||
assert.ok(
|
||||
icon.classList.contains("d-icon-d-regular"),
|
||||
"it shows the correct icon"
|
||||
);
|
||||
}
|
||||
});
|
||||
+3
-2
@@ -11,7 +11,7 @@ const buildTopic = function() {
|
||||
});
|
||||
};
|
||||
|
||||
moduleForComponent("pinned-options", {
|
||||
moduleForComponent("select-kit/pinned-options", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
@@ -24,12 +24,13 @@ componentTest("updating the content refreshes the list", {
|
||||
beforeEach() {
|
||||
this.siteSettings.automatically_unpin_topics = false;
|
||||
this.set("topic", buildTopic());
|
||||
this.set("pinned", true);
|
||||
this.set("pinned", "pinned");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(this.subject.header().name(), "pinned");
|
||||
|
||||
// we do it manually as clearPin is an ajax call
|
||||
await this.set("pinned", false);
|
||||
|
||||
assert.equal(this.subject.header().name(), "unpinned");
|
||||
@@ -0,0 +1,35 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
|
||||
export function testSelectKitModule(moduleName, options = {}) {
|
||||
moduleForComponent(`select-kit/${moduleName}`, {
|
||||
integration: true,
|
||||
|
||||
beforeEach() {
|
||||
this.set("subject", selectKit());
|
||||
options.beforeEach && options.beforeEach.call(this);
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
options.afterEach && options.afterEach.call(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const DEFAULT_CONTENT = [
|
||||
{ id: 1, name: "foo" },
|
||||
{ id: 2, name: "bar" },
|
||||
{ id: 3, name: "baz" }
|
||||
];
|
||||
|
||||
export function setDefaultState(ctx, value, options = {}) {
|
||||
const properties = Object.assign(
|
||||
{
|
||||
onChange: v => {
|
||||
this.set("value", v);
|
||||
}
|
||||
},
|
||||
options || {}
|
||||
);
|
||||
|
||||
ctx.setProperties(properties);
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
import { testSelectKitModule } from "./select-kit-test-helper";
|
||||
|
||||
testSelectKitModule("single-select");
|
||||
|
||||
function template(options = []) {
|
||||
return `
|
||||
{{single-select
|
||||
value=value
|
||||
content=content
|
||||
nameProperty=nameProperty
|
||||
valueProperty=valueProperty
|
||||
onChange=onChange
|
||||
options=(hash
|
||||
${options.join("\n")}
|
||||
)
|
||||
}}
|
||||
`;
|
||||
}
|
||||
|
||||
const DEFAULT_CONTENT = [
|
||||
{ id: 1, name: "foo" },
|
||||
{ id: 2, name: "bar" },
|
||||
{ id: 3, name: "baz" }
|
||||
];
|
||||
|
||||
const DEFAULT_VALUE = 1;
|
||||
|
||||
const setDefaultState = (ctx, options) => {
|
||||
const properties = Object.assign(
|
||||
{
|
||||
content: DEFAULT_CONTENT,
|
||||
value: DEFAULT_VALUE,
|
||||
nameProperty: "name",
|
||||
valueProperty: "id",
|
||||
onChange: value => {
|
||||
ctx.set("value", value);
|
||||
}
|
||||
},
|
||||
options || {}
|
||||
);
|
||||
ctx.setProperties(properties);
|
||||
};
|
||||
|
||||
componentTest("content", {
|
||||
template: "{{single-select content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const content = this.subject.displayedContent();
|
||||
assert.equal(content.length, 3, "it shows rows");
|
||||
assert.equal(
|
||||
content[0].name,
|
||||
this.content.firstObject.name,
|
||||
"it has the correct name"
|
||||
);
|
||||
assert.equal(
|
||||
content[0].id,
|
||||
this.content.firstObject.id,
|
||||
"it has the correct value"
|
||||
);
|
||||
assert.equal(
|
||||
this.subject.header().value(),
|
||||
null,
|
||||
"it doesn't set a value from the content"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("value", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(
|
||||
this.subject.header().value(this.content),
|
||||
1,
|
||||
"it selects the correct content to display"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("options.filterable", {
|
||||
template: template(["filterable=filterable"]),
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this, { filterable: true });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
assert.ok(this.subject.filter().exists(), "it shows the filter");
|
||||
|
||||
const filter = this.subject.displayedContent()[1].name;
|
||||
await this.subject.fillInFilter(filter);
|
||||
assert.equal(
|
||||
this.subject.displayedContent()[0].name,
|
||||
filter,
|
||||
"it filters the list"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("options.limitMatches", {
|
||||
template: template(["limitMatches=limitMatches", "filterable=filterable"]),
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this, { limitMatches: 1, filterable: true });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("ba");
|
||||
|
||||
assert.equal(
|
||||
this.subject.displayedContent().length,
|
||||
1,
|
||||
"it returns only 1 result"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("valueAttribute (deprecated)", {
|
||||
template: `
|
||||
{{single-select
|
||||
value=value
|
||||
content=content
|
||||
valueAttribute="value"
|
||||
}}
|
||||
`,
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", "normal");
|
||||
|
||||
const content = [
|
||||
{ name: "Smaller", value: "smaller" },
|
||||
{ name: "Normal", value: "normal" },
|
||||
{ name: "Larger", value: "larger" },
|
||||
{ name: "Largest", value: "largest" }
|
||||
];
|
||||
this.set("content", content);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.selectedRow().value(), this.value);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("none:string", {
|
||||
template: template(['none="test.none"']),
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { none: "(default)" };
|
||||
setDefaultState(this, { value: 1 });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const noneRow = this.subject.rowByIndex(0);
|
||||
assert.equal(noneRow.value(), null);
|
||||
assert.equal(noneRow.name(), I18n.t("test.none"));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("none:object", {
|
||||
template: template(["none=none"]),
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this, { none: { value: null, name: "(default)" } });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const noneRow = this.subject.rowByIndex(0);
|
||||
assert.equal(noneRow.value(), null);
|
||||
assert.equal(noneRow.name(), "(default)");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("content is a basic array", {
|
||||
template: template(['none="test.none"']),
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { none: "(default)" };
|
||||
setDefaultState(this, {
|
||||
nameProperty: null,
|
||||
valueProperty: null,
|
||||
value: "foo",
|
||||
content: ["foo", "bar", "baz"]
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const noneRow = this.subject.rowByIndex(0);
|
||||
assert.equal(noneRow.value(), I18n.t("test.none"));
|
||||
assert.equal(noneRow.name(), I18n.t("test.none"));
|
||||
assert.equal(this.value, "foo");
|
||||
|
||||
await this.subject.selectRowByIndex(0);
|
||||
|
||||
assert.equal(this.value, null);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("selected value can be 0", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
setDefaultState(this, {
|
||||
value: 1,
|
||||
content: [
|
||||
{ id: 0, name: "foo" },
|
||||
{ id: 1, name: "bar" }
|
||||
]
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(this.subject.header().value(), 1);
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue(0);
|
||||
|
||||
assert.equal(this.subject.header().value(), 0);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
import { testSelectKitModule } from "./select-kit-test-helper";
|
||||
import Site from "discourse/models/site";
|
||||
import { set } from "@ember/object";
|
||||
|
||||
testSelectKitModule("tag-drop", {
|
||||
beforeEach() {
|
||||
const site = Site.current();
|
||||
set(site, "top_tags", ["jeff", "neil", "arpit", "régis"]);
|
||||
|
||||
const response = object => {
|
||||
return [200, { "Content-Type": "application/json" }, object];
|
||||
};
|
||||
|
||||
// prettier-ignore
|
||||
server.get("/tags/filter/search", (params) => { //eslint-disable-line
|
||||
if (params.queryParams.q === "rég") {
|
||||
return response({
|
||||
"results": [
|
||||
{ "id": "régis", "text": "régis", "count": 2, "pm_count": 0 }
|
||||
]
|
||||
});
|
||||
}else if (params.queryParams.q === "dav") {
|
||||
return response({
|
||||
"results": [
|
||||
{ "id": "David", "text": "David", "count": 2, "pm_count": 0 }
|
||||
]
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function initTags(context) {
|
||||
const categories = context.site.categoriesList;
|
||||
const parentCategory = categories.findBy("id", 2);
|
||||
const childCategories = categories.filter(
|
||||
c => c.parentCategory === parentCategory
|
||||
);
|
||||
|
||||
// top_tags
|
||||
context.setProperties({
|
||||
firstCategory: parentCategory,
|
||||
secondCategory: childCategories.firstObject,
|
||||
tagId: "jeff"
|
||||
});
|
||||
}
|
||||
|
||||
function template(options = []) {
|
||||
return `
|
||||
{{tag-drop
|
||||
firstCategory=firstCategory
|
||||
secondCategory=secondCategory
|
||||
tagId=tagId
|
||||
options=(hash
|
||||
${options.join("\n")}
|
||||
)
|
||||
}}
|
||||
`;
|
||||
}
|
||||
|
||||
componentTest("default", {
|
||||
template: template(["tagId=tagId"]),
|
||||
|
||||
beforeEach() {
|
||||
initTags(this);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.ok(true);
|
||||
// const row = this.subject.rowByValue(this.category.id);
|
||||
// assert.ok(
|
||||
// exists(row.el().find(".category-desc")),
|
||||
// "it shows category description for newcomers"
|
||||
// );
|
||||
|
||||
const content = this.subject.displayedContent();
|
||||
|
||||
assert.equal(
|
||||
content[0].name,
|
||||
I18n.t("tagging.selector_no_tags"),
|
||||
"it has the translated label for no-tags"
|
||||
);
|
||||
assert.equal(
|
||||
content[1].name,
|
||||
I18n.t("tagging.selector_all_tags"),
|
||||
"it has the correct label for all-tags"
|
||||
);
|
||||
}
|
||||
});
|
||||
+7
-7
@@ -16,7 +16,7 @@ const buildTopic = function(level, archetype = "regular") {
|
||||
const originalTranslation =
|
||||
I18n.translations.en.js.topic.notifications.tracking_pm.title;
|
||||
|
||||
moduleForComponent("topic-notifications-button", {
|
||||
moduleForComponent("select-kit/topic-notifications-button", {
|
||||
integration: true,
|
||||
|
||||
afterEach() {
|
||||
@@ -36,9 +36,9 @@ componentTest("the header has a localized title", {
|
||||
assert.equal(
|
||||
selectKit()
|
||||
.header()
|
||||
.name(),
|
||||
.label(),
|
||||
"Normal",
|
||||
"it has the correct title"
|
||||
"it has the correct label"
|
||||
);
|
||||
|
||||
await this.set("topic", buildTopic(2));
|
||||
@@ -46,9 +46,9 @@ componentTest("the header has a localized title", {
|
||||
assert.equal(
|
||||
selectKit()
|
||||
.header()
|
||||
.name(),
|
||||
.label(),
|
||||
"Tracking",
|
||||
"it correctly changes the title"
|
||||
"it correctly changes the label"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -66,9 +66,9 @@ componentTest("the header has a localized title", {
|
||||
assert.equal(
|
||||
selectKit()
|
||||
.header()
|
||||
.name(),
|
||||
.label(),
|
||||
`${originalTranslation} PM`,
|
||||
"it has the correct title for PMs"
|
||||
"it has the correct label for PMs"
|
||||
);
|
||||
}
|
||||
});
|
||||
+11
-3
@@ -27,15 +27,20 @@ function getTranslations(type = "") {
|
||||
});
|
||||
}
|
||||
|
||||
moduleForComponent("topic-notifications-options", { integration: true });
|
||||
moduleForComponent("select-kit/topic-notifications-options", {
|
||||
integration: true
|
||||
});
|
||||
|
||||
componentTest("regular topic notification level descriptions", {
|
||||
template:
|
||||
"{{topic-notifications-options value=topic.details.notification_level topic=topic}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("topic", buildTopic("regular"));
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await selectKit().expand();
|
||||
await this.set("topic", buildTopic("regular"));
|
||||
|
||||
const uiTexts = extractDescs(selectKit().rows());
|
||||
const descriptions = getTranslations();
|
||||
@@ -59,9 +64,12 @@ componentTest("PM topic notification level descriptions", {
|
||||
template:
|
||||
"{{topic-notifications-options value=topic.details.notification_level topic=topic}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("topic", buildTopic("private_message"));
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await selectKit().expand();
|
||||
await this.set("topic", buildTopic("private_message"));
|
||||
|
||||
const uiTexts = extractDescs(selectKit().rows());
|
||||
const descriptions = getTranslations("_pm");
|
||||
@@ -0,0 +1,58 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
import { testSelectKitModule } from "./select-kit-test-helper";
|
||||
|
||||
testSelectKitModule("user-chooser");
|
||||
|
||||
function template() {
|
||||
return `{{user-chooser value=value}}`;
|
||||
}
|
||||
|
||||
componentTest("displays usernames", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", ["bob", "martin"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.equal(this.subject.header().name(), "bob,martin");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("can remove a username", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", ["bob", "martin"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.deselectItem("bob");
|
||||
assert.equal(this.subject.header().name(), "martin");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("can add a username", {
|
||||
template: template(),
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", ["bob", "martin"]);
|
||||
|
||||
const response = object => {
|
||||
return [200, { "Content-Type": "application/json" }, object];
|
||||
};
|
||||
|
||||
// prettier-ignore
|
||||
server.get("/u/search/users", () => { //eslint-disable-line
|
||||
return response({users:[{username: "maja", name: "Maja"}]});
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("maja");
|
||||
await this.subject.keyboard("enter");
|
||||
|
||||
assert.equal(this.subject.header().name(), "bob,martin,maja");
|
||||
}
|
||||
});
|
||||
@@ -1,813 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import { clearCallbacks } from "select-kit/mixins/plugin-api";
|
||||
|
||||
moduleForComponent("single-select", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("updating the content refreshes the list", {
|
||||
template: "{{single-select value=1 content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", [{ id: 1, name: "BEFORE" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rowByValue(1).name(), "BEFORE");
|
||||
|
||||
await this.set("content", [{ id: 1, name: "AFTER" }]);
|
||||
|
||||
assert.equal(this.subject.rowByValue(1).name(), "AFTER");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("accepts a value by reference", {
|
||||
template: "{{single-select value=value content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", 1);
|
||||
this.set("content", [{ id: 1, name: "robin" }, { id: 2, name: "regis" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.selectedRow().name(),
|
||||
"robin",
|
||||
"it highlights the row corresponding to the value"
|
||||
);
|
||||
|
||||
await this.subject.selectRowByValue(1);
|
||||
|
||||
assert.equal(this.value, 1, "it mutates the value");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("no default icon", {
|
||||
template: "{{single-select}}",
|
||||
|
||||
test(assert) {
|
||||
assert.equal(
|
||||
this.subject.header().icon().length,
|
||||
0,
|
||||
"it doesn’t have an icon if not specified"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("default search icon", {
|
||||
template: "{{single-select filterable=true}}",
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.ok(exists(this.subject.filter().icon()), "it has an icon");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with no search icon", {
|
||||
template: "{{single-select filterable=true filterIcon=null}}",
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.notOk(exists(this.subject.filter().icon()), "it has no icon");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("custom search icon", {
|
||||
template: '{{single-select filterable=true filterIcon="shower"}}',
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.ok(
|
||||
this.subject
|
||||
.filter()
|
||||
.icon()
|
||||
.hasClass("d-icon-shower"),
|
||||
"it has a the correct icon"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("is expandable", {
|
||||
template: "{{single-select}}",
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.ok(this.subject.isExpanded());
|
||||
|
||||
await this.subject.collapse();
|
||||
|
||||
assert.notOk(this.subject.isExpanded());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("accepts custom value/name keys", {
|
||||
template:
|
||||
'{{single-select value=value nameProperty="item" content=content valueAttribute="identifier"}}',
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", 1);
|
||||
this.set("content", [{ identifier: 1, item: "robin" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.selectedRow().name(), "robin");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("doesn’t render collection content before first expand", {
|
||||
template: "{{single-select value=1 content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", [{ value: 1, name: "robin" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.notOk(exists(find(".select-kit-collection")));
|
||||
|
||||
await this.subject.expand();
|
||||
|
||||
assert.ok(exists(find(".select-kit-collection")));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("dynamic headerText", {
|
||||
template: "{{single-select value=1 content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", [{ id: 1, name: "robin" }, { id: 2, name: "regis" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.header().name(), "robin");
|
||||
|
||||
await this.subject.selectRowByValue(2);
|
||||
|
||||
assert.equal(
|
||||
this.subject.header().name(),
|
||||
"regis",
|
||||
"it changes header text"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("supports custom row template", {
|
||||
template: "{{single-select content=content templateForRow=templateForRow}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", [{ id: 1, name: "robin" }]);
|
||||
this.set("templateForRow", rowComponent => {
|
||||
return `<b>${rowComponent.get("computedContent.name")}</b>`;
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject
|
||||
.rowByValue(1)
|
||||
.el()
|
||||
.html()
|
||||
.trim(),
|
||||
"<b>robin</b>"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("supports converting select value to integer", {
|
||||
template: "{{single-select value=value content=content castInteger=true}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", 2);
|
||||
this.set("content", [
|
||||
{ id: "1", name: "robin" },
|
||||
{ id: "2", name: "régis" }
|
||||
]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.selectedRow().name(), "régis");
|
||||
|
||||
await this.set("value", 1);
|
||||
|
||||
assert.equal(
|
||||
this.subject.selectedRow().name(),
|
||||
"robin",
|
||||
"it works with dynamic content"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("supports converting string as boolean to boolean", {
|
||||
template: "{{single-select value=value content=content castBoolean=true}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", true);
|
||||
this.set("content", [
|
||||
{ id: "true", name: "ASC" },
|
||||
{ id: "false", name: "DESC" }
|
||||
]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.selectedRow().name(), "ASC");
|
||||
|
||||
await this.set("value", false);
|
||||
|
||||
assert.equal(
|
||||
this.subject.selectedRow().name(),
|
||||
"DESC",
|
||||
"it works with dynamic content"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("supports keyboard events", {
|
||||
template: "{{single-select content=content filterable=true}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", [{ id: 1, name: "robin" }, { id: 2, name: "regis" }]);
|
||||
},
|
||||
|
||||
skip: true,
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.keyboard("down");
|
||||
|
||||
assert.equal(
|
||||
this.subject.highlightedRow().title(),
|
||||
"regis",
|
||||
"the next row is highlighted"
|
||||
);
|
||||
|
||||
await this.subject.keyboard("down");
|
||||
|
||||
assert.equal(
|
||||
this.subject.highlightedRow().title(),
|
||||
"robin",
|
||||
"it returns to the first row"
|
||||
);
|
||||
|
||||
await this.subject.keyboard("up");
|
||||
|
||||
assert.equal(
|
||||
this.subject.highlightedRow().title(),
|
||||
"regis",
|
||||
"it highlights the last row"
|
||||
);
|
||||
|
||||
await this.subject.keyboard("enter");
|
||||
|
||||
assert.equal(
|
||||
this.subject.selectedRow().title(),
|
||||
"regis",
|
||||
"it selects the row when pressing enter"
|
||||
);
|
||||
assert.notOk(
|
||||
this.subject.isExpanded(),
|
||||
"it collapses the select box when selecting a row"
|
||||
);
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.keyboard("escape");
|
||||
|
||||
assert.notOk(this.subject.isExpanded(), "it collapses the select box");
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("regis");
|
||||
await this.subject.keyboard("tab");
|
||||
|
||||
assert.notOk(
|
||||
this.subject.isExpanded(),
|
||||
"it collapses the select box when selecting a row"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with allowInitialValueMutation", {
|
||||
template:
|
||||
"{{single-select value=value content=content allowInitialValueMutation=true}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", "");
|
||||
this.set("content", [
|
||||
{ id: "1", name: "robin" },
|
||||
{ id: "2", name: "régis" }
|
||||
]);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.value, "1", "it mutates the value on initial rendering");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("support appending content through plugin api", {
|
||||
template: "{{single-select content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.13", api => {
|
||||
api
|
||||
.modifySelectKit("select-kit")
|
||||
.appendContent([{ id: "2", name: "regis" }]);
|
||||
});
|
||||
|
||||
this.set("content", [{ id: "1", name: "robin" }]);
|
||||
},
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rows().length, 2);
|
||||
assert.equal(this.subject.rowByIndex(1).name(), "regis");
|
||||
|
||||
clearCallbacks();
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("support modifying content through plugin api", {
|
||||
template: "{{single-select content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.13", api => {
|
||||
api
|
||||
.modifySelectKit("select-kit")
|
||||
.modifyContent((context, existingContent) => {
|
||||
existingContent.splice(1, 0, { id: "2", name: "sam" });
|
||||
return existingContent;
|
||||
});
|
||||
});
|
||||
|
||||
this.set("content", [
|
||||
{ id: "1", name: "robin" },
|
||||
{ id: "3", name: "regis" }
|
||||
]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rows().length, 3);
|
||||
assert.equal(this.subject.rowByIndex(1).name(), "sam");
|
||||
|
||||
clearCallbacks();
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("support prepending content through plugin api", {
|
||||
template: "{{single-select content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.13", api => {
|
||||
api
|
||||
.modifySelectKit("select-kit")
|
||||
.prependContent([{ id: "2", name: "regis" }]);
|
||||
});
|
||||
|
||||
this.set("content", [{ id: "1", name: "robin" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.rows().length, 2);
|
||||
assert.equal(this.subject.rowByIndex(0).name(), "regis");
|
||||
|
||||
clearCallbacks();
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("support modifying on select behavior through plugin api", {
|
||||
template:
|
||||
'<span class="on-select-test"></span>{{single-select content=content}}',
|
||||
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.13", api => {
|
||||
api.modifySelectKit("select-kit").onSelect((context, value) => {
|
||||
find(".on-select-test").html(value);
|
||||
});
|
||||
});
|
||||
|
||||
this.set("content", [
|
||||
{ id: "1", name: "robin" },
|
||||
{ id: "2", name: "arpit", __sk_row_type: "noopRow" }
|
||||
]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue(1);
|
||||
|
||||
assert.equal(find(".on-select-test").html(), "1");
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue(2);
|
||||
|
||||
assert.equal(
|
||||
find(".on-select-test").html(),
|
||||
"2",
|
||||
"it calls onSelect for noopRows"
|
||||
);
|
||||
|
||||
clearCallbacks();
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("support modifying on select none behavior through plugin api", {
|
||||
template:
|
||||
'<span class="on-select-none-test"></span>{{single-select none="none" content=content}}',
|
||||
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.25", api => {
|
||||
api.modifySelectKit("select-kit").onSelectNone(() => {
|
||||
find(".on-select-none-test").html("NONE");
|
||||
});
|
||||
});
|
||||
|
||||
this.set("content", [{ id: "1", name: "robin" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue(1);
|
||||
await this.subject.expand();
|
||||
await this.subject.selectNoneRow();
|
||||
|
||||
assert.equal(find(".on-select-none-test").html(), "NONE");
|
||||
|
||||
clearCallbacks();
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with nameChanges", {
|
||||
template: "{{single-select content=content nameChanges=true}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("robin", { id: "1", name: "robin" });
|
||||
this.set("content", [this.robin]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.header().name(), "robin");
|
||||
|
||||
await this.set("robin.name", "robin2");
|
||||
|
||||
assert.equal(this.subject.header().name(), "robin2");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with null value", {
|
||||
template: "{{single-select content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", [{ name: "robin" }]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.header().name(), "robin");
|
||||
assert.equal(this.subject.header().value(), undefined);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with collection header", {
|
||||
template: "{{single-select collectionHeader=collectionHeader}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("collectionHeader", "<h2>Hello</h2>");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.ok(exists(".collection-header h2"));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with title", {
|
||||
template: '{{single-select title=(i18n "test.title")}}',
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { title: "My title" };
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().title(), "My title");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("support modifying header computed content through plugin api", {
|
||||
template: "{{single-select content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.15", api => {
|
||||
api
|
||||
.modifySelectKit("select-kit")
|
||||
.modifyHeaderComputedContent((context, computedContent) => {
|
||||
computedContent.title = "Not so evil";
|
||||
return computedContent;
|
||||
});
|
||||
});
|
||||
|
||||
this.set("content", [{ id: "1", name: "robin" }]);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.subject.header().title(), "Not so evil");
|
||||
|
||||
clearCallbacks();
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with limitMatches", {
|
||||
template: "{{single-select content=content limitMatches=2}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["sam", "jeff", "neil"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.el().find(".select-kit-row").length, 2);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with minimum", {
|
||||
template:
|
||||
"{{single-select content=content minimum=1 allowAutoSelectFirst=false}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["sam", "jeff", "neil"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.validationMessage(), "Select at least 1 item.");
|
||||
|
||||
await this.subject.selectRowByValue("sam");
|
||||
|
||||
assert.equal(this.subject.header().label(), "sam");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with minimumLabel", {
|
||||
template:
|
||||
'{{single-select content=content minimum=1 minimumLabel="test.minimum" allowAutoSelectFirst=false}}',
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { minimum: "min %{count}" };
|
||||
this.set("content", ["sam", "jeff", "neil"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.validationMessage(), "min 1");
|
||||
|
||||
await this.subject.selectRowByValue("jeff");
|
||||
|
||||
assert.equal(this.subject.header().label(), "jeff");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with accents in filter", {
|
||||
template: "{{single-select content=content filterable=true}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["sam", "jeff", "neil"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("jéff");
|
||||
|
||||
assert.equal(this.subject.rows().length, 1);
|
||||
assert.equal(this.subject.rowByIndex(0).name(), "jeff");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with accents in content", {
|
||||
template: "{{single-select content=content filterable=true}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["sam", "jéff", "neil"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.fillInFilter("jeff");
|
||||
|
||||
assert.equal(this.subject.rows().length, 1);
|
||||
assert.equal(this.subject.rowByIndex(0).name(), "jéff");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with no content and allowAny", {
|
||||
template: "{{single-select allowAny=true}}",
|
||||
|
||||
skip: true,
|
||||
async test(assert) {
|
||||
await click(this.subject.header().el());
|
||||
|
||||
const $filter = this.subject.filter().el();
|
||||
|
||||
assert.ok($filter.hasClass("is-focused"));
|
||||
assert.ok(!$filter.hasClass("is-hidden"));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with forceEscape", {
|
||||
template: "{{single-select content=content forceEscape=true}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["<div>sam</div>"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const row = this.subject.rowByIndex(0);
|
||||
assert.equal(
|
||||
row
|
||||
.el()
|
||||
.find(".name")
|
||||
.html()
|
||||
.trim(),
|
||||
"<div>sam</div>"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".selected-name")
|
||||
.html()
|
||||
.trim(),
|
||||
"<div>sam</div>"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("without forceEscape", {
|
||||
template: "{{single-select content=content forceEscape=false}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", ["<div>sam</div>"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
const row = this.subject.rowByIndex(0);
|
||||
assert.equal(
|
||||
row
|
||||
.el()
|
||||
.find(".name")
|
||||
.html()
|
||||
.trim(),
|
||||
"<div>sam</div>"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
this.subject
|
||||
.header()
|
||||
.el()
|
||||
.find(".selected-name")
|
||||
.html()
|
||||
.trim(),
|
||||
"<div>sam</div>"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("onSelect", {
|
||||
template:
|
||||
"<div class='test-external-action'></div>{{single-select content=content onSelect=(action externalAction)}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("externalAction", actual => {
|
||||
find(".test-external-action").text(actual);
|
||||
});
|
||||
|
||||
this.set("content", ["red", "blue"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue("red");
|
||||
|
||||
assert.equal(
|
||||
find(".test-external-action")
|
||||
.text()
|
||||
.trim(),
|
||||
"red"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("onDeselect", {
|
||||
template:
|
||||
"<div class='test-external-action'></div>{{single-select content=content onDeselect=(action externalAction)}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("externalAction", actual => {
|
||||
find(".test-external-action").text(actual);
|
||||
});
|
||||
|
||||
this.set("content", ["red", "blue"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue("red");
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue("blue");
|
||||
|
||||
assert.equal(
|
||||
find(".test-external-action")
|
||||
.text()
|
||||
.trim(),
|
||||
"red"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("noopRow", {
|
||||
template: "{{single-select value=value content=content}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("value", "blue");
|
||||
this.set("content", [
|
||||
{ id: "red", name: "Red", __sk_row_type: "noopRow" },
|
||||
"blue",
|
||||
"green"
|
||||
]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue("red");
|
||||
assert.equal(this.value, "blue", "it doesn’t change the value");
|
||||
|
||||
await this.subject.expand();
|
||||
await this.subject.selectRowByValue("green");
|
||||
assert.equal(this.value, "green");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("onSelectAny", {
|
||||
template: `<div class='test-external-action'></div>{{single-select none="none" content=content onSelectAny=(action externalAction)}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.set("externalAction", actual => {
|
||||
find(".test-external-action").text(actual.value);
|
||||
});
|
||||
|
||||
this.set("content", ["blue"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.get("subject").expand();
|
||||
await this.get("subject").selectRowByValue("blue");
|
||||
|
||||
assert.equal(
|
||||
find(".test-external-action")
|
||||
.text()
|
||||
.trim(),
|
||||
"blue"
|
||||
);
|
||||
|
||||
await this.get("subject").expand();
|
||||
await this.get("subject").selectNoneRow();
|
||||
|
||||
assert.equal(
|
||||
find(".test-external-action")
|
||||
.text()
|
||||
.trim(),
|
||||
"__none__"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,97 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
||||
moduleForComponent("tag-drop", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("default", {
|
||||
template: "{{tag-drop}}",
|
||||
|
||||
beforeEach() {
|
||||
this.site.set("can_create_tag", true);
|
||||
this.set("site.top_tags", ["jeff", "neil", "arpit", "régis"]);
|
||||
|
||||
const response = object => {
|
||||
return [200, { "Content-Type": "application/json" }, object];
|
||||
};
|
||||
|
||||
// prettier-ignore
|
||||
server.get("/tags/filter/search", (params) => { //eslint-disable-line
|
||||
if (params.queryParams.q === "rég") {
|
||||
return response({
|
||||
"results": [
|
||||
{ "id": "régis", "text": "régis", "count": 2, "pm_count": 0 }
|
||||
]
|
||||
});
|
||||
}else if (params.queryParams.q === "dav") {
|
||||
return response({
|
||||
"results": [
|
||||
{ "id": "David", "text": "David", "count": 2, "pm_count": 0 }
|
||||
]
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(1).name(),
|
||||
"jeff",
|
||||
"it has the correct tag"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(2).name(),
|
||||
"neil",
|
||||
"it has the correct tag"
|
||||
);
|
||||
|
||||
await this.subject.fillInFilter("rég");
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(0).name(),
|
||||
"régis",
|
||||
"it displays the searched tag"
|
||||
);
|
||||
|
||||
await this.subject.fillInFilter("");
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(1).name(),
|
||||
"jeff",
|
||||
"it returns top tags for an empty search"
|
||||
);
|
||||
|
||||
sandbox.stub(DiscourseURL, "routeTo");
|
||||
await this.subject.fillInFilter("dav");
|
||||
await this.subject.keyboard("enter");
|
||||
assert.ok(
|
||||
DiscourseURL.routeTo.calledWith("/tags/david"),
|
||||
"it uses lowercase URLs for tags"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("no tags", {
|
||||
template: "{{tag-drop}}",
|
||||
|
||||
beforeEach() {
|
||||
this.site.set("can_create_tag", true);
|
||||
this.set("site.top_tags", undefined);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(
|
||||
this.subject.rowByIndex(1).name(),
|
||||
undefined,
|
||||
"it has no tags and doesn’t crash"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("text-overflow", { integration: true });
|
||||
|
||||
componentTest("default", {
|
||||
template: `
|
||||
<style>
|
||||
.overflow {
|
||||
max-height: 40px;
|
||||
overflow: hidden;
|
||||
width: 500px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>{{text-overflow class='overflow' text=text}}</div>`,
|
||||
|
||||
beforeEach() {
|
||||
this.set(
|
||||
"text",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nFusce convallis faucibus tortor quis vestibulum.<br>\nPhasellus pharetra dolor eget imperdiet tempor.<br>\nQuisque hendrerit magna id consectetur rutrum.<br>\nNulla vel tortor leo.<br>\nFusce ullamcorper lacus quis sodales ornare.<br>"
|
||||
);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
const text = find(".overflow")
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
assert.ok(text.startsWith("Lorem ipsum dolor sit amet"));
|
||||
assert.ok(text.endsWith("..."));
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("time-input", { integration: true });
|
||||
|
||||
function hoursInput() {
|
||||
return find(".field.hours");
|
||||
}
|
||||
|
||||
function minutesInput() {
|
||||
return find(".field.minutes");
|
||||
}
|
||||
|
||||
function setTime(time) {
|
||||
this.setProperties(time);
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
componentTest("default", {
|
||||
template: `{{time-input hours=hours minutes=minutes}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ hours: "14", minutes: "58" });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(hoursInput().val(), "14");
|
||||
assert.equal(minutesInput().val(), "58");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("prevents mutations", {
|
||||
template: `{{time-input hours=hours minutes=minutes}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ hours: "14", minutes: "58" });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await fillIn(hoursInput(), "12");
|
||||
assert.ok(this.hours === "14");
|
||||
|
||||
await fillIn(minutesInput(), "36");
|
||||
assert.ok(this.minutes === "58");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("allows mutations through actions", {
|
||||
template: `{{time-input hours=hours minutes=minutes onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ hours: "14", minutes: "58" });
|
||||
this.set("onChange", setTime);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await fillIn(hoursInput(), "12");
|
||||
assert.ok(this.hours === "12");
|
||||
|
||||
await fillIn(minutesInput(), "36");
|
||||
assert.ok(this.minutes === "36");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("hours and minutes have boundaries", {
|
||||
template: `{{time-input hours=14 minutes=58 onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.set("onChange", noop);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await fillIn(hoursInput(), "2");
|
||||
assert.equal(hoursInput().val(), "02");
|
||||
|
||||
await fillIn(hoursInput(), "@");
|
||||
assert.equal(hoursInput().val(), "00");
|
||||
|
||||
await fillIn(hoursInput(), "24");
|
||||
assert.equal(hoursInput().val(), "23");
|
||||
|
||||
await fillIn(hoursInput(), "-1");
|
||||
assert.equal(hoursInput().val(), "00");
|
||||
|
||||
await fillIn(minutesInput(), "@");
|
||||
assert.equal(minutesInput().val(), "00");
|
||||
|
||||
await fillIn(minutesInput(), "2");
|
||||
assert.equal(minutesInput().val(), "02");
|
||||
|
||||
await fillIn(minutesInput(), "60");
|
||||
assert.equal(minutesInput().val(), "59");
|
||||
|
||||
await fillIn(minutesInput(), "-1");
|
||||
assert.equal(minutesInput().val(), "00");
|
||||
}
|
||||
});
|
||||
@@ -1,184 +0,0 @@
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import componentTest from "helpers/component-test";
|
||||
import Topic from "discourse/models/topic";
|
||||
import { clearTopicFooterButtons } from "discourse/lib/register-topic-footer-button";
|
||||
|
||||
const buildTopic = function() {
|
||||
return Topic.create({
|
||||
id: 1234,
|
||||
title: "Qunit Test Topic"
|
||||
});
|
||||
};
|
||||
|
||||
moduleForComponent("topic-footer-buttons-desktop", {
|
||||
integration: true,
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = {
|
||||
title: "My title",
|
||||
label: "My Label"
|
||||
};
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
clearTopicFooterButtons();
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("default", {
|
||||
template: "{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-button",
|
||||
icon: "user",
|
||||
label: "test.label",
|
||||
title: "test.title"
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
const button = await find("#topic-footer-button-my-button");
|
||||
assert.ok(exists(button), "it creates an inline button");
|
||||
|
||||
const icon = await button.find(".d-icon-user");
|
||||
assert.ok(exists(icon), "the button has the correct icon");
|
||||
|
||||
const label = await button.find(".d-button-label");
|
||||
assert.ok(exists(label), "the button has a label");
|
||||
assert.equal(
|
||||
label.text(),
|
||||
I18n.t("test.label"),
|
||||
"the button has the correct label"
|
||||
);
|
||||
|
||||
const title = button.attr("title");
|
||||
assert.equal(
|
||||
title,
|
||||
I18n.t("test.title"),
|
||||
"the button has the correct title"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("priority", {
|
||||
template: "{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-second-button",
|
||||
priority: 750,
|
||||
icon: "user"
|
||||
});
|
||||
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-third-button",
|
||||
priority: 500,
|
||||
icon: "flag"
|
||||
});
|
||||
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-first-button",
|
||||
priority: 1000,
|
||||
icon: "times"
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
const buttons = await find(".topic-footer-button");
|
||||
const firstButton = find("#topic-footer-button-my-first-button");
|
||||
const secondButton = find("#topic-footer-button-my-second-button");
|
||||
const thirdButton = find("#topic-footer-button-my-third-button");
|
||||
|
||||
assert.ok(buttons.index(firstButton) < buttons.index(secondButton));
|
||||
assert.ok(buttons.index(secondButton) < buttons.index(thirdButton));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with functions", {
|
||||
template: "{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-button",
|
||||
icon() {
|
||||
return "user";
|
||||
},
|
||||
label() {
|
||||
return "test.label";
|
||||
},
|
||||
title() {
|
||||
return "test.title";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
const button = await find("#topic-footer-button-my-button");
|
||||
assert.ok(exists(button), "it creates an inline button");
|
||||
|
||||
const icon = await button.find(".d-icon-user");
|
||||
assert.ok(exists(icon), "the button has the correct icon");
|
||||
|
||||
const label = await button.find(".d-button-label");
|
||||
assert.ok(exists(label), "the button has a label");
|
||||
assert.equal(
|
||||
label.text(),
|
||||
I18n.t("test.label"),
|
||||
"the button has the correct label"
|
||||
);
|
||||
|
||||
const title = button.attr("title");
|
||||
assert.equal(
|
||||
title,
|
||||
I18n.t("test.title"),
|
||||
"the button has the correct title"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("action", {
|
||||
template: "<div id='test-action'></div>{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-button",
|
||||
icon: "flag",
|
||||
action() {
|
||||
$("#test-action").text(this.get("topic.title"));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
await click("#topic-footer-button-my-button");
|
||||
|
||||
assert.equal(find("#test-action").text(), this.get("topic.title"));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("dropdown", {
|
||||
template: "{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-button",
|
||||
icon: "flag",
|
||||
dropdown: true
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
const button = await find("#topic-footer-button-my-button");
|
||||
assert.notOk(exists(button), "it doesn’t create an inline button");
|
||||
}
|
||||
});
|
||||
@@ -1,35 +0,0 @@
|
||||
import selectKit from "helpers/select-kit-helper";
|
||||
import componentTest from "helpers/component-test";
|
||||
import Topic from "discourse/models/topic";
|
||||
|
||||
const buildTopic = function() {
|
||||
return Topic.create({
|
||||
id: 1234,
|
||||
title: "Qunit Test Topic"
|
||||
});
|
||||
};
|
||||
|
||||
moduleForComponent("topic-footer-mobile-dropdown", {
|
||||
integration: true,
|
||||
beforeEach: function() {
|
||||
this.set("subject", selectKit());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("default", {
|
||||
template: "{{topic-footer-mobile-dropdown topic=topic}}",
|
||||
beforeEach() {
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await this.subject.expand();
|
||||
|
||||
assert.equal(this.subject.header().title(), "Topic Controls");
|
||||
assert.equal(this.subject.header().value(), null);
|
||||
assert.notOk(
|
||||
this.subject.selectedRow().exists(),
|
||||
"it doesn’t preselect first row"
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("user-selector", { integration: true });
|
||||
|
||||
function paste(element, text) {
|
||||
let e = new Event("paste");
|
||||
e.clipboardData = { getData: () => text };
|
||||
element.dispatchEvent(e);
|
||||
}
|
||||
|
||||
componentTest("pasting a list of usernames", {
|
||||
template: `{{user-selector usernames=usernames class="test-selector"}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.set("usernames", "evil,trout");
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
let element = find(".test-selector")[0];
|
||||
|
||||
assert.equal(this.get("usernames"), "evil,trout");
|
||||
paste(element, "zip,zap,zoom");
|
||||
assert.equal(this.get("usernames"), "evil,trout,zip,zap,zoom");
|
||||
paste(element, "evil,abc,abc,abc");
|
||||
assert.equal(this.get("usernames"), "evil,trout,zip,zap,zoom,abc");
|
||||
|
||||
this.set("usernames", "");
|
||||
paste(element, "names with spaces");
|
||||
assert.equal(this.get("usernames"), "names,with,spaces");
|
||||
|
||||
this.set("usernames", null);
|
||||
paste(element, "@eviltrout,@codinghorror sam");
|
||||
assert.equal(this.get("usernames"), "eviltrout,codinghorror,sam");
|
||||
|
||||
this.set("usernames", null);
|
||||
paste(element, "eviltrout\nsam\ncodinghorror");
|
||||
assert.equal(this.get("usernames"), "eviltrout,sam,codinghorror");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("excluding usernames", {
|
||||
template: `{{user-selector usernames=usernames excludedUsernames=excludedUsernames class="test-selector"}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.set("usernames", "mark");
|
||||
this.set("excludedUsernames", ["jeff", "sam", "robin"]);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
let element = find(".test-selector")[0];
|
||||
paste(element, "roman,penar,jeff,robin");
|
||||
assert.equal(this.get("usernames"), "mark,roman,penar");
|
||||
}
|
||||
});
|
||||
@@ -6,9 +6,12 @@ componentTest("adding a value", {
|
||||
template: "{{value-list values=values}}",
|
||||
|
||||
skip: true,
|
||||
async test(assert) {
|
||||
this.set("values", "vinkas\nosama");
|
||||
|
||||
beforeEach() {
|
||||
this.set("values", "vinkas\nosama");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await selectKit().expand();
|
||||
await selectKit().fillInFilter("eviltrout");
|
||||
await selectKit().keyboard("enter");
|
||||
@@ -29,9 +32,11 @@ componentTest("adding a value", {
|
||||
componentTest("removing a value", {
|
||||
template: "{{value-list values=values}}",
|
||||
|
||||
async test(assert) {
|
||||
beforeEach() {
|
||||
this.set("values", "vinkas\nosama");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(".values .value[data-index='0'] .remove-value-btn");
|
||||
|
||||
assert.ok(
|
||||
@@ -40,16 +45,28 @@ componentTest("removing a value", {
|
||||
);
|
||||
|
||||
assert.equal(this.values, "osama", "it removes the expected value");
|
||||
|
||||
await selectKit().expand();
|
||||
|
||||
assert.ok(
|
||||
find(".select-kit-collection li.select-kit-row span.name")[0]
|
||||
.innerText === "vinkas",
|
||||
"it adds the removed value to choices"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("selecting a value", {
|
||||
template: "{{value-list values=values choices=choices}}",
|
||||
|
||||
async test(assert) {
|
||||
this.set("values", "vinkas\nosama");
|
||||
this.set("choices", ["maja", "michael"]);
|
||||
beforeEach() {
|
||||
this.setProperties({
|
||||
values: "vinkas\nosama",
|
||||
choices: ["maja", "michael"]
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await selectKit().expand();
|
||||
await selectKit().selectRowByValue("maja");
|
||||
|
||||
@@ -69,6 +86,10 @@ componentTest("selecting a value", {
|
||||
componentTest("array support", {
|
||||
template: "{{value-list values=values inputType='array'}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("values", ["vinkas", "osama"]);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
this.set("values", ["vinkas", "osama"]);
|
||||
|
||||
@@ -92,10 +113,13 @@ componentTest("array support", {
|
||||
componentTest("delimiter support", {
|
||||
template: "{{value-list values=values inputDelimiter='|'}}",
|
||||
|
||||
skip: true,
|
||||
async test(assert) {
|
||||
beforeEach() {
|
||||
this.set("values", "vinkas|osama");
|
||||
},
|
||||
|
||||
skip: true,
|
||||
|
||||
async test(assert) {
|
||||
await selectKit().expand();
|
||||
await selectKit().fillInFilter("eviltrout");
|
||||
await selectKit().keyboard("enter");
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
import { currentUser } from "helpers/qunit-helpers";
|
||||
let BookmarkController;
|
||||
|
||||
moduleFor("controller:bookmark", {
|
||||
beforeEach() {
|
||||
BookmarkController = this.subject({ currentUser: currentUser() });
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
sandbox.restore();
|
||||
}
|
||||
});
|
||||
|
||||
function mockMomentTz(dateString) {
|
||||
let now = moment.tz(dateString, BookmarkController.currentUser.timezone);
|
||||
sandbox.useFakeTimers(now.valueOf());
|
||||
}
|
||||
|
||||
QUnit.test("showLaterToday when later today is tomorrow do not show", function(
|
||||
assert
|
||||
) {
|
||||
mockMomentTz("2019-12-11T13:00:00Z");
|
||||
|
||||
assert.equal(BookmarkController.get("showLaterToday"), false);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"showLaterToday when later today is before the end of the day, show",
|
||||
function(assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00Z");
|
||||
|
||||
assert.equal(BookmarkController.get("showLaterToday"), true);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("nextWeek gets next week correctly", function(assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00Z");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.nextWeek().format("YYYY-MM-DD"),
|
||||
"2019-12-18"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("nextMonth gets next month correctly", function(assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00Z");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.nextMonth().format("YYYY-MM-DD"),
|
||||
"2020-01-11"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"nextBusinessDay gets next business day of monday correctly if today is friday",
|
||||
function(assert) {
|
||||
mockMomentTz("2019-12-13T08:00:00Z");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.nextBusinessDay().format("YYYY-MM-DD"),
|
||||
"2019-12-16"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"nextBusinessDay gets next business day of monday correctly if today is saturday",
|
||||
function(assert) {
|
||||
mockMomentTz("2019-12-14T08:00:00Z");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.nextBusinessDay().format("YYYY-MM-DD"),
|
||||
"2019-12-16"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"nextBusinessDay gets next business day of monday correctly if today is sunday",
|
||||
function(assert) {
|
||||
mockMomentTz("2019-12-15T08:00:00Z");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.nextBusinessDay().format("YYYY-MM-DD"),
|
||||
"2019-12-16"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"nextBusinessDay gets next business day of thursday correctly if today is wednesday",
|
||||
function(assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00Z");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.nextBusinessDay().format("YYYY-MM-DD"),
|
||||
"2019-12-12"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("tomorrow gets tomorrow correctly", function(assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00Z");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.tomorrow().format("YYYY-MM-DD"),
|
||||
"2019-12-12"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"startOfDay changes the time of the provided date to 8:00am correctly",
|
||||
function(assert) {
|
||||
let dt = moment.tz(
|
||||
"2019-12-11T11:37:16Z",
|
||||
BookmarkController.currentUser.timezone
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.startOfDay(dt).format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 08:00:00"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"laterToday gets 3 hours from now and if before half-past, it sets the time to half-past",
|
||||
function(assert) {
|
||||
mockMomentTz("2019-12-11T08:13:00Z");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 21:30:00"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"laterToday gets 3 hours from now and if after half-past, it rounds up to the next hour",
|
||||
function(assert) {
|
||||
mockMomentTz("2019-12-11T08:43:00Z");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 22:00:00"
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -1,3 +1,4 @@
|
||||
import EmberObject from "@ember/object";
|
||||
moduleFor("controller:preferences/account");
|
||||
|
||||
QUnit.test("updating of associated accounts", function(assert) {
|
||||
@@ -5,11 +6,11 @@ QUnit.test("updating of associated accounts", function(assert) {
|
||||
siteSettings: {
|
||||
enable_google_oauth2_logins: true
|
||||
},
|
||||
model: Ember.Object.create({
|
||||
model: EmberObject.create({
|
||||
second_factor_enabled: true,
|
||||
is_anonymous: true
|
||||
}),
|
||||
site: Ember.Object.create({
|
||||
site: EmberObject.create({
|
||||
isMobileDevice: false
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import EmberObject from "@ember/object";
|
||||
import { mapRoutes } from "discourse/mapping-router";
|
||||
import createStore from "helpers/create-store";
|
||||
|
||||
@@ -8,7 +9,7 @@ moduleFor("controller:reorder-categories", "controller:reorder-categories", {
|
||||
needs: ["controller:modal"]
|
||||
});
|
||||
|
||||
QUnit.test("fixIndices set unique position number", function(assert) {
|
||||
QUnit.test("reorder set unique position number", function(assert) {
|
||||
const store = createStore();
|
||||
|
||||
const categories = [];
|
||||
@@ -16,10 +17,10 @@ QUnit.test("fixIndices set unique position number", function(assert) {
|
||||
categories.push(store.createRecord("category", { id: i, position: 0 }));
|
||||
}
|
||||
|
||||
const site = Ember.Object.create({ categories: categories });
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
|
||||
reorderCategoriesController.fixIndices();
|
||||
reorderCategoriesController.reorder();
|
||||
|
||||
reorderCategoriesController
|
||||
.get("categoriesOrdered")
|
||||
@@ -29,7 +30,7 @@ QUnit.test("fixIndices set unique position number", function(assert) {
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"fixIndices places subcategories after their parent categories, while maintaining the relative order",
|
||||
"reorder places subcategories after their parent categories, while maintaining the relative order",
|
||||
function(assert) {
|
||||
const store = createStore();
|
||||
|
||||
@@ -59,10 +60,10 @@ QUnit.test(
|
||||
const categories = [child2, parent, other, child1];
|
||||
const expectedOrderSlugs = ["parent", "child2", "child1", "other"];
|
||||
|
||||
const site = Ember.Object.create({ categories: categories });
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
|
||||
reorderCategoriesController.fixIndices();
|
||||
reorderCategoriesController.reorder();
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
@@ -95,13 +96,13 @@ QUnit.test(
|
||||
});
|
||||
|
||||
const categories = [elem1, elem2, elem3];
|
||||
const site = Ember.Object.create({ categories: categories });
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
|
||||
reorderCategoriesController.actions.change.call(
|
||||
reorderCategoriesController,
|
||||
elem1,
|
||||
{ target: "<input value='2'>" }
|
||||
{ target: { value: "2" } }
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
@@ -142,13 +143,13 @@ QUnit.test(
|
||||
});
|
||||
|
||||
const categories = [elem1, child1, elem2, elem3];
|
||||
const site = Ember.Object.create({ categories: categories });
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
|
||||
reorderCategoriesController.actions.change.call(
|
||||
reorderCategoriesController,
|
||||
elem1,
|
||||
{ target: "<input value='3'>" }
|
||||
{ target: { value: 3 } }
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
@@ -176,23 +177,30 @@ QUnit.test(
|
||||
parent_category_id: 1
|
||||
});
|
||||
|
||||
const child2 = store.createRecord("category", {
|
||||
id: 5,
|
||||
position: 2,
|
||||
slug: "foochildchild",
|
||||
parent_category_id: 4
|
||||
});
|
||||
|
||||
const elem2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
position: 2,
|
||||
position: 3,
|
||||
slug: "bar"
|
||||
});
|
||||
|
||||
const elem3 = store.createRecord("category", {
|
||||
id: 3,
|
||||
position: 3,
|
||||
position: 4,
|
||||
slug: "test"
|
||||
});
|
||||
|
||||
const categories = [elem1, child1, elem2, elem3];
|
||||
const site = Ember.Object.create({ categories: categories });
|
||||
const categories = [elem1, child1, child2, elem2, elem3];
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
|
||||
reorderCategoriesController.fixIndices();
|
||||
reorderCategoriesController.reorder();
|
||||
|
||||
reorderCategoriesController.actions.moveDown.call(
|
||||
reorderCategoriesController,
|
||||
@@ -201,7 +209,7 @@ QUnit.test(
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
["bar", "foo", "foochild", "test"]
|
||||
["bar", "foo", "foochild", "foochildchild", "test"]
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import AppEvents from "discourse/lib/app-events";
|
||||
import EmberObject from "@ember/object";
|
||||
import { next } from "@ember/runloop";
|
||||
import Topic from "discourse/models/topic";
|
||||
import PostStream from "discourse/models/post-stream";
|
||||
import { Placeholder } from "discourse/lib/posts-with-placeholders";
|
||||
import User from "discourse/models/user";
|
||||
import { Promise } from "rsvp";
|
||||
|
||||
moduleFor("controller:topic", "controller:topic", {
|
||||
needs: ["controller:composer", "controller:application"],
|
||||
needs: [
|
||||
"controller:composer",
|
||||
"controller:application",
|
||||
"service:app-events"
|
||||
],
|
||||
beforeEach() {
|
||||
this.registry.register("app-events:main", AppEvents.create(), {
|
||||
instantiate: false
|
||||
});
|
||||
this.registry.injection("controller", "appEvents", "app-events:main");
|
||||
this.registry.injection("controller", "appEvents", "service:app-events");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -191,7 +195,7 @@ QUnit.test("selectedPostsUsername", function(assert) {
|
||||
});
|
||||
|
||||
QUnit.test("showSelectedPostsAtBottom", function(assert) {
|
||||
const site = Ember.Object.create({ mobileView: false });
|
||||
const site = EmberObject.create({ mobileView: false });
|
||||
const model = Topic.create({ posts_count: 3 });
|
||||
const controller = this.subject({ model, site });
|
||||
|
||||
@@ -221,7 +225,7 @@ QUnit.test("canDeleteSelected", function(assert) {
|
||||
],
|
||||
stream: [1, 2, 3]
|
||||
};
|
||||
const currentUser = Discourse.User.create({ admin: false });
|
||||
const currentUser = User.create({ admin: false });
|
||||
this.registry.register("current-user:main", currentUser, {
|
||||
instantiate: false
|
||||
});
|
||||
@@ -314,14 +318,17 @@ QUnit.test("Can split/merge topic", function(assert) {
|
||||
});
|
||||
|
||||
QUnit.test("canChangeOwner", function(assert) {
|
||||
const currentUser = Discourse.User.create({ admin: false });
|
||||
const currentUser = User.create({ admin: false });
|
||||
this.registry.register("current-user:main", currentUser, {
|
||||
instantiate: false
|
||||
});
|
||||
this.registry.injection("controller", "currentUser", "current-user:main");
|
||||
|
||||
const postStream = {
|
||||
posts: [{ id: 1, username: "gary" }, { id: 2, username: "lili" }],
|
||||
posts: [
|
||||
{ id: 1, username: "gary" },
|
||||
{ id: 2, username: "lili" }
|
||||
],
|
||||
stream: [1, 2]
|
||||
};
|
||||
|
||||
@@ -467,7 +474,7 @@ QUnit.test("togglePostSelection", function(assert) {
|
||||
// });
|
||||
|
||||
QUnit.test("selectBelow", function(assert) {
|
||||
const site = Ember.Object.create({
|
||||
const site = EmberObject.create({
|
||||
post_types: { small_action: 3, whisper: 4 }
|
||||
});
|
||||
|
||||
@@ -511,3 +518,42 @@ QUnit.test("topVisibleChanged", function(assert) {
|
||||
"it should work with a post-placehodler"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"deletePost - no modal is shown if post does not have replies",
|
||||
function(assert) {
|
||||
/* global server */
|
||||
server.get("/posts/2/reply-ids.json", () => {
|
||||
return [200, { "Content-Type": "application/json" }, []];
|
||||
});
|
||||
|
||||
let destroyed;
|
||||
const post = EmberObject.create({
|
||||
id: 2,
|
||||
post_number: 2,
|
||||
can_delete: true,
|
||||
reply_count: 3,
|
||||
destroy: () => {
|
||||
destroyed = true;
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
const postStream = EmberObject.create({
|
||||
stream: [2, 3, 4],
|
||||
posts: [post, { id: 3 }, { id: 4 }]
|
||||
});
|
||||
|
||||
const currentUser = EmberObject.create({ moderator: true });
|
||||
const model = Topic.create({ postStream });
|
||||
const controller = this.subject({ model, currentUser });
|
||||
|
||||
const done = assert.async();
|
||||
controller.send("deletePost", post);
|
||||
|
||||
next(() => {
|
||||
assert.ok(destroyed, "post was destroyed");
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
},
|
||||
description:
|
||||
"Discussion about the next-generation open source Discourse forum software",
|
||||
title: "Discourse Meta",
|
||||
title: "QUnit Discourse Tests",
|
||||
locale: "en_US",
|
||||
version: "2.2.0.beta8",
|
||||
https: true,
|
||||
@@ -31,7 +31,7 @@ export default {
|
||||
avatar_template:
|
||||
"/user_avatar/meta.discourse.org/sam/{size}/102149_2.png",
|
||||
title: "co-founder",
|
||||
last_seen_at: "2019-01-15T13:30:43.272Z"
|
||||
last_seen_at: "2030-01-15T13:30:43.272Z"
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
avatar_template:
|
||||
"/user_avatar/meta.discourse.org/codinghorror/{size}/110067_2.png",
|
||||
title: "co-founder",
|
||||
last_seen_at: "2019-01-15T13:21:56.592Z"
|
||||
last_seen_at: "2030-01-15T13:21:56.592Z"
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
@@ -49,7 +49,7 @@ export default {
|
||||
avatar_template:
|
||||
"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png",
|
||||
title: "co-founder",
|
||||
last_seen_at: "2019-01-14T18:03:08.232Z"
|
||||
last_seen_at: "2030-01-14T18:03:08.232Z"
|
||||
}
|
||||
],
|
||||
moderators: [
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
avatar_template:
|
||||
"/user_avatar/meta.discourse.org/sam/{size}/102149_2.png",
|
||||
title: "co-founder",
|
||||
last_seen_at: "2019-01-15T13:30:43.272Z"
|
||||
last_seen_at: "2030-01-15T13:30:43.272Z"
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
@@ -69,7 +69,7 @@ export default {
|
||||
avatar_template:
|
||||
"/user_avatar/meta.discourse.org/codinghorror/{size}/110067_2.png",
|
||||
title: "co-founder",
|
||||
last_seen_at: "2019-01-15T13:21:56.592Z"
|
||||
last_seen_at: "2030-01-15T13:21:56.592Z"
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
avatar_template:
|
||||
"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png",
|
||||
title: "co-founder",
|
||||
last_seen_at: "2019-01-14T18:03:08.232Z"
|
||||
last_seen_at: "2030-01-14T18:03:08.232Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ export default {
|
||||
position: 25,
|
||||
cannot_delete_reason:
|
||||
"Can't delete this category because it has 2030 topics. Oldest topic is <a href=\"https://localhost:3000/t/when-a-new-post-appears-in-a-topic-the-bookmark-isn-t-updated/39\">When a new post appears in a topic, the bookmark isn't updated</a>.",
|
||||
allow_badges: true
|
||||
allow_badges: true,
|
||||
custom_fields: {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2683,7 +2683,7 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
"/c/bug/l/latest.json": {
|
||||
"/c/bug/1/l/latest.json": {
|
||||
users: [
|
||||
{ id: 1, username: "sam", avatar_template: "/images/avatar.png" },
|
||||
{
|
||||
@@ -3756,7 +3756,114 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
"/c/feature/l/latest.json": {
|
||||
"/tag/important/l/latest.json": {
|
||||
users: [{ id: 1, username: "sam", avatar_template: "/images/avatar.png" }],
|
||||
primary_groups: [],
|
||||
topic_list: {
|
||||
can_create_topic: true,
|
||||
draft: null,
|
||||
draft_key: "new_topic",
|
||||
draft_sequence: 4,
|
||||
per_page: 30,
|
||||
tags: [
|
||||
{
|
||||
id: 1,
|
||||
name: "test",
|
||||
topic_count: 2,
|
||||
staff: false
|
||||
}
|
||||
],
|
||||
topics: [
|
||||
{
|
||||
id: 16,
|
||||
title: "Dinosaurs are the best",
|
||||
fancy_title: "Dinosaurs are the best",
|
||||
slug: "dinosaurs-are-the-best",
|
||||
posts_count: 1,
|
||||
reply_count: 0,
|
||||
highest_post_number: 1,
|
||||
image_url: null,
|
||||
created_at: "2019-11-12T05:19:52.300Z",
|
||||
last_posted_at: "2019-11-12T05:19:52.848Z",
|
||||
bumped: true,
|
||||
bumped_at: "2019-11-12T05:19:52.848Z",
|
||||
unseen: false,
|
||||
last_read_post_number: 1,
|
||||
unread: 0,
|
||||
new_posts: 0,
|
||||
pinned: false,
|
||||
unpinned: null,
|
||||
visible: true,
|
||||
closed: false,
|
||||
archived: false,
|
||||
notification_level: 3,
|
||||
bookmarked: false,
|
||||
liked: false,
|
||||
tags: ["test"],
|
||||
views: 2,
|
||||
like_count: 0,
|
||||
has_summary: false,
|
||||
archetype: "regular",
|
||||
last_poster_username: "sam",
|
||||
category_id: 1,
|
||||
pinned_globally: false,
|
||||
featured_link: null,
|
||||
posters: [
|
||||
{
|
||||
extras: "latest single",
|
||||
description: "Original Poster, Most Recent Poster",
|
||||
user_id: 1,
|
||||
primary_group_id: null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
title: "This is a test tagged post",
|
||||
fancy_title: "This is a test tagged post",
|
||||
slug: "this-is-a-test-tagged-post",
|
||||
posts_count: 1,
|
||||
reply_count: 0,
|
||||
highest_post_number: 1,
|
||||
image_url: null,
|
||||
created_at: "2019-11-12T05:19:32.032Z",
|
||||
last_posted_at: "2019-11-12T05:19:32.516Z",
|
||||
bumped: true,
|
||||
bumped_at: "2019-11-12T05:19:32.516Z",
|
||||
unseen: false,
|
||||
last_read_post_number: 1,
|
||||
unread: 0,
|
||||
new_posts: 0,
|
||||
pinned: false,
|
||||
unpinned: null,
|
||||
visible: true,
|
||||
closed: false,
|
||||
archived: false,
|
||||
notification_level: 3,
|
||||
bookmarked: false,
|
||||
liked: false,
|
||||
tags: ["test"],
|
||||
views: 1,
|
||||
like_count: 0,
|
||||
has_summary: false,
|
||||
archetype: "regular",
|
||||
last_poster_username: "sam",
|
||||
category_id: 3,
|
||||
pinned_globally: false,
|
||||
featured_link: null,
|
||||
posters: [
|
||||
{
|
||||
extras: "latest single",
|
||||
description: "Original Poster, Most Recent Poster",
|
||||
user_id: 1,
|
||||
primary_group_id: null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/c/feature/2/l/latest.json": {
|
||||
users: [
|
||||
{ id: 1, username: "sam", avatar_template: "/images/avatar.png" },
|
||||
{
|
||||
@@ -4828,7 +4935,7 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
"/c/dev/l/latest.json": {
|
||||
"/c/dev/7/l/latest.json": {
|
||||
users: [
|
||||
{ id: 1, username: "sam", avatar_template: "/images/avatar.png" },
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export default {
|
||||
"/draft.json": {
|
||||
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: null,
|
||||
draft_sequence: 0
|
||||
}
|
||||
};
|
||||
|
||||
@@ -46,7 +46,8 @@ export default {
|
||||
flair_url: "fa-adjust",
|
||||
is_group_owner: true,
|
||||
mentionable: true,
|
||||
messageable: true
|
||||
messageable: true,
|
||||
can_see_members: true
|
||||
},
|
||||
extras: {
|
||||
visible_group_names: ["discourse"]
|
||||
|
||||
@@ -18,6 +18,38 @@ export default {
|
||||
notification_type: NOTIFICATION_TYPES.liked_consolidated,
|
||||
read: false,
|
||||
data: { display_username: "aquaman", count: "5" }
|
||||
},
|
||||
{
|
||||
id: 789,
|
||||
notification_type: NOTIFICATION_TYPES.group_message_summary,
|
||||
read: false,
|
||||
post_number: null,
|
||||
topic_id: null,
|
||||
slug: null,
|
||||
data: {
|
||||
group_id: 41,
|
||||
group_name: "test",
|
||||
inbox_count: 5,
|
||||
username: "test2"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 1234,
|
||||
notification_type: NOTIFICATION_TYPES.invitee_accepted,
|
||||
read: false,
|
||||
post_number: null,
|
||||
topic_id: null,
|
||||
slug: null,
|
||||
data: { display_username: "test1" }
|
||||
},
|
||||
{
|
||||
id: 5678,
|
||||
notification_type: NOTIFICATION_TYPES.membership_request_accepted,
|
||||
read: false,
|
||||
post_number: null,
|
||||
topic_id: null,
|
||||
slug: null,
|
||||
data: { group_id: 41, group_name: "test" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -544,5 +544,191 @@ export default {
|
||||
],
|
||||
chunk_size: 20,
|
||||
bookmarked: false
|
||||
},
|
||||
"/t/topic_with_pie_chart_poll.json": {
|
||||
post_stream: {
|
||||
posts: [
|
||||
{
|
||||
id: 294,
|
||||
name: "",
|
||||
username: "markvanlan",
|
||||
avatar_template: "/user_avatar/localhost/markvanlan/{size}/11_2.png",
|
||||
created_at: "2019-11-22T18:55:41.439Z",
|
||||
cooked:
|
||||
'\u003cdiv class="poll" data-poll-status="open" data-poll-max="3" data-poll-min="1" data-poll-results="always" data-poll-charttype="pie" data-poll-type="multiple" data-poll-name="poll"\u003e\n\u003cdiv\u003e\n\u003cdiv class="poll-container"\u003e\n\u003cul\u003e\n\u003cli data-poll-option-id="687a1ccf3c6a260f9aeeb7f68a1d463c"\u003eThis Is\u003c/li\u003e\n\u003cli data-poll-option-id="9377906763a1221d31d656ea0c4a4495"\u003eA test for sure\u003c/li\u003e\n\u003cli data-poll-option-id="ecf47c65a85a0bb20029072b1b721977"\u003eWhy not give it some more\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv class="poll-info"\u003e\n\u003cp\u003e\n\u003cspan class="info-number"\u003e0\u003c/span\u003e\n\u003cspan class="info-label"\u003evoters\u003c/span\u003e\n\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e',
|
||||
post_number: 1,
|
||||
post_type: 1,
|
||||
updated_at: "2019-11-22T18:55:41.439Z",
|
||||
reply_count: 0,
|
||||
reply_to_post_number: null,
|
||||
quote_count: 0,
|
||||
incoming_link_count: 0,
|
||||
reads: 2,
|
||||
readers_count: 1,
|
||||
score: 0.2,
|
||||
yours: false,
|
||||
topic_id: 256,
|
||||
topic_slug: "14-the-title-must-be-longer-i-guess",
|
||||
display_username: "",
|
||||
primary_group_name: "Team",
|
||||
primary_group_flair_url: null,
|
||||
primary_group_flair_bg_color: "",
|
||||
primary_group_flair_color: "",
|
||||
version: 1,
|
||||
can_edit: false,
|
||||
can_delete: false,
|
||||
can_recover: false,
|
||||
can_wiki: false,
|
||||
read: true,
|
||||
user_title: "You are a member of the team",
|
||||
actions_summary: [
|
||||
{ id: 2, can_act: true },
|
||||
{ id: 3, can_act: true },
|
||||
{ id: 4, can_act: true },
|
||||
{ id: 8, can_act: true },
|
||||
{ id: 6, can_act: true },
|
||||
{ id: 7, can_act: true }
|
||||
],
|
||||
moderator: true,
|
||||
admin: true,
|
||||
staff: true,
|
||||
user_id: 1,
|
||||
hidden: false,
|
||||
trust_level: 4,
|
||||
deleted_at: null,
|
||||
user_deleted: false,
|
||||
edit_reason: null,
|
||||
can_view_edit_history: true,
|
||||
wiki: false,
|
||||
user_custom_fields: { team: "Engineering", votes: [247, 251, 248] },
|
||||
can_accept_answer: false,
|
||||
can_unaccept_answer: false,
|
||||
accepted_answer: false,
|
||||
can_translate: false,
|
||||
can_vote: true,
|
||||
polls: [
|
||||
{
|
||||
name: "poll",
|
||||
type: "multiple",
|
||||
status: "open",
|
||||
results: "always",
|
||||
min: 1,
|
||||
max: 3,
|
||||
options: [
|
||||
{
|
||||
id: "687a1ccf3c6a260f9aeeb7f68a1d463c",
|
||||
html: "This Is",
|
||||
votes: 2
|
||||
},
|
||||
{
|
||||
id: "9377906763a1221d31d656ea0c4a4495",
|
||||
html: "A test for sure",
|
||||
votes: 2
|
||||
},
|
||||
{
|
||||
id: "ecf47c65a85a0bb20029072b1b721977",
|
||||
html: "Why not give it some more",
|
||||
votes: 1
|
||||
}
|
||||
],
|
||||
voters: 2,
|
||||
chart_type: "pie"
|
||||
}
|
||||
],
|
||||
polls_votes: {
|
||||
poll: [
|
||||
"687a1ccf3c6a260f9aeeb7f68a1d463c",
|
||||
"9377906763a1221d31d656ea0c4a4495"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
stream: [294]
|
||||
},
|
||||
timeline_lookup: [[1, 2]],
|
||||
suggested_topics: [],
|
||||
tags: [],
|
||||
id: 256,
|
||||
title: "14 the title must be longer i guess",
|
||||
fancy_title: "14 the title must be longer i guess",
|
||||
posts_count: 1,
|
||||
created_at: "2019-11-22T18:55:41.259Z",
|
||||
views: 3,
|
||||
reply_count: 0,
|
||||
like_count: 0,
|
||||
last_posted_at: "2019-11-22T18:55:41.439Z",
|
||||
visible: true,
|
||||
closed: false,
|
||||
archived: false,
|
||||
has_summary: false,
|
||||
archetype: "regular",
|
||||
slug: "14-the-title-must-be-longer-i-guess",
|
||||
category_id: 1,
|
||||
word_count: 24,
|
||||
deleted_at: null,
|
||||
user_id: 1,
|
||||
featured_link: null,
|
||||
pinned_globally: false,
|
||||
pinned_at: null,
|
||||
pinned_until: null,
|
||||
image_url: null,
|
||||
draft: null,
|
||||
draft_key: "topic_256",
|
||||
draft_sequence: 0,
|
||||
posted: false,
|
||||
unpinned: null,
|
||||
pinned: false,
|
||||
current_post_number: 1,
|
||||
highest_post_number: 1,
|
||||
last_read_post_number: 1,
|
||||
last_read_post_id: 294,
|
||||
deleted_by: null,
|
||||
actions_summary: [
|
||||
{ id: 4, count: 0, hidden: false, can_act: true },
|
||||
{ id: 8, count: 0, hidden: false, can_act: true },
|
||||
{ id: 7, count: 0, hidden: false, can_act: true }
|
||||
],
|
||||
chunk_size: 20,
|
||||
bookmarked: false,
|
||||
topic_timer: null,
|
||||
private_topic_timer: null,
|
||||
message_bus_last_id: 1,
|
||||
participant_count: 1,
|
||||
show_read_indicator: false,
|
||||
can_vote: true,
|
||||
vote_count: 0,
|
||||
user_voted: false,
|
||||
details: {
|
||||
notification_level: 1,
|
||||
notifications_reason_id: null,
|
||||
can_create_post: true,
|
||||
can_reply_as_new_topic: true,
|
||||
can_flag_topic: true,
|
||||
participants: [
|
||||
{
|
||||
id: 1,
|
||||
username: "markvanlan",
|
||||
name: "",
|
||||
avatar_template: "/user_avatar/localhost/markvanlan/{size}/11_2.png",
|
||||
post_count: 1,
|
||||
primary_group_name: "Team",
|
||||
primary_group_flair_url: null,
|
||||
primary_group_flair_color: "",
|
||||
primary_group_flair_bg_color: ""
|
||||
}
|
||||
],
|
||||
created_by: {
|
||||
id: 1,
|
||||
username: "markvanlan",
|
||||
name: "",
|
||||
avatar_template: "/user_avatar/localhost/markvanlan/{size}/11_2.png"
|
||||
},
|
||||
last_poster: {
|
||||
id: 1,
|
||||
username: "markvanlan",
|
||||
name: "",
|
||||
avatar_template: "/user_avatar/localhost/markvanlan/{size}/11_2.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*jshint maxlen:10000000 */
|
||||
export default {
|
||||
"/topics/private-messages/eviltrout.json": {
|
||||
users: [
|
||||
{
|
||||
id: 19,
|
||||
username: "eviltrout",
|
||||
name: null,
|
||||
avatar_template: "/letter_avatar_proxy/v4/letter/t/f9ae1b/{size}.png"
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
username: "mixtape",
|
||||
name: null,
|
||||
avatar_template: "/letter_avatar_proxy/v4/letter/m/34f0e0/{size}.png"
|
||||
}
|
||||
],
|
||||
primary_groups: [],
|
||||
topic_list: {
|
||||
can_create_topic: true,
|
||||
draft: null,
|
||||
draft_key: "new_topic",
|
||||
draft_sequence: 33,
|
||||
per_page: 30,
|
||||
topics: [
|
||||
{
|
||||
id: 174,
|
||||
title: "BUG: Can not render emoji properly :/",
|
||||
fancy_title: "BUG: Can not render emoji properly :confused:",
|
||||
slug: "bug-can-not-render-emoji-properly",
|
||||
posts_count: 1,
|
||||
reply_count: 0,
|
||||
highest_post_number: 2,
|
||||
image_url: null,
|
||||
created_at: "2019-07-26T01:29:24.008Z",
|
||||
last_posted_at: "2019-07-26T01:29:24.177Z",
|
||||
bumped: true,
|
||||
bumped_at: "2019-07-26T01:29:24.177Z",
|
||||
unseen: false,
|
||||
last_read_post_number: 2,
|
||||
unread: 0,
|
||||
new_posts: 0,
|
||||
pinned: false,
|
||||
unpinned: null,
|
||||
visible: true,
|
||||
closed: false,
|
||||
archived: false,
|
||||
notification_level: 3,
|
||||
bookmarked: false,
|
||||
liked: false,
|
||||
views: 5,
|
||||
like_count: 0,
|
||||
has_summary: false,
|
||||
archetype: "private_message",
|
||||
last_poster_username: "mixtape",
|
||||
category_id: null,
|
||||
pinned_globally: false,
|
||||
featured_link: null,
|
||||
posters: [
|
||||
{
|
||||
extras: "latest single",
|
||||
description: "Original Poster, Most Recent Poster",
|
||||
user_id: 13,
|
||||
primary_group_id: null
|
||||
}
|
||||
],
|
||||
participants: [
|
||||
{
|
||||
extras: "latest",
|
||||
description: null,
|
||||
user_id: 13,
|
||||
primary_group_id: null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user