mirror of
https://github.com/discourse/discourse.git
synced 2024-11-29 20:04:02 +08:00
92b26ecbfb
We had a handful of methods attached to the root `Discourse` object related to focus and notification counts. This patch pulls them out into a service called `document-title` for updating the title, and a component called `d-document` to attach and listen for browser events related to focus. It also removes some computed properties and observers in favor of plain old Javascript objects.
546 lines
14 KiB
JavaScript
546 lines
14 KiB
JavaScript
import EmberObject from "@ember/object";
|
|
import { next } from "@ember/runloop";
|
|
import Topic from "discourse/models/topic";
|
|
import { Placeholder } from "discourse/lib/posts-with-placeholders";
|
|
import User from "discourse/models/user";
|
|
import { Promise } from "rsvp";
|
|
import pretender from "helpers/create-pretender";
|
|
|
|
moduleFor("controller:topic", "controller:topic", {
|
|
needs: [
|
|
"controller:composer",
|
|
"controller:application",
|
|
"service:app-events",
|
|
"service:document-title"
|
|
],
|
|
beforeEach() {
|
|
this.registry.injection("controller", "appEvents", "service:app-events");
|
|
}
|
|
});
|
|
|
|
function topicWithStream(streamDetails) {
|
|
let topic = Topic.create();
|
|
topic.get("postStream").setProperties(streamDetails);
|
|
return topic;
|
|
}
|
|
|
|
QUnit.test("editTopic", function(assert) {
|
|
const model = Topic.create();
|
|
const controller = this.subject({ model });
|
|
|
|
assert.not(controller.get("editingTopic"), "we are not editing by default");
|
|
|
|
controller.set("model.details.can_edit", false);
|
|
controller.send("editTopic");
|
|
|
|
assert.not(
|
|
controller.get("editingTopic"),
|
|
"calling editTopic doesn't enable editing unless the user can edit"
|
|
);
|
|
|
|
controller.set("model.details.can_edit", true);
|
|
controller.send("editTopic");
|
|
|
|
assert.ok(
|
|
controller.get("editingTopic"),
|
|
"calling editTopic enables editing if the user can edit"
|
|
);
|
|
assert.equal(controller.get("buffered.title"), model.get("title"));
|
|
assert.equal(
|
|
controller.get("buffered.category_id"),
|
|
model.get("category_id")
|
|
);
|
|
|
|
controller.send("cancelEditingTopic");
|
|
|
|
assert.not(
|
|
controller.get("editingTopic"),
|
|
"cancelling edit mode reverts the property value"
|
|
);
|
|
});
|
|
|
|
QUnit.test("toggleMultiSelect", function(assert) {
|
|
const model = Topic.create();
|
|
const controller = this.subject({ model });
|
|
|
|
assert.not(
|
|
controller.get("multiSelect"),
|
|
"multi selection mode is disabled by default"
|
|
);
|
|
|
|
controller.get("selectedPostIds").pushObject(1);
|
|
assert.equal(controller.get("selectedPostIds.length"), 1);
|
|
|
|
controller.send("toggleMultiSelect");
|
|
|
|
assert.ok(
|
|
controller.get("multiSelect"),
|
|
"calling 'toggleMultiSelect' once enables multi selection mode"
|
|
);
|
|
assert.equal(
|
|
controller.get("selectedPostIds.length"),
|
|
0,
|
|
"toggling 'multiSelect' clears 'selectedPostIds'"
|
|
);
|
|
|
|
controller.get("selectedPostIds").pushObject(2);
|
|
assert.equal(controller.get("selectedPostIds.length"), 1);
|
|
|
|
controller.send("toggleMultiSelect");
|
|
|
|
assert.not(
|
|
controller.get("multiSelect"),
|
|
"calling 'toggleMultiSelect' twice disables multi selection mode"
|
|
);
|
|
assert.equal(
|
|
controller.get("selectedPostIds.length"),
|
|
0,
|
|
"toggling 'multiSelect' clears 'selectedPostIds'"
|
|
);
|
|
});
|
|
|
|
QUnit.test("selectedPosts", function(assert) {
|
|
let model = topicWithStream({ posts: [{ id: 1 }, { id: 2 }, { id: 3 }] });
|
|
const controller = this.subject({ model });
|
|
|
|
controller.set("selectedPostIds", [1, 2, 42]);
|
|
|
|
assert.equal(
|
|
controller.get("selectedPosts.length"),
|
|
2,
|
|
"selectedPosts only contains already loaded posts"
|
|
);
|
|
assert.not(
|
|
controller.get("selectedPosts").some(p => p === undefined),
|
|
"selectedPosts only contains valid post objects"
|
|
);
|
|
});
|
|
|
|
QUnit.test("selectedAllPosts", function(assert) {
|
|
let model = topicWithStream({ stream: [1, 2, 3] });
|
|
const controller = this.subject({ model });
|
|
|
|
controller.set("selectedPostIds", [1, 2]);
|
|
|
|
assert.not(controller.get("selectedAllPosts"), "not all posts are selected");
|
|
|
|
controller.get("selectedPostIds").pushObject(3);
|
|
|
|
assert.ok(controller.get("selectedAllPosts"), "all posts are selected");
|
|
|
|
controller.get("selectedPostIds").pushObject(42);
|
|
|
|
assert.ok(
|
|
controller.get("selectedAllPosts"),
|
|
"all posts (including filtered posts) are selected"
|
|
);
|
|
|
|
model.setProperties({
|
|
"postStream.isMegaTopic": true,
|
|
posts_count: 1
|
|
});
|
|
|
|
assert.ok(
|
|
controller.get("selectedAllPosts"),
|
|
"it uses the topic's post count for megatopics"
|
|
);
|
|
});
|
|
|
|
QUnit.test("selectedPostsUsername", function(assert) {
|
|
let model = topicWithStream({
|
|
posts: [
|
|
{ id: 1, username: "gary" },
|
|
{ id: 2, username: "gary" },
|
|
{ id: 3, username: "lili" }
|
|
],
|
|
stream: [1, 2, 3]
|
|
});
|
|
const controller = this.subject({ model });
|
|
const selectedPostIds = controller.get("selectedPostIds");
|
|
|
|
assert.equal(
|
|
controller.get("selectedPostsUsername"),
|
|
undefined,
|
|
"no username when no selected posts"
|
|
);
|
|
|
|
selectedPostIds.pushObject(1);
|
|
|
|
assert.equal(
|
|
controller.get("selectedPostsUsername"),
|
|
"gary",
|
|
"username of the selected posts"
|
|
);
|
|
|
|
selectedPostIds.pushObject(2);
|
|
|
|
assert.equal(
|
|
controller.get("selectedPostsUsername"),
|
|
"gary",
|
|
"username of all the selected posts when same user"
|
|
);
|
|
|
|
selectedPostIds.pushObject(3);
|
|
|
|
assert.equal(
|
|
controller.get("selectedPostsUsername"),
|
|
undefined,
|
|
"no username when more than 1 user"
|
|
);
|
|
|
|
selectedPostIds.replace(2, 1, [42]);
|
|
|
|
assert.equal(
|
|
controller.get("selectedPostsUsername"),
|
|
undefined,
|
|
"no username when not already loaded posts are selected"
|
|
);
|
|
});
|
|
|
|
QUnit.test("showSelectedPostsAtBottom", function(assert) {
|
|
const site = EmberObject.create({ mobileView: false });
|
|
const model = Topic.create({ posts_count: 3 });
|
|
const controller = this.subject({ model, site });
|
|
|
|
assert.not(controller.get("showSelectedPostsAtBottom"), "false on desktop");
|
|
|
|
site.set("mobileView", true);
|
|
|
|
assert.not(
|
|
controller.get("showSelectedPostsAtBottom"),
|
|
"requires at least 3 posts on mobile"
|
|
);
|
|
|
|
model.set("posts_count", 4);
|
|
|
|
assert.ok(
|
|
controller.get("showSelectedPostsAtBottom"),
|
|
"true when mobile and more than 3 posts"
|
|
);
|
|
});
|
|
|
|
QUnit.test("canDeleteSelected", function(assert) {
|
|
const currentUser = User.create({ admin: false });
|
|
this.registry.register("current-user:main", currentUser, {
|
|
instantiate: false
|
|
});
|
|
this.registry.injection("controller", "currentUser", "current-user:main");
|
|
let model = topicWithStream({
|
|
posts: [
|
|
{ id: 1, can_delete: false },
|
|
{ id: 2, can_delete: true },
|
|
{ id: 3, can_delete: true }
|
|
],
|
|
stream: [1, 2, 3]
|
|
});
|
|
const controller = this.subject({ model });
|
|
const selectedPostIds = controller.get("selectedPostIds");
|
|
|
|
assert.not(
|
|
controller.get("canDeleteSelected"),
|
|
"false when no posts are selected"
|
|
);
|
|
|
|
selectedPostIds.pushObject(1);
|
|
|
|
assert.not(
|
|
controller.get("canDeleteSelected"),
|
|
"false when can't delete one of the selected posts"
|
|
);
|
|
|
|
selectedPostIds.replace(0, 1, [2, 3]);
|
|
|
|
assert.ok(
|
|
controller.get("canDeleteSelected"),
|
|
"true when all selected posts can be deleted"
|
|
);
|
|
|
|
selectedPostIds.pushObject(1);
|
|
|
|
assert.not(
|
|
controller.get("canDeleteSelected"),
|
|
"false when all posts are selected and user is staff"
|
|
);
|
|
|
|
currentUser.set("admin", true);
|
|
|
|
assert.ok(
|
|
controller.get("canDeleteSelected"),
|
|
"true when all posts are selected and user is staff"
|
|
);
|
|
});
|
|
|
|
QUnit.test("Can split/merge topic", function(assert) {
|
|
let model = topicWithStream({
|
|
posts: [
|
|
{ id: 1, post_number: 1, post_type: 1 },
|
|
{ id: 2, post_number: 2, post_type: 4 },
|
|
{ id: 3, post_number: 3, post_type: 1 }
|
|
],
|
|
stream: [1, 2, 3]
|
|
});
|
|
model.set("details.can_move_posts", false);
|
|
const controller = this.subject({ model });
|
|
const selectedPostIds = controller.get("selectedPostIds");
|
|
|
|
assert.not(
|
|
controller.get("canMergeTopic"),
|
|
"can't merge topic when no posts are selected"
|
|
);
|
|
|
|
selectedPostIds.pushObject(1);
|
|
|
|
assert.not(
|
|
controller.get("canMergeTopic"),
|
|
"can't merge topic when can't move posts"
|
|
);
|
|
|
|
model.set("details.can_move_posts", true);
|
|
|
|
assert.ok(controller.get("canMergeTopic"), "can merge topic");
|
|
|
|
selectedPostIds.removeObject(1);
|
|
selectedPostIds.pushObject(2);
|
|
|
|
assert.ok(
|
|
controller.get("canMergeTopic"),
|
|
"can merge topic when 1st post is not a regular post"
|
|
);
|
|
|
|
selectedPostIds.pushObject(3);
|
|
|
|
assert.ok(
|
|
controller.get("canMergeTopic"),
|
|
"can merge topic when all posts are selected"
|
|
);
|
|
});
|
|
|
|
QUnit.test("canChangeOwner", function(assert) {
|
|
const currentUser = User.create({ admin: false });
|
|
this.registry.register("current-user:main", currentUser, {
|
|
instantiate: false
|
|
});
|
|
this.registry.injection("controller", "currentUser", "current-user:main");
|
|
|
|
let model = topicWithStream({
|
|
posts: [
|
|
{ id: 1, username: "gary" },
|
|
{ id: 2, username: "lili" }
|
|
],
|
|
stream: [1, 2]
|
|
});
|
|
model.set("currentUser", { admin: false });
|
|
const controller = this.subject({ model });
|
|
const selectedPostIds = controller.get("selectedPostIds");
|
|
|
|
assert.not(
|
|
controller.get("canChangeOwner"),
|
|
"false when no posts are selected"
|
|
);
|
|
|
|
selectedPostIds.pushObject(1);
|
|
|
|
assert.not(controller.get("canChangeOwner"), "false when not admin");
|
|
|
|
currentUser.set("admin", true);
|
|
|
|
assert.ok(
|
|
controller.get("canChangeOwner"),
|
|
"true when admin and one post is selected"
|
|
);
|
|
|
|
selectedPostIds.pushObject(2);
|
|
|
|
assert.not(
|
|
controller.get("canChangeOwner"),
|
|
"false when admin but more than 1 user"
|
|
);
|
|
});
|
|
|
|
QUnit.test("canMergePosts", function(assert) {
|
|
let model = topicWithStream({
|
|
posts: [
|
|
{ id: 1, username: "gary", can_delete: true },
|
|
{ id: 2, username: "lili", can_delete: true },
|
|
{ id: 3, username: "gary", can_delete: false },
|
|
{ id: 4, username: "gary", can_delete: true }
|
|
],
|
|
stream: [1, 2, 3]
|
|
});
|
|
const controller = this.subject({ model });
|
|
const selectedPostIds = controller.get("selectedPostIds");
|
|
|
|
assert.not(
|
|
controller.get("canMergePosts"),
|
|
"false when no posts are selected"
|
|
);
|
|
|
|
selectedPostIds.pushObject(1);
|
|
|
|
assert.not(
|
|
controller.get("canMergePosts"),
|
|
"false when only one post is selected"
|
|
);
|
|
|
|
selectedPostIds.pushObject(2);
|
|
|
|
assert.not(
|
|
controller.get("canMergePosts"),
|
|
"false when selected posts are from different users"
|
|
);
|
|
|
|
selectedPostIds.replace(1, 1, [3]);
|
|
|
|
assert.not(
|
|
controller.get("canMergePosts"),
|
|
"false when selected posts can't be deleted"
|
|
);
|
|
|
|
selectedPostIds.replace(1, 1, [4]);
|
|
|
|
assert.ok(
|
|
controller.get("canMergePosts"),
|
|
"true when all selected posts are deletable and by the same user"
|
|
);
|
|
});
|
|
|
|
QUnit.test("Select/deselect all", function(assert) {
|
|
let model = topicWithStream({ stream: [1, 2, 3] });
|
|
const controller = this.subject({ model });
|
|
|
|
assert.equal(
|
|
controller.get("selectedPostsCount"),
|
|
0,
|
|
"no posts selected by default"
|
|
);
|
|
|
|
controller.send("selectAll");
|
|
|
|
assert.equal(
|
|
controller.get("selectedPostsCount"),
|
|
3,
|
|
"calling 'selectAll' selects all posts"
|
|
);
|
|
|
|
controller.send("deselectAll");
|
|
|
|
assert.equal(
|
|
controller.get("selectedPostsCount"),
|
|
0,
|
|
"calling 'deselectAll' deselects all posts"
|
|
);
|
|
});
|
|
|
|
QUnit.test("togglePostSelection", function(assert) {
|
|
const controller = this.subject();
|
|
const selectedPostIds = controller.get("selectedPostIds");
|
|
|
|
assert.equal(selectedPostIds[0], undefined, "no posts selected by default");
|
|
|
|
controller.send("togglePostSelection", { id: 1 });
|
|
|
|
assert.equal(
|
|
selectedPostIds[0],
|
|
1,
|
|
"adds the selected post id if not already selected"
|
|
);
|
|
|
|
controller.send("togglePostSelection", { id: 1 });
|
|
|
|
assert.equal(
|
|
selectedPostIds[0],
|
|
undefined,
|
|
"removes the selected post id if already selected"
|
|
);
|
|
});
|
|
|
|
// QUnit.test("selectReplies", function(assert) {
|
|
// const controller = this.subject();
|
|
// const selectedPostIds = controller.get("selectedPostIds");
|
|
//
|
|
// assert.equal(selectedPostIds[0], undefined, "no posts selected by default");
|
|
//
|
|
// controller.send("selectReplies", { id: 42 });
|
|
//
|
|
// assert.equal(selectedPostIds[0], 42, "selected post #42");
|
|
// assert.equal(selectedPostIds[1], 45, "selected post #45");
|
|
// assert.equal(selectedPostIds[2], 100, "selected post #100");
|
|
// });
|
|
|
|
QUnit.test("selectBelow", function(assert) {
|
|
const site = EmberObject.create({
|
|
post_types: { small_action: 3, whisper: 4 }
|
|
});
|
|
let model = topicWithStream({
|
|
stream: [1, 2, 3, 4, 5, 6, 7, 8],
|
|
posts: [
|
|
{ id: 5, cooked: "whisper post", post_type: 4 },
|
|
{ id: 6, cooked: "a small action", post_type: 3 },
|
|
{ id: 7, cooked: "", post_type: 4 }
|
|
]
|
|
});
|
|
const controller = this.subject({ site, model });
|
|
let selectedPostIds = controller.get("selectedPostIds");
|
|
|
|
assert.equal(selectedPostIds[0], undefined, "no posts selected by default");
|
|
|
|
controller.send("selectBelow", { id: 3 });
|
|
|
|
assert.equal(selectedPostIds[0], 3, "selected post #3");
|
|
assert.equal(selectedPostIds[1], 4, "also selected 1st post below post #3");
|
|
assert.equal(selectedPostIds[2], 5, "also selected 2nd post below post #3");
|
|
assert.equal(selectedPostIds[3], 8, "also selected 3rd post below post #3");
|
|
});
|
|
|
|
QUnit.test("topVisibleChanged", function(assert) {
|
|
let model = topicWithStream({
|
|
posts: [{ id: 1 }]
|
|
});
|
|
const controller = this.subject({ model });
|
|
const placeholder = new Placeholder("post-placeholder");
|
|
|
|
assert.equal(
|
|
controller.send("topVisibleChanged", {
|
|
post: placeholder
|
|
}),
|
|
null,
|
|
"it should work with a post-placehodler"
|
|
);
|
|
});
|
|
|
|
QUnit.test(
|
|
"deletePost - no modal is shown if post does not have replies",
|
|
function(assert) {
|
|
pretender.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 currentUser = EmberObject.create({ moderator: true });
|
|
let model = topicWithStream({
|
|
stream: [2, 3, 4],
|
|
posts: [post, { id: 3 }, { id: 4 }]
|
|
});
|
|
const controller = this.subject({ model, currentUser });
|
|
|
|
const done = assert.async();
|
|
controller.send("deletePost", post);
|
|
|
|
next(() => {
|
|
assert.ok(destroyed, "post was destroyed");
|
|
done();
|
|
});
|
|
}
|
|
);
|