DEV: select-kit 2 (#7998)

This new iteration of select-kit focuses on following best principales and disallowing mutations inside select-kit components. A best effort has been made to avoid breaking changes, however if you content was a flat array, eg: ["foo", "bar"] You will need to set valueProperty=null and nameProperty=null on the component.

Also almost every component should have an `onChange` handler now to decide what to do with the updated data. **select-kit will not mutate your data by itself anymore**
This commit is contained in:
Joffrey JAFFEUX
2020-02-03 14:22:14 +01:00
committed by GitHub
parent 0e2cbee339
commit 0431942f3d
278 changed files with 7566 additions and 6957 deletions
@@ -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");
@@ -447,6 +447,8 @@ 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"
@@ -638,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");
@@ -659,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(),
@@ -668,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"
);
@@ -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,
+8 -15
View File
@@ -51,29 +51,22 @@ QUnit.test("Anonymous Viewing Group", async assert => {
);
assert.ok(count(".user-stream-item") > 0, "it lists stream items");
await selectKit(".group-dropdown").expand();
const groupDropdown = selectKit(".group-dropdown");
await groupDropdown.expand();
assert.equal(groupDropdown.rowByIndex(1).name(), "discourse");
assert.equal(
find(".select-kit-row")
.text()
.trim(),
"discourse",
"it displays the right row"
);
assert.equal(
find(".group-dropdown-filter")
.text()
.trim(),
I18n.t("groups.index.all").toLowerCase(),
"it displays the right header"
groupDropdown.rowByIndex(0).name(),
I18n.t("groups.index.all").toLowerCase()
);
Discourse.SiteSettings.enable_group_directory = false;
await visit("/g");
await visit("/g/discourse");
await selectKit(".group-dropdown").expand();
await groupDropdown.expand();
assert.equal(
find(".group-dropdown-filter").length,
@@ -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`);
@@ -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(
@@ -375,8 +377,9 @@ QUnit.test("update post time through advanced search ui", async assert => {
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(
@@ -28,15 +28,15 @@ 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);
});
@@ -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;
@@ -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(),
@@ -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;
@@ -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;
@@ -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;
@@ -26,7 +26,7 @@ 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"
);
@@ -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,209 +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"
);
}
});
@@ -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");
}
});
@@ -1,121 +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.siteSettings.tags_sort_alphabetically = 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: "penar", count: 3 }, { text: "bianca", count: 3 }, { text: "régis", count: 5 }]
});
});
},
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(),
"penar",
"it has the correct tag at the correct position after alphabetical sorting"
);
assert.equal(
this.subject.rowByIndex(2).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,323 +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 shouldnt 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(),
"&lt;div&gt;sam&lt;/div&gt;"
);
await this.subject.fillInFilter("<div>jeff</div>");
await this.subject.keyboard("enter");
assert.equal(
this.subject
.header()
.el()
.find(".name")
.html()
.trim(),
"&lt;div&gt;jeff&lt;/div&gt;"
);
}
});
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();
}
});
@@ -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();
@@ -52,7 +61,7 @@ componentTest("with scopedCategoryId", {
});
componentTest("with allowUncategorized=null", {
template: "{{category-chooser allowUncategorized=null}}",
template: template(["allowUncategorized=null"]),
beforeEach() {
this.siteSettings.allow_uncategorized_topics = false;
@@ -60,12 +69,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 +82,12 @@ componentTest("with allowUncategorized=null rootNone=true", {
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 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 +96,12 @@ componentTest("with disallowed 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");
}
});
componentTest("with allowed uncategorized", {
template: "{{category-chooser allowUncategorized=true}}",
template: template(["allowUncategorized=true"]),
beforeEach() {
this.siteSettings.allow_uncategorized_topics = true;
@@ -101,12 +109,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 +122,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 +136,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,336 @@
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";
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() {
Ember.set(this.currentUser, "staff", false);
Ember.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() {
Ember.set(this.currentUser, "staff", false);
Ember.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() {
Ember.set(this.currentUser, "staff", true);
Ember.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("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"
);
}
});
@@ -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,33 @@
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,217 @@
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);
}
});
@@ -0,0 +1,78 @@
import componentTest from "helpers/component-test";
import { testSelectKitModule } from "./select-kit-test-helper";
import Site from "discourse/models/site";
testSelectKitModule("tag-drop", {
beforeEach() {
const site = Site.current();
Ember.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"
// );
}
});
@@ -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"
);
}
});
@@ -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");
@@ -1,822 +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 doesnt 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("doesnt 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(),
"&lt;div&gt;sam&lt;/div&gt;"
);
assert.equal(
this.subject
.header()
.el()
.find(".selected-name")
.html()
.trim(),
"&lt;div&gt;sam&lt;/div&gt;"
);
}
});
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 doesnt 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,133 +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, target_tag: null }
]
});
} else if (params.queryParams.q === "dav") {
return response({
"results": [
{ "id": "David", "text": "David", "count": 2, "pm_count": 0, target_tag: null }
]
});
}
});
},
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("/tag/david"),
"it uses lowercase URLs for tags"
);
}
});
componentTest("synonym", {
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 === "robin") {
return response({
"results": [
{ "id": "Robin", "text": "Robin", "count": 2, "pm_count": 0, target_tag: 'EvilTrout' }
]
});
}
});
},
async test(assert) {
await this.subject.expand();
sandbox.stub(DiscourseURL, "routeTo");
await this.subject.fillInFilter("robin");
await this.subject.keyboard("enter");
assert.ok(
DiscourseURL.routeTo.calledWith("/tag/eviltrout"),
"it routes to the target tag"
);
}
});
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 doesnt crash"
);
}
});
@@ -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 doesnt 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 doesnt preselect first row"
);
}
});
@@ -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(
@@ -46,10 +51,14 @@ componentTest("removing a value", {
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 +78,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 +105,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");
@@ -24,7 +24,10 @@ async function collapseSelectKit(selector) {
async function selectKitFillInFilter(filter, selector) {
checkSelectKitIsNotCollapsed(selector);
await fillIn(`${selector} .filter-input`, filter);
await fillIn(
`${selector} .filter-input`,
find(`${selector} .filter-input`).val() + filter
);
}
async function selectKitSelectRowByValue(value, selector) {
@@ -66,7 +69,11 @@ async function keyboardHelper(value, target, selector) {
tab: { keyCode: 9 }
};
await triggerEvent(target, "keydown", mapping[value]);
await triggerEvent(
target,
"keydown",
mapping[value] || { keyCode: value.charCodeAt(0) }
);
}
}
@@ -82,7 +89,8 @@ function rowHelper(row) {
return row.attr("title");
},
value() {
return row.attr("data-value");
const value = row.attr("data-value");
return Ember.isEmpty(value) ? null : value;
},
exists() {
return exists(row);
@@ -96,7 +104,8 @@ function rowHelper(row) {
function headerHelper(header) {
return {
value() {
return header.attr("data-value");
const value = header.attr("data-value");
return Ember.isEmpty(value) ? null : value;
},
name() {
return header.attr("data-name");
@@ -105,7 +114,7 @@ function headerHelper(header) {
return header.text().trim();
},
icon() {
return header.find(".icon");
return header.find(".d-icon");
},
title() {
return header.attr("title");
@@ -124,6 +133,9 @@ function filterHelper(filter) {
exists() {
return exists(filter);
},
value() {
return filter.find("input").val();
},
el() {
return filter;
}
@@ -194,6 +206,17 @@ export default function selectKit(selector) {
return find(selector).find(".select-kit-row");
},
displayedContent() {
return this.rows()
.map((_, row) => {
return {
name: row.getAttribute("data-name"),
id: row.getAttribute("data-value")
};
})
.toArray();
},
rowByValue(value) {
return rowHelper(
find(selector).find('.select-kit-row[data-value="' + value + '"]')
+3 -1
View File
@@ -164,7 +164,9 @@ QUnit.testDone(function() {
Object.keys(events).forEach(function(eventKey) {
var event = events[eventKey];
event.forEach(function(listener) {
appEvents.off(eventKey, listener.target, listener.fn);
if (appEvents.has(eventKey)) {
appEvents.off(eventKey, listener.target, listener.fn);
}
});
});